Skip to content

Device and Platform

Qwavey edited this page Aug 23, 2026 · 1 revision

Device & Platform

Hardware controls, device state, and the payload launcher itself.

Every command block below is that script's own --help output on the real device, pulled straight from its header comment - not retyped.

  • battery.sh - Show battery level and charge state.
  • led.sh - Control the Pager's LEDs.
  • ringtone.sh - Play a ringtone and/or vibrate.
  • screen.sh - Turn the physical display on/off.
  • config.sh - Device configuration helper.
  • reset.sh - Put the device back to a known-good state (bridges, radios, orphaned background jobs).
  • crash_logger.sh - Continuously mirror dmesg/logread to persistent storage, so a crash-reboot doesn't erase its own evidence.
  • alert.sh - Push a message to the Pager's screen from SSH.
  • webui.sh - Start/stop the Pager Control Panel.
  • PayloadRunner.sh - Launcher for the Pager's real payload system.

battery.sh

Show battery level and charge state.

battery.sh - Show the Pager's battery level. Wraps the official
BATTERY_PERCENT and BATTERY_CHARGING DuckyScript commands.

Usage:
  battery.sh              print "72% (charging)" style summary
  battery.sh --percent     print just the number, e.g. "72"
  battery.sh --watch        refresh every 5s until Ctrl+C

Options:
  --percent       Print only the raw percentage number
  --watch          Keep printing, refreshing every --interval seconds
  --interval SECS   Refresh interval for --watch (default: 5)
  --alert-below PCT  With --watch: fire an on-screen ALERT the moment the
                       battery first drops below PCT (not charging), once
                       per dip - not every cycle. Useful for an unattended
                       field engagement where nobody's watching the SSH
                       output but the physical screen is still visible.
  -h, --help         This help

led.sh

Control the Pager's LEDs.

led.sh - Control the Pager's LEDs. Wraps LED and DPADLED_CONFIG.
Syntax confirmed live via LED --help / DPADLED_CONFIG --help.

LED [color] [pattern]   e.g. LED R SLOW, LED W SOLID
  colors:   R G B Y C M W (or off via the OFF state)
  patterns: SOLID SLOW FAST VERYFAST SINGLE DOUBLE TRIPLE QUAD QUIN
            ISINGLE IDOUBLE ITRIPLE IQUAD IQUIN SUCCESS
LED [state]             e.g. LED ATTACK, LED FAIL, LED OFF
  states: SETUP FAIL FAIL1 FAIL2 FAIL3 ATTACK STAGE1..5 SPECIAL SPECIAL1..5
          CLEANUP FINISH OFF
DPADLED_CONFIG [color]  one of: red green blue cyan yellow magenta white off

Usage:
  led.sh --set COLOR [PATTERN]     e.g. led.sh --set R SLOW
  led.sh --state STATE               e.g. led.sh --state ATTACK
  led.sh --off
  led.sh --dpad COLOR                  red/green/blue/cyan/yellow/magenta/white/off
  led.sh                                 interactive mode

ringtone.sh

Play a ringtone and/or vibrate.

ringtone.sh - Play a ringtone (with optional vibration) or vibrate alone.
Wraps the RINGTONE and VIBRATE DuckyScript commands.

Confirmed syntax from the Hak5 docs:
  RINGTONE {--vibrate} [rtttl-or-filename]
  VIBRATE [rtttl-or-filename]
Ringtone files are looked up in /root/ringtones/.

Usage:
  ringtone.sh --play NAME_OR_FILE [--vibrate]
  ringtone.sh --vibrate-only NAME_OR_FILE
  ringtone.sh --list          list ringtone files available under /root/ringtones/
  ringtone.sh                interactive mode

screen.sh

Turn the physical display on/off.

screen.sh - Turn the Pager's physical display on/off. Wraps
ENABLE_DISPLAY / DISABLE_DISPLAY (documented as simple no-argument
toggles in the Hak5 DuckyScript command table).

Usage:
  screen.sh --on
  screen.sh --off
  screen.sh                interactive mode

config.sh

Device configuration helper.


config.sh - Device configuration helper for the WiFi Pineapple Pager.

Wraps OFFICIAL Hak5 commands only:
  WIFI_MGMT_AP / _DISABLE / _HIDE / _CLEAR     Management AP identity
  PINEAPPLE_MIMIC_ENABLE / _DISABLE            Open AP mimic mode
  PINEAPPLE_SSID_POOL_START [random]           Randomized BSSID for the SSID pool
  PINEAPPLE_DEVICE_FILTER_MODE / NETWORK_FILTER_MODE   allow/deny scoping
  PAYLOAD_SET/GET/DEL_CONFIG                   Persistent settings store
    (shared with EvilTwin.sh / LanScan.sh / PayloadRunner.sh)

NOTE ON HOSTNAME / SYSTEM IDENTITY:
  There is no documented Hak5 command to change the underlying OpenWRT
  hostname or the client-mode (wlan0cli) MAC address. Those aren't
  Pineapple-specific features - they're generic OpenWRT/Linux config
  (uci system, or the wireless macaddr option). This script keeps that
  OFF by default and only touches it with --system-hostname, clearly
  labelled as "generic OpenWRT, not an official Pineapple command."

Usage:
  config.sh                          interactive menu
  config.sh --show                   show current config
  config.sh [options]

Options:
  --mgmt-ssid SSID          Set the Management AP SSID
  --mgmt-pw PASS             Set the Management AP password (WPA2/WPA3-SAE-mixed)
  --mgmt-hide                 Hide the Management AP
  --mgmt-disable               Disable the Management AP
  --mimic on|off                 Toggle PineAP Open-AP mimic mode
  --ssid-pool-random on|off      Randomize BSSID per SSID when advertising the pool
  --device-filter allow|deny     Set PineAP client (MAC) filter mode
  --network-filter allow|deny    Set PineAP network (SSID) filter mode
  --set KEY VALUE                 Store an arbitrary named setting (shared store)
  --get KEY                        Print a stored setting
  --del KEY                        Delete a stored setting
  --system-hostname NAME            Generic OpenWRT hostname change (NOT a Hak5 command, off by default)
  --show                             Print current PineAP / config state
  -y, --yes                          Don't prompt for confirmation
  -h, --help                          This help

reset.sh

Put the device back to a known-good state (bridges, radios, orphaned background jobs).

reset.sh - Put the device back to a known-good, standard state: no
leftover bridges, no locked WiFi channel, no Bluetooth radio stuck in
test/adv-spam mode, and no orphaned background captures/attacks from
this toolkit. Also (optionally) reloads the network subsystem from its
own saved config - the standard, safe OpenWRT way to undo any RUNTIME-
ONLY interface changes (bridges, interface masters) without touching
any actual configuration file.

WHY THIS EXISTS: built after SSH connectivity to the device was lost
mid-session while testing sniff.sh's --bridge tap mode, which enslaves
eth0 - the SAME interface USB-C/SSH management normally rides on - into
a separate ad-hoc bridge (br-sniff). If that bridge (or any other
runtime interface change from this toolkit) is left up when something
goes wrong, this is the one command that puts everything back without
needing a full device reboot or factory reset.

Deliberately does NOT touch: system-wide library/linker config (the
ld-musl incident that bricked this device once already, documented in
README.md, is exactly the kind of change this script stays away from),
UCI config files, firmware settings, or anything persistent - only
runtime state (bridges, radio modes, background processes) and a
config RELOAD (which re-applies what's already saved on disk, it
doesn't change what's saved).

BUG FOUND AND FIXED (live-caught): 'reset' is a real busybox/system
command (terminal reset - ESC codes, termios) already on $PATH ahead of
/root/scripts, so an alias literally named 'reset' would always be
shadowed - confirmed live: `reset --help` silently ran the terminal
reset instead of this script, three times in a row, before this was
caught. The CLI alias for this script is 'devreset', not 'reset' (see
setup.py's COLLISION_NAMES, same fix already applied to 'wifi'/
'autossh' for the same reason) - always run it as `devreset ...` or the
full path, never bare `reset`.

Usage:
  reset.sh                       interactive: confirm once, do everything below (recommended, no reboot)
  reset.sh --all -y                   do everything below, no prompts
  reset.sh --network [-y]                tear down br-sniff, reload network config
  reset.sh --wifi [-y]                      reset WiFi channel lock / recon hopping
  reset.sh --bluetooth [-y]                   reset BT radio (test mode / adv-spam)
  reset.sh --processes [-y]                     stop any running toolkit captures/attacks
  reset.sh --fast-restart [-y]                    restart just the pineapple app (fast)
  reset.sh --reboot [-y]                             full system reboot (slow)
  reset.sh --dry-run                                    preview what --all would do, change nothing

Options:
  --network        Tear down the br-sniff bridge if present, then reload
                       the network subsystem from its saved config (via
                       `ubus call network reload`, falling back to
                       `/etc/init.d/network reload` if ubus isn't
                       available) - fixes eth0/br-lan/SSH connectivity
                       if a bridge or other runtime interface change
                       was left in a bad state.
  --wifi              Reset the WiFi channel lock (PINEAPPLE_EXAMINE_RESET)
                         so recon resumes its normal channel-hopping instead
                         of staying parked wherever a stopped/crashed
                         deauth attack last locked it.
  --bluetooth            Send LE Test End (in case the radio is stuck
                            transmitting from --disrupt) and clear any
                            registered adv-spam advertising instances.
  --processes              Stop any currently-running deauth/bluetooth/
                              sniff/tracer/EvilTwin/deadnet/wigle capture
                              or attack this toolkit's scripts track, plus
                              a defense-in-depth kill-by-name for the
                              underlying tcpdump/l2ping processes in case
                              something wasn't tracked (same reasoning as
                              deauth.sh's own stray-process fix).
  --all                        All of the above (same as running with no
                                  flags at all, just without the interactive
                                  confirmation prompt listing).
  --fast-restart                 Restart just the pineapple app process -
                                    the SAME mechanism setup.py itself uses
                                    after deploying changed payloads (kill
                                    the real /pineapple/pineapple PID,
                                    procd respawns it cleanly). Brief UI
                                    interruption, no full reboot, no drop
                                    of your SSH session.
  --reboot                          Full system reboot - slower, drops SSH
                                       and the physical UI until the device
                                       fully comes back up. Use when more
                                       than just the pineapple app needs a
                                       clean slate (e.g. after something
                                       deeper than this script's other
                                       options can reach).
  --dry-run                          Print exactly what the selected flags
                                        would do and exit - no confirmation
                                        prompt, no commands actually run,
                                        nothing on the device touched.
                                        Combine with --network/--wifi/etc.
                                        to preview a specific subset, or use
                                        alone to preview the full --all run.
  -y, --yes                        Skip the confirmation prompt.
  -h, --help                          This help.

crash_logger.sh

Continuously mirror dmesg/logread to persistent storage, so a crash-reboot doesn't erase its own evidence.

crash_logger.sh - continuously appends dmesg/logread output to PERSISTENT
storage (/mmc, a real ext4 partition), so a crash that forces a reboot
doesn't destroy its own evidence the way it has three times running now
during dual-radio deauth testing.

WHY THIS EXISTS: dmesg and logread both live entirely in RAM on this
device (confirmed live: `mount` shows / is an overlayfs over a 31.6MB
jffs2 partition, /tmp is tmpfs, and the kernel ring buffer itself is
obviously RAM-only) - every one of the three real crashes hit while
testing raw_deauth.py's dual-radio feature left ZERO forensic trace,
because dmesg/logread both reset on boot and nothing was capturing them
to disk in real time. /mmc is a genuinely separate, persistent ext4
partition (3.3GB free, confirmed live) - the one piece of storage on
this device that actually survives a crash/reboot.

This polls dmesg with a bounded diff (wc -l + tail -n +N, the same
pattern used throughout this codebase instead of `dmesg -w`/`tail -f` -
not because -w is known broken here, just consistency with the
established orphan-process-safe pattern) every 1s, appends new lines to
/mmc/crash_dmesg.log, and calls `sync` after every write so whatever
made it to the file is actually on flash, not just buffered - cheap
insurance against a hard crash that doesn't get a clean unmount.

Usage: crash_logger.sh --background   (start, logs to /mmc/crash_dmesg.log)
       crash_logger.sh --stop
       crash_logger.sh --status
       crash_logger.sh --tail [N]         (print the last N lines - default
                                            50 - of the persistent log and
                                            exit; doesn't start/need the
                                            background logger)

After a crash, read /mmc/crash_dmesg.log (survives the reboot) instead
of dmesg (which won't - it resets on every boot). --tail is the quick way
to do that without having to remember the path or reach for `tail`/`cat`
separately over SSH.

alert.sh

Push a message to the Pager's screen from SSH.

alert.sh - Send a message to the Pager's physical screen from SSH.
Wraps ALERT / PROMPT / CONFIRMATION_DIALOG / ERROR_DIALOG (confirmed
syntax from the Hak5 docs - each just takes a message string). These
take over the screen and (for PROMPT/CONFIRMATION_DIALOG) block until
the user responds on the device.

NOTE: CONFIRMATION_DIALOG returns its yes/no answer via EXIT CODE (0 =
yes, non-zero = no), not via printed output - the docs' own example is
`CONFIRMATION_DIALOG "..." || { ... }`, checking $?, not $(...). This
script checks the exit code accordingly.

Usage:
  alert.sh --alert "message"                full-screen alert + ringtone
  alert.sh --prompt "message"                 modal, waits for dismissal
  alert.sh --confirm "question"                 yes/no on-screen, prints the answer
  alert.sh --error "message"                      error dialog
  alert.sh                                          interactive mode

webui.sh

Start/stop the Pager Control Panel.

webui.sh - Start/stop the Pager Control Panel (a local web dashboard for
the scripts in this toolkit). Binds ONLY to br-lan (Management WiFi /
USB-C) - never the internet-facing uplink - same access model the Hak5
Virtual Pager itself uses.

Usage:
  webui.sh --start [--port PORT]     start the server (default port 8081)
  webui.sh --stop                       stop it
  webui.sh --status                       is it running?
  webui.sh --set-token [TOKEN]              set/rotate the access token
                                               (required before first use -
                                                blank = generate a random one)
  webui.sh                                    interactive mode

PayloadRunner.sh

Launcher for the Pager's real payload system.


PayloadRunner.sh - Dynamic launcher for the WiFi Pineapple Pager's real
                   payload system (bash + Hak5 DuckyScript commands).

IMPORTANT: The Pager has NO keyboard-injection / HID capability - Hak5's
own docs are explicit: "The WiFi Pineapple Pager is not a keyboard
injection device... inject commands from the USB Rubber Ducky will not
work." Its DuckyScript is a Pager-specific command set for the screen,
WiFi, and PineAP - not for typing into a target computer. This script
runs THAT system (the one that actually exists on this hardware):
payload.sh scripts under /root/payloads/{user,alerts,recon}/.

Payloads run standalone fine over SSH (confirmed by testing) - any
on-screen commands they use (ALERT, PROMPT, LOG, etc.) will drive the
physical Pager screen exactly as if launched from the dashboard.

Usage:
  PayloadRunner.sh                          interactive picker
  PayloadRunner.sh --list [--category CAT]
  PayloadRunner.sh --run CATEGORY/NAME [options]

Options:
  --list                       List available payloads (with descriptions)
  --category user|alerts|recon   Restrict listing/picking to one category
  --run CATEGORY/NAME            Run a specific payload non-interactively
  --timeout SECONDS               Kill the payload if it runs longer than this
                                   (useful for headless runs - some payloads use
                                   WAIT_FOR_BUTTON_PRESS and need a person present)
  --background                     Launch and detach, logging to /root/loot/payload-runs/
  --status                          Is a backgrounded payload still running, and which one
  --stop                             Stop the currently backgrounded payload
  -y, --yes                        Don't prompt for confirmation
  -h, --help                        This help

Clone this wiki locally