ESP32 Marauder · Volume 9

ESP32 Marauder Firmware Volume 9 — Capture Analysis Pipelines

pcap → Wireshark / tshark / scapy / hashcat / aircrack-ng / bettercap — the host-side workflow

Contents

SectionTopic
1About this volume
2pcap → Wireshark — interactive analysis
· 2.1Loading Marauder pcaps
· 2.2Display filters for management frames
· 2.3Decryption with captured PSK
· 2.4Validating a captured 4-way handshake
3pcap → tshark / scapy — scripted analysis
· 3.1tshark one-liners
· 3.2scapy for deeper analysis
4Handshake / PMKID → hashcat (mode 22000)
· 4.1pcap → 22000 conversion (hcxpcapngtool)
· 4.2The hashcat 22000 invocation
· 4.3Wordlist strategy
· 4.4Time estimates by GPU class
5Probe-request clustering — who’s-looking-for-what
· 5.1The SSID-set fingerprint
· 5.2A clustering recipe (Python + sklearn)
· 5.3BLE manufacturer-data clustering
6bettercap integration patterns
7aircrack-ng for legacy workflows
8Cred-log triage from Evil Portal
9Workflow recipes
10Resources

1. About this volume

Vol 9 closes the on-device-to-host loop. Vols 4 / 5 / 6 cover what Marauder captures and writes to the SD card; this volume covers what the operator does with those bytes once the card is pulled and attached to a workstation.

Five major host-side workflows live here:

  1. Wireshark interactive for everything (§ 2)
  2. tshark + scapy scripted for batch processing (§ 3)
  3. hashcat 22000 for offline WPA / PMKID cracking (§ 4) — the workflow with the highest payoff per engagement
  4. Clustering analysis of probe requests + BLE advertising for de-anonymization research (§ 5)
  5. Cred-log triage for Evil Portal output (§ 8)

The volume assumes the operator has a Linux host with hashcat, Wireshark, hcxtools, aircrack-ng, bettercap, and Python (with scapy and sklearn) installed. On macOS, all of these install via Homebrew; on Windows, WSL is the practical path.


2. pcap → Wireshark — interactive analysis

2.1 Loading Marauder pcaps

Open a Marauder pcap in Wireshark:

wireshark /path/to/sd/marauder/pcaps/eapol_2026-05-13T14-32-17.pcap

Wireshark identifies the link-type-105 (LINKTYPE_IEEE802_11) automatically. The capture displays as raw 802.11 frames — no Ethernet encapsulation, no radiotap header. Per-frame RSSI and channel are not embedded in the pcap (Vol 4 § 9.1) — Wireshark shows only the frame contents.

2.2 Display filters for management frames

The handful of display filters that handle most Marauder analysis:

FilterSelects
wlan.fc.type == 0All management frames
wlan.fc.subtype == 8Beacons only
wlan.fc.subtype == 4Probe requests only
wlan.fc.subtype == 5Probe responses only
wlan.fc.subtype == 12Deauth frames
wlan.fc.subtype == 0x0aDisassoc frames
wlan.bssid == aa:bb:cc:dd:ee:ffAll frames involving a specific BSSID
wlan.sa == aa:bb:cc:dd:ee:ffFrames sourced by a specific MAC
eapolEAPOL 4-way handshake frames
wlan_rsna_eapolEAPOL with the WPA decoder dissecting the key data
wlan.fc.type == 0 && wlan.bssid contains 04:5e:60Frames involving APs from a vendor OUI prefix

Combine with && and ||. Wireshark’s display-filter expression builder helps when unfamiliar with the field names.

2.3 Decryption with captured PSK

If you cracked the PSK (or know it), Wireshark can decrypt the data-frame payloads:

  1. Edit → Preferences → Protocols → IEEE 802.11.
  2. Add a decryption key with type wpa-pwd. Format: passphrase:SSID.
  3. Wireshark replays the EAPOL handshake from the pcap (must be in the same pcap) and derives the per-session keys.
  4. Data frames now decrypt — you can see DNS, HTTP, etc.

The 4-way handshake must be present in the same pcap for decryption to work. Marauder’s pcaps from the PMKID sniffer typically do contain a complete handshake (M1+M2+M3+M4 if captured cleanly); the pure PMKID-only path doesn’t preserve enough to decrypt traffic.

2.4 Validating a captured 4-way handshake

Before sending to hashcat, validate the pcap actually contains a usable handshake:

# Quick test:
tshark -r eapol.pcap -Y "eapol" -T fields -e frame.number -e wlan.bssid -e wlan_rsna_eapol.keydes.msgnr

# Output shows: frame#, BSSID, M-number (1, 2, 3, or 4)
# A usable capture: at minimum a paired M1+M2 or M2+M3 for the same BSSID.

Better: hcxpcapngtool -o /dev/null eapol.pcap and read its summary output — it tells you how many usable handshakes vs incomplete ones it found.


3. pcap → tshark / scapy — scripted analysis

3.1 tshark one-liners

tshark is Wireshark’s CLI counterpart — same filters and parsing.

Enumerate unique BSSIDs:

tshark -r beacons.pcap -Y "wlan.fc.subtype == 8" -T fields -e wlan.bssid | sort -u

Enumerate probe-request MAC + SSID pairs:

tshark -r probes.pcap -Y "wlan.fc.subtype == 4" -T fields \
  -e wlan.sa -e wlan.ssid | sort -u

Count probe requests per source MAC:

tshark -r probes.pcap -Y "wlan.fc.subtype == 4" -T fields -e wlan.sa \
  | sort | uniq -c | sort -rn | head -20

Extract all deauth frames with source + destination:

tshark -r capture.pcap -Y "wlan.fc.subtype == 12" -T fields \
  -e frame.time -e wlan.sa -e wlan.da

tshark output is one row per matching frame; pipe into awk/sort/uniq for aggregation.

3.2 scapy for deeper analysis

scapy gives full Python access to every byte of every frame:

from scapy.all import rdpcap, Dot11ProbeReq

packets = rdpcap("probes.pcap")

# Cluster probe requests by source MAC
probes_by_mac = {}
for p in packets:
    if p.haslayer(Dot11ProbeReq):
        src = p.addr2  # source MAC
        ssid_field = p.getlayer(Dot11ProbeReq).info
        ssid = ssid_field.decode("utf-8", errors="replace") if ssid_field else ""
        probes_by_mac.setdefault(src, set()).add(ssid)

# Print MACs with > 5 distinct SSID probes (highly identifying)
for mac, ssids in probes_by_mac.items():
    if len(ssids) > 5:
        print(f"{mac}: {sorted(ssids)}")

scapy can also synthesize and inject — useful for testing Marauder’s parser end-to-end on a known frame, or for building a custom analysis tool. The full scapy API is at https://scapy.readthedocs.io/.


4. Handshake / PMKID → hashcat (mode 22000)

4.1 pcap → 22000 conversion (hcxpcapngtool)

hashcat mode 22000 expects a specific format produced by hcxpcapngtool from the hcxtools package. Install:

# Ubuntu/Debian:
sudo apt install hcxtools

# Mac via Homebrew:
brew install hcxtools

# From source:
git clone https://github.com/ZerBea/hcxtools && cd hcxtools && make && sudo make install

Conversion:

hcxpcapngtool -o handshake.hc22000 eapol_2026-05-13T14-32-17.pcap

The tool reports what it found:

summary capture file
----------------------
file name......: eapol_2026-05-13T14-32-17.pcap
file type......: pcap 1.0
file hardware information......: unknown
file os information............: unknown
file application information...: unknown
network type...................: DLT_IEEE802_11
endianness.....................: little endian
read errors....................: flawless
packets inside.................: 18420
... (long output)
packets contain hashcat -m 22000 PMKID hash data: 7
packets contain hashcat -m 22000 EAPOL hash data: 12

The “PMKID hash data: 7” and “EAPOL hash data: 12” numbers are what you care about. Both write into handshake.hc22000.

The 22000 mode unifies WPA-PMKID-PBKDF2 + WPA-PBKDF2 + EAPOL — you crack everything in one hashcat invocation, regardless of capture source. The old separate modes 2500 / 16800 are deprecated.

4.2 The hashcat 22000 invocation

hashcat -m 22000 -a 0 handshake.hc22000 /path/to/wordlist.txt

# With rules:
hashcat -m 22000 -a 0 handshake.hc22000 wordlist.txt -r /path/to/best64.rule

# Mask attack (for known patterns):
hashcat -m 22000 -a 3 handshake.hc22000 '?u?l?l?l?l?d?d?d?d'

# Resume an interrupted run:
hashcat --restore

Cracked passwords end up in handshake.hc22000.pot (the pot file). View with hashcat -m 22000 handshake.hc22000 --show.

4.3 Wordlist strategy

WordlistSourceHit rate (informal) on residential APs
rockyou.txt2009 social-network leak30-50%
SecLists 10-million-password-listAggregated leaks40-60%
Custom (target name + variants + locale)Hand-curated10-20% (but high-yield per hit when it works)
Phone-number patterns?d?d?d?d?d?d?d?d?d?d maskHigh on cellular-provider default-config networks
Date patterns?d?d?d?d?d?d?d?d maskHigh on user-set passwords using birthdays

Practical default: start with rockyou + best64 rules. If that misses, try SecLists’ 10M-password-list. Then custom. Then masks.

4.4 Time estimates by GPU class

Rough hashcat 22000 cracking rates against a single hash, as of 2026:

GPUrockyou (14M words, ~5 sec/run)rockyou + best64 rules8-char mask ?a?a?a?a?a?a?a?a
RTX 4090~2 minutes~15 minutes~3 days
RTX 3080~5 minutes~30 minutes~7 days
Mac M2 (CPU only)~30 minutes~3 hoursimpractical
Intel laptop CPU~2 hours~12 hoursimpractical

GPU rental is the cost-effective path when you don’t have a heavy GPU on hand — RunPod or Vast.ai have RTX 4090 instances at ~$0.40-0.80/hour. A 5-minute rockyou run costs ~$0.05.


5. Probe-request clustering — who’s-looking-for-what

5.1 The SSID-set fingerprint

Modern OSes randomize MAC per-SSID-probe-target but each device still probes for the same set of remembered SSIDs. A device that probes for {HomeNetwork, CompanyVPN, MomsHouse, GymWiFi} is fingerprinted by that set even when the MAC rotates.

Use cases:

  • De-anonymization research — identify whether the same person visited multiple sites by comparing SSID-set overlap.
  • Targeted attack reconnaissance — find which SSIDs a target user is vulnerable to spoofing.
  • Privacy auditing — quantify how much information leaks via probe requests at a venue.

5.2 A clustering recipe (Python + sklearn)

import csv
from sklearn.cluster import DBSCAN
from sklearn.feature_extraction.text import TfidfVectorizer

# Load Marauder probe-request csv
records = []
with open("probes.csv") as f:
    reader = csv.DictReader(f)
    for row in reader:
        records.append((row["MAC"], row["SSID"]))

# Group by MAC into SSID sets
ssids_by_mac = {}
for mac, ssid in records:
    if ssid:  # ignore wildcard probes
        ssids_by_mac.setdefault(mac, set()).add(ssid)

# Filter to MACs with >= 3 distinct SSIDs (worth clustering)
worth_clustering = {m: s for m, s in ssids_by_mac.items() if len(s) >= 3}

# Build TF-IDF features over the SSID sets
docs = [" ".join(sorted(s)) for s in worth_clustering.values()]
vec = TfidfVectorizer()
X = vec.fit_transform(docs)

# DBSCAN: cluster MACs whose SSID-sets overlap substantially
clustering = DBSCAN(eps=0.4, min_samples=2, metric="cosine").fit(X)
labels = clustering.labels_

# Group MACs by cluster
clusters = {}
for mac, label in zip(worth_clustering.keys(), labels):
    if label != -1:  # -1 = noise/outlier
        clusters.setdefault(label, []).append(mac)

for cluster_id, macs in clusters.items():
    print(f"Cluster {cluster_id}: {len(macs)} MACs")
    common = set.intersection(*[worth_clustering[m] for m in macs])
    print(f"  Shared SSIDs: {sorted(common)}")

The cluster output identifies MAC groups whose SSID probes are similar enough to be plausibly the same person across MAC rotations. eps and min_samples tune sensitivity — narrow eps clusters only near-identical SSID-sets.

5.3 BLE manufacturer-data clustering

Same pattern works on BLE-scan output (Vol 6 § 4):

  • Cluster BD_ADDRs by manufacturer-data subtype fingerprint + advertising-interval timing + TX-power.
  • Apple AirPods Pro emits a distinct subtype pattern that’s stable per-device.
  • iPhones in offline-finding mode (Find My) have a 2-second cadence that’s distinctive (Vol 6 § 6.1).

Bruce / Ghost ESP’s richer manufacturer-data parses (Vol 6 § 4.2) make this easier; mainline’s conservative parses require more host-side enrichment.


6. bettercap integration patterns

bettercap is the bigger-than-Marauder Wi-Fi/BLE attack tool — runs on a Linux host with a monitor-mode-capable Wi-Fi adapter. Marauder + bettercap is the natural pairing when Marauder isn’t enough:

  • Marauder for reconnaissance (small form factor, low-profile site survey).
  • bettercap for active engagement (full power, scripting, MITM, automated workflows).

Typical hand-off pattern:

  1. Marauder runs probe-request + AP scan over hours at the target site.
  2. SD card extracts to laptop; csv informs bettercap target list.
  3. bettercap on the laptop fires the targeted attacks (deauth-and-rejoin handshake capture, evil twin, MITM) with the operator parked nearby.
  4. Bettercap captures go to host disk; analysis identical to Marauder pcaps (§ 2-4).

bettercap supports a JavaScript-based scripting language for custom workflows. The Marauder→bettercap handoff is usually a manual “use these BSSIDs as targets” step rather than scripted, but for repeated engagements it can be automated.

bettercap docs and walkthroughs: https://www.bettercap.org/. Install: apt install bettercap on Linux or brew install bettercap on Mac.


7. aircrack-ng for legacy workflows

aircrack-ng is the older toolset — predates hashcat 22000 by years. Still useful when:

  • CPU-only environment — aircrack-ng cracks on CPU (slower than hashcat-GPU but works without GPU). hashcat also supports CPU mode but aircrack-ng’s CPU implementation is faster for legacy modes.
  • WEP networks (rare in 2026) — aircrack-ng has dedicated WEP cracking modes that hashcat doesn’t replicate.
  • Legacy pcap formats — older capture tools that don’t produce hashcat-compatible output sometimes work directly with aircrack-ng.
  • Replay attacksaireplay-ng is the canonical tool for ARP-replay attacks against WEP (only relevant for WEP, but if you’re cracking WEP at all, you need it).

The hashcat path is preferred for everything else. aircrack-ng’s WPA/WPA2 cracking is materially slower than hashcat 22000 on the same hardware.


8. Cred-log triage from Evil Portal

creds.txt from Evil Portal captures (Vol 5 § 5.4 + Vol 8 § 5):

2026-05-13T14:32:17, AP=MarauderGuest, IP=10.0.0.42, MAC=A4:5E:60:11:22:33, [email protected], password=hunter2
2026-05-13T14:35:08, AP=MarauderGuest, IP=10.0.0.43, MAC=DC:A6:32:44:55:66, [email protected], password=admin123

Parsing into a structured report:

import csv, re

with open("creds.txt") as f:
    captures = []
    for line in f:
        if not line.strip():
            continue
        # Parse the comma-separated key=value pairs
        fields = {}
        for kv in line.strip().split(", "):
            if "=" in kv:
                k, v = kv.split("=", 1)
                fields[k.strip()] = v.strip()
        captures.append(fields)

# Sort by timestamp; deduplicate by email
seen = set()
unique = []
for c in captures:
    email = c.get("email", "")
    if email and email not in seen:
        seen.add(email)
        unique.append(c)

print(f"Total captures: {len(captures)}")
print(f"Unique emails: {len(unique)}")
for c in unique:
    print(f"  {c.get('email', '?')}")

Triage workflow:

  1. Parse and deduplicate.
  2. Validate captured emails against the engagement scope — were they actually authorized targets, or did bystanders enter credentials?
  3. Drop bystander credentials — purge from logs, document the purge in the engagement report.
  4. For in-scope captures, validate that the captured password actually works against the legitimate service (manual verification step; don’t include in automated tools to avoid accidentally locking accounts).
  5. Build the engagement-deliverable report — captured credentials + dates + per-capture justification.
  6. Hash + encrypt the source creds.txt before delivery.

Chain-of-custody discipline (Vol 8 § 9 + Vol 11 § 7): treat Evil Portal output as evidence-grade. Don’t sloppy-handle the file.


9. Workflow recipes

Recipe A — Full handshake-to-crack pipeline

Goal: from a Marauder PMKID/EAPOL capture to a cracked passphrase.

# 1. Mount SD card. cd to pcaps directory.
cd /mnt/sd/marauder/pcaps

# 2. Convert all pcaps to 22000 format
hcxpcapngtool -o /tmp/all_handshakes.hc22000 *.pcap

# 3. Sanity-check — how many usable hashes?
wc -l /tmp/all_handshakes.hc22000

# 4. Crack with rockyou + best64 rules
hashcat -m 22000 /tmp/all_handshakes.hc22000 \
    ~/wordlists/rockyou.txt \
    -r ~/hashcat/rules/best64.rule \
    --status --status-timer 60

# 5. View cracked
hashcat -m 22000 /tmp/all_handshakes.hc22000 --show

Recipe B — Probe-request de-anonymization study

Goal: identify which MACs at a venue are plausibly the same person across MAC rotations.

# 1. Collect via Marauder (Vol 4 § 10.1) — 10+ minute passive walk
# 2. Extract probes_<ts>.csv to host
# 3. Run the clustering recipe from § 5.2
python3 cluster_probes.py probes.csv

# 4. Output: cluster groups of MACs with shared SSID sets
# 5. (Optional) cross-reference with BLE scan from same time window for biometric correlation

Recipe C — Evil Portal engagement deliverable

Goal: produce a clean engagement-deliverable report from Evil Portal captures.

# 1. Mount SD card. Verify provenance.
sha256sum /mnt/sd/marauder/creds.txt > /tmp/creds_hash_2026-05-13.txt

# 2. Parse + deduplicate
python3 parse_creds.py /mnt/sd/marauder/creds.txt > /tmp/captures_parsed.csv

# 3. Manual review — drop bystanders, validate in-scope captures
# 4. Build engagement report (Markdown / Word per template)
# 5. Encrypt source + report; deliver via secure channel
# 6. Hash + encrypt source, securely delete original

10. Resources

Tools

Wordlists

Rules / masks

hashcat docs

GPU rental for crackers without high-end GPUs

Forward references in this series

  • Operational handling of captured material (chain-of-custody full): Vol 11 § 7
  • SD-card-side data layout (where the captures live before extraction): Vol 8
  • Per-capture-type context (what’s in the pcap to begin with): Vols 4 + 6

This is Volume 9 of a twelve-volume series. Next: Vol 10 covers the build toolchain (PlatformIO setup), building from source for a specific board, adding a custom attack, forking strategy, and the web flasher.