13 KiB
13 KiB
Pager WebUI — Design Spec (v1)
- Date: 2026-08-10
- Status: Approved (pending written-spec review)
- Owner: Hak5 WiFi Pineapple Pager expansion project
1. Goal
Build a Mark VII-style web management UI that runs on the WiFi Pineapple Pager itself, with core-parity featureset (Dashboard, PineAP, Recon, Handshakes/Loot, Payloads, Logs, Settings, and a bottom-docked terminal), packaged as a Payload-Portal-installable payload and deployed over SSH/SCP.
The UI is reached at http://172.16.52.1:8080/ (verified free on the device).
2. Verified Research Basis (live devices)
Pager (172.16.52.1)
- MediaTek MT76x8 SoC, OpenWRT-based
Pineapple Pager 24.10.1(ramips/mt76x8,mipsel_24kc, Linux 6.6.86). - Full root SSH (
root+ device password).opkgavailable. - Persistent 4GB MMC:
/root->/mmc/root; payloads live under/root/payloads/, loot under/root/loot/. - Tools present:
python3(3.11.14),sqlite3,iwinfo,hostapd_cli,ubus,uci,curl,nc,wget. - Firmware is U-Boot signed — firmware modification is out of scope; no stock files are touched.
Hak5 daemon (/pineapple/pineapple, Go)
- HTTP API on
:1471and an unauthenticated Unix socket/tmp/api.sockserving the same router. - Auth:
POST /api/login({"username":"root","password":...}) returns{"token": ...}. Requests authorize withAuthorization: Bearer <token>or theAUTH_<serverid>cookie (serverid from/api/api_ping, e.g.001337AEE050). - Existing API used by the stock Virtual Pager:
api_ping,login,payloads/portal/{index,refresh,updates,<key>/install,<key>/remove},loot/archive,loot/zip,files/zip/root/loot/handshakes,terminal/openWs(WS),pager/input/keys.ws(WS),pager/display/screen.ws(WS). - Terminal WS
/api/terminal/openWs: plain-text WebSocket (keystrokes in, output out). Requires theAUTH_*cookie (verified 401 without, 101 with). Accepts cross-origin handshakes. Cookies are host-scoped and port-agnostic, and the:8080page and:1471host are the same site, so a cookie set for host172.16.52.1is sent on the WS handshake from the:8080page. The docked terminal therefore connects directly to the daemon WS; a server-side relay is the documented fallback.
Control channel (payload interface)
/usr/bin/hak5cmd(C++, protobuf client to the daemon). 49 symlinks provide every PineAP operation, e.g.:PINEAPPLE_SSID_POOL_{ADD,ADD_FILE,START,STOP,LIST,DELETE,CLEAR,COLLECT_START,COLLECT_STOP},PINEAPPLE_NETWORK_FILTER_{MODE,ADD,ADD_FILE,DELETE,LIST,CLEAR},PINEAPPLE_DEVICE_FILTER_{MODE,ADD,ADD_FILE,DELETE,LIST,CLEAR},PINEAPPLE_MIMIC_{ENABLE,DISABLE},PINEAPPLE_DEAUTH_CLIENT,PINEAPPLE_EXAMINE_{BSSID,CHANNEL,RESET},PINEAPPLE_HOPPING_{START,STOP},PINEAPPLE_SET_BANDS,PINEAPPLE_RECON_NEW,PINEAPPLE_LOOT_ARCHIVE.- Verified: commands run from CLI over SSH (read-only ones confirmed).
Recon data
- SQLite
/root/recon/recon.db: tablesscan,wifi_device,ssid,handshake,hostap_chalresp,hostap_basic,hostap_client,hostap_handshake.
PineAP config
- UCI file
/etc/config/pineapd(sections:pineapd,hostapd,ssidpool,ssid_filter,mac_filter,interface wlan0mon/wlan1mon/wlan2mon). - Reload trigger exists (
/etc/init.d/pineapd reload,pineap_reload).
Precedents (payloads already on device)
nautilus(user/remote_access/nautilus): web UI payload with init script (nautilus.init, START=99, procd), python3 proxy, foreground/background run modes,PAYLOAD_GET_CONFIGpersistence.virtual_pager_enhancer: uhttpd+CGI on port 4040 (init script pattern).
Mark VII blueprint
- 113
/api/*endpoints extracted from its Angular bundle: dashboard,pineap/*(clients, kick, deauth, filters, handshakes, settings, ssids, summary),recon/*(scans, start/stop/status, tags), logging, settings/ networking, modules, device, terminal (xterm.js bottom-docked panel).
3. Repo Layout
Dev workspace: C:\Users\root\Documents\Pineapple\pager-webui\
pager-webui\
├── payload\user\general\pager-webui\
│ ├── _hak5_manifest.json
│ ├── payload.sh # run-mode installer (nautilus pattern)
│ ├── pagerwebui.init # OpenWRT init template (START=99, procd, respawn)
│ ├── server.py # Python3 stdlib HTTP+WS+JSON API backend (:8080)
│ └── www\ # vanilla JS SPA + xterm.js bundle
├── scripts\
│ ├── deploy.ps1 # build portal zip + SCP/install to Pager
│ └── dev.ps1 # local dev server w/ proxy to Pager
├── docs\specs\ # design specs
└── README.md
4. Packaging & Deployment
- Payload format mirrors
nautilusexactly: self-contained directoryuser/general/pager-webui/withpayload.sh,pagerwebui.init,server.py,www/. - Sideload (v1 delivery):
scripts/deploy.ps1builds a portal-format zip (payload-<b64>.zip) and installs it to/root/payloads/user/general/pager-webui/on the Pager over SCP. The payload then appears in the on-device Payloads menu and the Virtual Pager's portal view — installable like any Payload Portal payload. - Future portal distribution: packaging is drop-in ready for a PR to
hak5/wifipineapplepager-payloads; the build script generates the manifesttime/last_hash/zipfields.
payload.sh run flow (nautilus pattern)
- If
/etc/init.d/pagerwebuiexists andrunning: show URLhttp://172.16.52.1:8080/, offerCONFIRMATION_DIALOG "Stop service?"; on confirm ->stop,disable, remove init script; exit. - Read
PAYLOAD_GET_CONFIG pager_webui auto_mode/run_mode. Ifauto_mode=true, skip prompt (background ifrun_mode=background, else foreground). - Else
CONFIRMATION_DIALOG "Run as background service?":- Yes -> background:
cp pagerwebui.init /etc/init.d/pagerwebui,chmod +x,enable,start(procd runspython3 server.py, respawns on crash). Boot-persistent via rc.d symlinks. - No -> foreground: spawn
python3 server.pywith/tmp/pagerwebui.pid, show URL, loopWAIT_FOR_INPUTuntil B/Escape,trap cleanupkills the server and removes the pid file.
- Yes -> background:
- Guard: python3 is present on current firmware; init script fails gracefully if missing.
- Uninstall: the stop path above, plus deleting the payload directory (portal semantics).
- Boot persistence: rc.d symlinks (overlay). Survives reboots and firmware upgrades; after a firmware upgrade (which wipes overlay) re-run the payload to re-enable — same caveat as nautilus. Foreground mode needs no persistence.
5. Backend (server.py)
Python 3.11 stdlib only (http.server, sqlite3, subprocess, json,
socketserver, hand-rolled minimal RFC6455 for the live-update WS). Binds
0.0.0.0:8080. Single origin serves the SPA and the API.
| Endpoint | Method | Implementation |
|---|---|---|
/api/login |
POST | validate via daemon :1471/api/login; on success return token and Set-Cookie: AUTH_<serverid> (host 172.16.52.1, no Domain, Path=/, HttpOnly, SameSite=Lax) so docked terminal + loot downloads on :1471 are authorized cross-port |
/api/api_ping |
GET | serverid + version (mirror daemon) |
/api/status |
GET | battery/power via sysfs, WiFi/interfaces via iwinfo + ip, firmware/daemon versions, disk via df, uptime |
/api/pineap/settings |
GET/POST | read/write UCI /etc/config/pineapd (mimic, collect probes, advertise, collect handshakes, random MAC, wigle, bands) + pineap_reload |
/api/pineap/ssids |
GET/POST | SSID pool read/write via hak5cmd |
/api/pineap/ssidpool/{start,stop,collect_start,collect_stop} |
POST | via hak5cmd |
/api/pineap/filters/client |
GET/POST | device filter mode + list via hak5cmd |
/api/pineap/filters/ssid |
GET/POST | network filter mode + list via hak5cmd |
/api/pineap/clients |
GET | associated clients via iwinfo assoclist / hostapd_cli on wlan0open/wlan0wpa/wlan0mgmt |
/api/pineap/clients/kick |
POST | deauth via hak5cmd + auto-add to deny filter (Mark VII behavior) |
/api/pineap/deauth/client |
POST | PINEAPPLE_DEAUTH_CLIENT |
/api/recon/{start,stop} |
POST | hak5cmd RECON_NEW / stop |
/api/recon/scans |
GET | scans list from recon.db |
/api/recon/scans/<id> |
GET | APs/clients/handshakes for a scan from recon.db |
/api/pineap/handshakes |
GET/DELETE | list/delete /root/loot/handshakes |
/api/loot/zip |
GET | zip download (wraps daemon :1471 with cookie) |
/api/loot/archive |
POST | wraps daemon :1471 |
/api/payloads/index, /api/payloads/install, /api/payloads/remove, /api/payloads/refresh |
GET/POST | wrap daemon portal endpoints |
/api/logging/system |
GET | logread (filtered, tail) |
/api/logging/pineap |
GET | pineapd/daemon log sources |
/api/device |
GET | hostname, MACs, model |
/api/settings/hostname |
GET/POST | uci network hostname |
/api/settings/password |
POST | change the device root password via BusyBox passwd stdin |
/api/settings/ntp |
GET/POST | UCI system timeserver (enabled + servers) + restart sysntpd |
/api/ws |
WS | live push: status/clients/recon deltas every ~2s |
/api/terminal/openWs |
WS | same path on our origin; client connects direct to daemon ws://172.16.52.1:1471/api/terminal/openWs (cookie-authed). Fallback: server relays |
Auth model
- All endpoints (except
/api/login) require a valid session cookie set by/api/login. - Token is never logged or stored server-side beyond the session check; the
backend validates each request by checking the session cookie against the
daemon's token (a lightweight session store in
/tmp/pagerwebui.session). - Commands are executed with argument lists (no shell string interpolation) to prevent injection.
6. Frontend (vanilla JS SPA)
- Static files in
www/:index.html,css/app.css,js/app.js(or split modules),js/xterm.js+js/xterm-fit.js(bundled copies),assets/. - Mark VII-style chrome: dark theme, top bar (logo, live status, Terminal
button), nav rail, hash-based routing (
#/dashboard,#/pineap,#/recon,#/handshakes,#/payloads,#/logs,#/settings). - Dashboard: status cards + live counters (battery, clients, APs,
handshakes) fed by
/api/ws. - PineAP: settings toggles (mimic/advertise/collect/handshakes/random MAC/ wigle/bands), SSID pool CRUD + collect/start/stop, client & SSID filters (mode + list CRUD), client list with kick.
- Recon: scan list, AP/client tables from
recon.db, start/stop buttons. - Handshakes/Loot: list, download zip, delete, archive.
- Payloads: portal list + install/remove/refresh.
- Logs: system + pineap logs with tail/poll.
- Settings: hostname, NTP, password, webUI prefs (poll interval, accent), service status (background/foreground).
- Terminal: bottom-docked xterm panel toggled by the top-bar button (Mark
VII parity). Connects
ws://172.16.52.1:1471/api/terminal/openWs. Default 80x24; resize behavior validated during implementation (daemon may ignore resize; fallback fixed size with xtermfitdisabled).
7. Security & Resilience
- Auth: device password validated through the daemon; HttpOnly session cookie; no plaintext secret storage; state-changing endpoints all behind login.
- No stock files modified; no
opkgchanges; reversible viapayload.shstop/uninstall; factory reset / firmware recovery remain available. server.pyfailure modes: procd respawn in background mode; foreground mode cleans up on exit;recon.dbopened read-only; subprocesses use arg lists.- Binds
0.0.0.0:8080(same exposure class as stock:1471/:7681).
8. Testing & Verification
- Windows dev loop:
scripts/dev.ps1runs the SPA + API locally and proxies to the Pager. - PowerShell-driven API tests against
:8080: login, status, each read/write endpoint before frontend wiring. - On-device smoke tests per page: status, pool CRUD, filter toggles, recon start/scan read, handshake listing, portal install/remove, terminal I/O, background vs foreground modes, reboot persistence.
- Recovery drill: uninstall, re-install, foreground-stop, firmware-upgrade caveat documented in README.
9. Out of Scope (v1)
:1471takeover/redirect.- Mark VII features without a Pager equivalent: Campaigns, Modules, Cloud C², Enterprise/EAP pages.
- Physical-display screen mirror.
- Publishing a PR to the official payloads repo (packaging ready; submission later).
10. Open Risks (mitigated during implementation)
- Client-list source validation:
iwinfo assoclist/hostapd_cliagainst the Pager's hostapd interfaces (wlan0open,wlan0wpa,wlan0mgmt). - Minimal RFC6455 WebSocket server correctness.
- Daemon terminal resize support (fallback to fixed 80x24).