Files
Mark-VIII/docs/superpowers/specs/2026-08-11-pineap-page-overhaul-design.md
T
2026-08-11 20:24:24 -07:00

9.6 KiB

Design: Mark VII PineAP Page Port for the Pager WebUI

Date: 2026-08-11

Goal

Replace the current pager-webui PineAP page with a faithful replica of the WiFi Pineapple Mark VII PineAP view, fully functional on the WiFi Pineapple Pager, independent of campaign needs. Also fix the side-rail icon (location pin -> wifi).

Background / findings

  • The current PineAP page (tabs: Open, Clients, Filtering, APs, Impersonation) looks nothing like the Mark VII page and its backend is broken on the Pager:

    • server.py reads Mark VII uci keys (pineapd.pineapd.mimic, collect_handshakes, ...) that do not exist in the Pager's pineapd config, so settings never persist (live GET /api/pineap/settings returns only {"bands":"2.4"} plus raw).
    • The filter-mode endpoints call hak5cmd commands without the required argument, returning usage text instead of the mode.
  • The Pager's daemon (/pineapple/pineapple, listens on :1471) natively exposes a Mark VII-style PineAP REST API over a root-only unix socket at /tmp/api.sock (raw HTTP/1.1, no auth - socket permission is the boundary). pager-webui already uses this socket (daemon_sock_call, e.g. recon start/stop). Confirmed live routes:

    Route Method Purpose
    /api/pineap/get_config, /set_config GET/POST log flags, handshake path, ssidpool autocollect
    /api/pineap/hostapd/get_config, /set_config GET/POST master PineAP on/off, Evil WPA (wpa_ifaces), Evil Enterprise (pineape_*)
    /api/pineap/hostapd/enable_pineap POST master enable
    /api/pineap/hostapd/enable_pineape POST evil enterprise enable
    /api/pineap/hostapd/enable_pineape_auth POST enterprise auth-pass capture
    /api/pineap/mimic/enable, /disable POST karma
    /api/pineap/examine/bssid, /reset POST targeted handshake capture
    /api/pineap/ssidpool/list, /add, /clear, /disable, /disable_collect GET/POST SSID pool + advertise/collect
    /api/pineap/ssidfilter/get_config, /set_config, /allow/*, /deny/* GET/POST SSID filter mode + lists
    /api/pineap/macfilter/get_config, /set_config, /set_mode, /allow/*, /deny/* GET/POST MAC/client filter mode + lists
    /api/pineap/interfaces/get, /set_interface, /set_interface_bands GET/POST monitor interface hop/inject/bands
    `/api/pineap/log/recon pcap wigle/start
    /api/pineap/recon/new POST new recon scan
  • The Pager natively supports Evil WPA (rogue AP on wlan0wpa: SSID, PSK, encryption WPA2-PSK / WPA3-SAE / WPA3-OAE, hidden, enabled) and Evil Enterprise (EAP, auth-pass capture), confirmed via the daemon binary strings and the native (virtual) pager menu.

  • The daemon lacks routes for: connected clients (use iwinfo), kick (hak5cmd PINEAPPLE_DEAUTH_CLIENT), nearby-AP scan (iwinfo scan), and handshake file listing (/root/loot/handshakes/). pager-webui already implements these.

Architecture

Backend (payload/user/general/pager-webui/server.py)

  1. Add a generic proxy handler h_pineap_proxy that forwards the browser request (method + JSON body) verbatim to the daemon socket path /api/pineap/<subpath> using the existing daemon_sock_call, and returns the daemon's JSON response. Register it for the whole native tree (get_config, set_config, hostapd/*, mimic/*, examine/*, ssidpool/*, ssidfilter/*, macfilter/*, interfaces/*, log/*, recon/new) so every native capability is reachable 1:1 through pager-webui's authenticated /api/pineap/* namespace.
  2. Keep the custom endpoints that have no daemon route: clients (iwinfo), clients/kick (deauth), aps (iwinfo scan), handshakes (loot listing + download/delete).
  3. Add Enterprise data endpoints: list + clear of hostap_basic / hostap_challenge rows from recon.db (via the sqlite3 CLI, the same mechanism the recon page uses).
  4. Delete the broken Mark VII-uci settings handlers (SETTING_MAP, _uci_map, h_pineap_settings_get/post) and the filter handlers that returned hak5 usage text (h_filter_get/post); replace with the proxy.
  5. On daemon socket failure return 502 with a JSON error; never crash.

Frontend (www/js/app.js, www/js/views.js, www/css/app.css)

  1. Rail icon: change the PineAP rail item from pineap (map-marker) to the existing wifi icon in icons.js/app.js rail definition.

  2. Rebuild the PineAP page as 8 tabs matching the Mark VII structure (using the existing vanilla-JS SPA design language - material-style cards, toggles, tables, tab bar):

    Tab Route Content
    PineAP #/pineap Mode badge (Passive/Active/Advanced from pineap_disabled+mimic+evilwpa/enterprise), description, quick toggles (Capture SSIDs to Pool, Advertise AP Impersonation Pool, Randomize Source MAC), alert-payload info note, 4 status cards (Karma / Open Network / Evil WPA / Evil Enterprise) with Configure links
    Open AP #/pineap/open Enable PineAP (master), Karma, Logging group (handshakes/partial/pcap/wigle/recon), Capture SSIDs, Advertise Pool, Randomize MAC, AP Channel select, PineAP MAC + Target MAC (read-only)
    Evil WPA #/pineap/evilwpa SSID, passphrase, encryption select (WPA2-PSK / WPA3-SAE / WPA3-OAE), Hidden, Enabled; handshake capture card (Examine BSSID + start/stop/reset); captured handshakes table
    Enterprise #/pineap/enterprise Enabled, Auth Pass Capture; Basic Data + Challenge Data tables with Clear
    Impersonation #/pineap/impersonation SSID pool textarea editor + Add + Clear; Advertise + Randomize toggles; pool start/stop/collect
    Clients #/pineap/clients Connected clients table (MAC/interface/RSSI) + Kick, 5s auto-refresh
    Filtering #/pineap/filtering Client Filter + SSID Filter cards: Allow/Deny mode + line-based list textareas with add/delete/clear
    APs #/pineap/aps Kept as-is (iwinfo scan table, 10s refresh)
  3. Mark VII-only controls with no Pager equivalent (Autostart, Beacon Responses, Beacon Intervals, enterprise cert generation) are omitted rather than greyed out; Client Connect/Disconnect Notifications are shown as an info note (the Pager handles these via alert payloads natively).

  4. Register the new routes in the routes map and add the tab bar entries + CSS.

Data flow

Browser -> pager-webui /api/pineap/* (session-authenticated) -> daemon_sock_call -> /tmp/api.sock -> Pager daemon -> pineapd/hostapd. Mutating calls show a toast; failures surface a friendly error.

Error handling

  • Daemon socket unavailable -> HTTP 502 {error}; frontend shows a toast and never renders a dead page.
  • Unknown/unsupported subpath -> 404 through pager-webui.
  • Enterprise tables empty -> "no data" empty states (same pattern as Recon).

Testing

  • Rewrite tests/test_pineap_{settings,pool,clients,aps}.py and add test_pineap_evilwpa.py / test_pineap_enterprise.py / test_pineap_filtering.py against the proxied shapes, mocking the daemon socket call (module-level monkeypatch per the existing test conventions).
  • Run the README's per-file unittest loop on Windows.
  • On-device smoke test after deploy.ps1: walk every tab, verify toggles persist across a reboot.

Out of scope

  • Firmware changes; the daemon API internals are used as-is.
  • A PR to hak5/wifipineapplepager-payloads (packaging is drop-in ready).

Open items (resolved during implementation)

  • Exact hostapd/set_config Evil WPA field names (SSID/PSK/encryption/ hidden/enabled) - probe on the live device. Resolved: Evil WPA lives in UCI wireless.wlan0wpa. The daemon route PUT /api/settings/wifi/set_ap takes {"configs":[{interface, ssid, enctype, enabled, hidden, key, channel(int)}]} (full replace; channel must be int). PUT /api/settings/wifi/get_ap is a stub (always {"interfaces":[]}), so the webui reads UCI wireless.wlan0wpa/wlan0open directly. enctype values: psk2, psk, sae, none (not wpa2). Applying reconfigures the radio and briefly drops the management connection.
  • ssidpool list response shape (fallback: hak5cmd PINEAPPLE_SSID_POOL_LIST). Resolved: GET /api/pineap/ssidpool/list is a stub ({"success":true}); the pool is stored base64 in UCI pineapd.@ssidpool[0].ssid. hak5cmd PINEAPPLE_SSID_POOL_LIST/ADD/DELETE/CLEAR all work and are used.
  • Randomize-Source-MAC route (GetPineAPRandomizeMAC internal var) - discover the backing route/field. Resolved (partial): no readable daemon route for the pool advertise state (ssidpool/enable|disable are POST; enable returns 500 natively because no wlan0open interface exists on the Pager). The webui surfaces the daemon error.
  • sqlite3 CLI availability for the Enterprise tables. Resolved: sqlite3 CLI present; hostap_basic/hostap_challenge do not exist yet in recon.db, so the Enterprise tab renders empty tables (webui returns rows: []).
  • Pineapd UCI required-field cycle: the daemon's set_config/hostapd/set_config are full-replaces. If a write omits required fields (pineapd.@pineapd[0]. reconpath etc.), get_config then fails and later writes stay broken. The webui merges each write over the current daemon config (or defaults when get_config fails) so required fields are always preserved.
  • Verified daemon methods: reads GET; set_config/hostapd/set_config/ hostapd/enable_pineap/interfaces/set_interface/ssidpool/add/wifi/* PUT; toggles (mimic/*, examine/*, ssidpool/enable|disable|enable_collect| disable_collect) POST. ssidfilter/macfilter set_config are PUT.