# Mark VIII A Mark VII-style web management UI that runs **on the WiFi Pineapple Pager** at `http://172.16.52.1:8080/`. Packaged as a native Pager payload. Features: Dashboard (live), PineAP (settings, SSID pool, filters, clients/kick), Recon (scans from `recon.db`), Handshakes/Loot, Payloads (embedded stock Pager Portal), Logs, Settings (hostname/NTP/password/prefs), and a bottom-docked xterm terminal. Recon AP focus offers bulk deauth; an Evil Portal tab imports Hak5 EvilPortalNano-format portals (`kleo/evilportals` compatible), serves them to victims via DNS hijack on port 80, and captures form credentials. - Rogue AP on the second radio (5GHz / 6GHz Wi-Fi 6E): Open AP and Evil WPA (WPA2-PSK/WPA3-SAE/WPA3-OWE) on `radio1`, band-aware channel pickers, 6GHz requires WPA3. While a radio1 AP is enabled the stock monitor-hopping (`wlan1mon`) is paused and resumed on disable; 2.4GHz PineAP is untouched. ## Requirements - WiFi Pineapple Pager, firmware `Pineapple Pager 24.10.1` - `python3` on the device (factory 24.10.1 may not ship it; `scripts/deploy.sh` installs OpenWrt `python3-light` from offline ipks) - Python 3.11 on the development machine ## Install (sideload) macOS/Linux: ```bash # Recommended: key authentication ./scripts/deploy.sh --ssh-key "$HOME/.ssh/pager_key" # Password authentication requires sshpass brew install hudochenkov/sshpass/sshpass ./scripts/deploy.sh --password '' ``` Windows: ```powershell # deploy.ps1 needs either an SSH key or sshpass for password auth: & .\scripts\deploy.ps1 -SshKey "$HOME\.ssh\pager_key" # or set up a key and add it: ssh-copy-id root@172.16.52.1 ``` The deployment scripts build `build/pager-webui/payload-.zip`, upload it, extract it to `/root/payloads/user/remote_access/pager-webui/`, and refresh the portal index. Then on the Pager menu, run **Mark VIII**: - **Yes** to "Run as background service?" -> procd service (respawns on crash, boot-persistent via rc.d symlinks). - **No** -> foreground mode; press **B** to stop. - Re-run the payload while running to **Stop** the service. - `PAYLOAD_GET_CONFIG pager_webui auto_mode/run_mode` skip the prompt. Every payload run (and every service startup) first runs an **environment check** that prints on the payload screen / `/tmp/pagerwebui.log`: daemon reachable, pineapd alive, monitor interfaces up, and recon DB readable. On a healthy pager it **does not rewrite** live PineAP or wireless UCI (SSID pool, hopping, dummy_radio0 STA, Open AP). Crash-prone settings are reported as warnings. Startup aborts only if a core dependency fails. The health monitor will restart a dead pineapd and re-raise dropped monitors; it will not clear the SSID list or disable pool broadcast. Browse `http://172.16.52.1:8080/` and log in with the device password. ## Uninstall / recovery Re-run the payload and confirm "Stop service?" (stops, disables, removes the init script), then delete the payload directory via the portal or: `rm -rf /root/payloads/user/remote_access/pager-webui`. No stock files are modified. After a **firmware upgrade** (which wipes the overlay), reinstall and run the payload to re-enable it—the same caveat as Nautilus. ## Local dev loop ```powershell .\scripts\deploy.ps1 -SshKey "$HOME\.ssh\pager_key" # deploy backend once .\scripts\dev.ps1 -Tunnel # local SPA + API proxy # open http://127.0.0.1:8000 ``` `dev.ps1` serves `www/` locally, proxies `/api/*` to the Pager, and points the terminal at the Pager's daemon WS (`-Tunnel` opens the `:1471` SSH tunnel). The live WebSocket falls back to 5s polling through the dev proxy. ## API tests Python unit tests (stdlib `unittest`, runnable on Windows with mocks). Run each module in its own process—the tests monkeypatch module-level helpers and do not restore them, so a single `discover` process leaks state between files: ```powershell $py = "$env:LOCALAPPDATA\Programs\Python\Python311\python.exe" Get-ChildItem tests\test_*.py | ForEach-Object { $mod = "tests." + [IO.Path]::GetFileNameWithoutExtension($_.Name) & $py -m unittest $mod -v } ``` On-device smoke tests cover every page, background vs foreground, terminal I/O, and reboot persistence. ## Architecture - `server.py` — Mark VIII's pure-socket HTTP + JSON API + minimal RFC6455 WS on `0.0.0.0:8080`, written to run on the device's `python3-light` (no `urllib`/`http.server`/`sqlite3` stdlib modules there); talks to the Hak5 daemon (`127.0.0.1:1471`) over a raw-socket HTTP client, `hak5cmd`, `uci`, `iwinfo`, and `recon.db` read-only (via the `sqlite3` CLI). - `www/` — vanilla JS SPA (no build step) + bundled xterm.js. - `payload.sh` + `pagerwebui.init` — Nautilus-style installer / procd service. ## Stability notes (Pager 24.10.1) ### Reliability Core (v1.4.0) The factory `/etc/config/pineapd` ships every crash source below enabled, so guards that only live inside Mark VIII revert on every reset/upgrade. v1.4.0 makes the fixes structural (payload-only — no firmware changes): - **Boot guard** (`mk8-guard`, START=49): enforces the known-good UCI set before the S50 pineapple stack starts; idempotent, diff-only commits, logged to `/tmp/mk8-guard.log` and the event journal. - **Profiles + rollback watchdogs**: every risky operation (AP deploy, client-mode change, profile restore) first snapshots UCI under `/mmc/mk8/profiles/`, then spawns a detached watchdog probing *local* liveness (`127.0.0.1:8080` + monitor presence). Sustained failure → automatic snapshot restore + `wifi reload`; recovery → promotes `lastknown-good`. Watchdogs self-exit after 120 quiet ticks. - **RF plan**: phy0 is always 2.4GHz ops; phy1 role-switches uplink/attack/idle (`POST /api/rfplan/role`). The uplink STA lives on radio1, so client-mode no longer blinds 2.4GHz recon. Hop governance belongs to the role manager, not the boot guard. - **Supervisor**: passive sampler (pidof/iw//proc only — never pineapd's socket), bounded JSONL journal at `/mmc/mk8/events.log`, boot-marker unexpected-reboot detection, memory watermark alerts. Dashboard shows events feed + reliability counters + guard sync chip. - **Atomic deploys**: `scripts/deploy.sh` stamps `VERSION` into build copies only, sha256-verifies the upload, swaps `/mmc/mk8/releases/` with a `current` symlink, polls local health, and auto-rolls back to the previous release on failure. - **Smoke suite**: `scripts/smoke.sh` on-device (read-only checks always; `--write` adds reconcile/watchdog drills; `SMOKE_UPLINK_SSID=...` enables the RF-role drill). All checks verified against live hardware including reboot persistence. Persistent state lives in `/mmc/mk8/` and survives reboots *and* firmware upgrades (overlay wipes). A pre-reliability config backup is captured at first deploy. ### pineapd crash sources found and fixed on this firmware pineapd crash sources found and fixed on this firmware (verified on-device, zero crashes over sustained watches): 1. **SSID-pool broadcast** — segfaults pineapd (~15s cadence). Kept disabled. 2. **wlan2mon** — a 6GHz monitor this hardware never creates; hopping the missing iface segfaults pineapd. Disabled. 3. **Large refilled pool** — the pool list itself crashes pineapd even with broadcast disabled. The health monitor clears it (collect refills). 4. **wlan1mon fast-hopping 6GHz** — stalls pineapd's command socket; the stock daemon's watchdog then SIGTERMs pineapd every ~30s. Bands pinned to 5GHz (2.4GHz only on wlan0mon). 5. **Socket collisions** — actively pinging pineapd from a health monitor collides with the stock daemon's own socket writes. The monitor now checks `pidof` only. `GET /api/health` reports pineapd/monitor state; the top bar shows a PINEAP OK / POOL OFF / PINEAPD DOWN chip. ### Live validation findings (v1.4.x, Pager 24.10.1) Fixed after an on-hardware attack validation pass: 1. **Evil Portal import** no longer uses `zipfile` (pulls `pathlib → urllib`, absent from python3-light). A minimal `struct`+`zlib` ZIP reader handles stored/deflate entries. 2. **Portal DNS hijack** now uses `uci add_list/del_list` (`dhcp.@dnsmasq[0].address` is a list option; a plain `uci set` was silently dropped from the generated dnsmasq config). 3. **5 GHz attack APs are bridged** into `br-lan` (`network.brlan.ports`) so victims get DHCP/portal reach; the standalone enterprise AP (`wlan1ent`) is runtime-bridged after its hostapd instance verifies ENABLED (the retry loop may recreate it). 4. **5 GHz WPA deploys auto-start a pinned `wlan1mon` capture** and the matching stop tears it down: loot flows via pcap → `.hc22000` export instead of the dead daemon path below. Residual firmware limitation (not fixable in-process): pineapd refuses handshake/PineAPE forwarding from hostapd instances it did not provision itself (`PINEAP: could not send ... Broken pipe`), and the stock daemon's `set_ap` rejects radio1 interface names ("Invalid access point interface"). Consequences: `hostap_handshake` rows never populate for radio1 evil twins (use the auto-capture + `.hc22000` export, which is crack-verified end-to-end), and enterprise credentials never reach `hostap_basic`/`hostap_chalresp` even though the AP terminates PEAP/MSCHAPv2 successfully. Fixing these requires a Hak5 pineapd change. ## Security notes - Auth via device password validated against the daemon; HttpOnly session cookie `AUTH_`; all state-changing endpoints gated. - Commands run with argument lists (no shell interpolation). - Binds `0.0.0.0:8080` — same exposure class as the stock `:1471`/`:7681`. - Same-origin validation protects browser writes and WebSocket upgrades; the daemon token is stored in a root-only temporary session file. ## Out of scope (v1) `:1471` takeover, Mark VII-only features (Campaigns/Modules/Cloud C2/EAP), physical-display mirroring, and a PR to `hak5/wifipineapplepager-payloads` (packaging is drop-in ready for that PR). ## License Mark VIII source code is available under the MIT License. See `LICENSE` and `THIRD_PARTY_NOTICES.md` for bundled-component and trademark notices.