Skip to content

Client Disruption

Qwavey edited this page Aug 23, 2026 · 1 revision

Client Disruption

Deauth, Bluetooth disruption, and wired-LAN ARP/IPv6 kill.

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

  • deauth.sh - Deauthenticate WiFi clients via PineAP (outside attacker or LAN-side).
  • bluetooth.sh - Bluetooth recon + disruption over the Pager's internal radio.
  • deadnet.sh - Disconnect wired-LAN devices via ARP-cache poisoning + IPv6 dead-router spoofing.

deauth.sh

Deauthenticate WiFi clients via PineAP (outside attacker or LAN-side).

deauth.sh - Deauthenticate WiFi clients via PineAP. For clients you're NOT
connected to (an outside attacker knocking clients off a WiFi network).
Wraps PINEAPPLE_DEAUTH_CLIENT. For LAN-side disconnection when you ARE on
the network, use deadnet.sh instead - different tool, different trust model.

Only works on 2.4GHz/non-DFS 5GHz; PMF/WPA3/6GHz networks are resistant to
this by design.

Per Hak5's own docs: "Networks which utilize Protected Management Frames
(PMF) or the 802.11w standard will not be susceptible to injected
disconnection packets. All networks utilizing WPA3 also enable Protected
Management Frames... Some clients ignore disconnection attempts
deliberately regardless of the network type or channel." That's a
protocol-level ceiling, not a bug in this script - no deauth tool can
beat it. What this script CAN do is maximize pressure within that ceiling
(see --burst).

A single DEAUTH_CLIENT call only sends one round of disconnect packets -
real clients reassociate within a second or two, so this now REPEATS the
attack continuously (like a real deauth flood) until you stop it, instead
of firing once and exiting. Foreground: Ctrl+C stops it. Launched with
--background (what the wifi_deauth payload does): use --stop.

"Works sometimes" against a client that mostly stays connected (confirmed
via a real ping -t transcript showing occasional timeouts among mostly-
successful replies) is the EXPECTED signature of a client that reconnects
fast, not evidence the attack is broken - see --burst below, which raises
disruption pressure per window, and Hak5's own docs (quoted further down)
on PMF/802.11w and clients that deliberately ignore disconnects.

BUG FOUND AND FIXED (live-diagnosed): recon hops radio channels FAST -
confirmed by sampling `iw dev wlan1mon info` once a second, it showed a
DIFFERENT channel on almost every sample. Hak5's own docs say
DEAUTH_CLIENT "returns immediately... in the background, the PineAP
system will transmit" the packets - it's fire-and-forget, not
synchronous, and does not itself guarantee the radio is actually parked
on the target's channel the instant it transmits. If hopping had already
moved on by then, the deauth frame went out on the WRONG channel and did
nothing - the command still "succeeded" with no error, so this looked
exactly like "deauth just doesn't work" from the outside. Every attack
loop below now locks the radio to the target's channel (via the
official PINEAPPLE_EXAMINE_CHANNEL, confirmed live it reliably holds)
immediately before each transmission, and resets to normal hopping when
stopped.

Target defaults to FF:FF:FF:FF:FF:FF (every client of the AP) without
asking - picking one specific client isn't the normal case, so --pick no
longer prompts for it. Pass --target explicitly if you want just one.

BUG FOUND AND FIXED (live-diagnosed - the real explanation for reports
that "the whole network only gets deauthed sometimes"): --all used to
source its target list from recon's hostap_client table (which AP each
specific client MAC is associated to). Checked that table directly on a
real device mid-investigation and found it completely EMPTY - recon had
never recorded a single client association, so every round of the old
--all found zero targets and sent NOTHING, not "spread thin across many
devices". Hak5's own docs say the target argument accepts
"FF:FF:FF:FF:FF:FF for all clients" - broadcast target already
disconnects every client of a BSSID with no per-client MAC needed at
all. --all now attacks every AP recon has beaconed from (the `ssid`
table - reliably populated, --scan/--ssid/--pick already depend on it)
with a broadcast target instead - the same proven mechanism --ssid/
--bssid already use, just applied to every nearby AP instead of one.

IMPORTANT: only use this against networks/clients you are authorized to
test. Deauthenticating devices you don't own or don't have permission to
test may be illegal in your jurisdiction.

Usage:
  deauth.sh --bssid AA:BB:CC:DD:EE:FF [--target MAC] --channel 6
  deauth.sh --ssid "SSID name" [--target MAC]
  deauth.sh --all
  deauth.sh --scan
  deauth.sh --pick
  deauth.sh --stop
  deauth.sh --status
  deauth.sh                interactive mode

Options:
  --bssid MAC       BSSID (MAC) of one specific access point
  --ssid NAME         Attack EVERY AP recon has seen broadcasting this exact
                         SSID, not just one BSSID - the normal case when
                         several access points share one name (a mesh
                         network, or just unrelated routers using the same
                         default name). --scan/--pick group by SSID for
                         exactly this reason.
  --target MAC       MAC of the client to disconnect (default: FF:FF:FF:FF:FF:FF, every client - you don't normally need to set this)
  --channel N          Channel to send disconnect packets on (only with --bssid - --ssid looks channels up per-AP itself)
  --interval SECONDS      Delay between repeat rounds while attacking (default: 2)
  --burst N               Deauth calls sent back-to-back within each
                             channel-locked window before sleeping
                             (default: 3). More packets per window = more
                             disruption pressure on clients that reconnect
                             fast - raise this if a target seems to only
                             partially disconnect. Doesn't help at all
                             against PMF/WPA3 (see above) - that's a wall,
                             not a density problem.
  (--second-radio / raw injection on the internal radio: REMOVED. Caused
   repeated hard hangs on real hardware - the kernel went silent with no
   panic/oops trace, meaning a genuine USB/firmware-level lockup needing
   a manual power cycle, not a soft crash. Also, "internal only" was
   never a real choice via official commands either way - confirmed
   live that DEAUTH_CLIENT/PINEAPPLE_EXAMINE_CHANNEL take no radio-
   select parameter and always drive whichever radio PineAP itself
   designates.)

  CHASE MODE (automatic, no flag needed): with --ssid (a mesh/multi-AP
    group) AND a specific --target (not the FF:FF:FF:FF:FF:FF broadcast
    default), each round briefly re-checks which BSSID the target MAC
    is actually transmitting from and prioritizes attacking THAT one -
    instead of blind round-robin across every AP on a fixed schedule
    regardless of where the target actually is. Built specifically for
    mesh networks with roaming (802.11k/v/r): kicking a client off one
    AP just bounces it to a sibling AP near-invisibly, so the real fix
    isn't more attack surface, it's tracking where the client actually
    went and following it there. See find_target_bssid() below. Falls
    back to normal round-robin whenever the target hasn't been sighted
    yet (unknown location - attack broadly until found).

  REACTIVE MODE (--reactive flag, needs a specific --target): a
    different strategy from chase mode above, and mutually exclusive
    with it - chase mode still polls on a schedule (just prioritizing
    smartly), reactive mode replaces the schedule entirely with an
    event trigger. Live testing found that once a target IS located,
    escalating burst pressure (3->5 packets/round) against it still had
    ZERO observed effect - the target was reassociating within 1-2
    seconds, faster than a full multi-AP attack round can even
    complete once. That means the bottleneck isn't packets-per-round,
    it's LATENCY: our reaction time to "the target is reconnecting
    right now" versus theirs. Reactive mode closes that gap directly -
    see run_reactive_strike() below for the mechanism.
  --all                  Continuously deauth EVERY AP recon has seen a
                           beacon from (broadcast target = every client
                           of it, no per-client tracking needed), using
                           the Pineapple's own recon database
                           (/root/recon/recon.db - the same data the
                           physical Recon screen shows). Re-scans recon
                           each round, so newly-seen APs get picked up
                           too. Needs recon to have beaconed at least one
                           AP - normally just seconds, not the long wait
                           per-client tracking needed (see the file
                           header for why this changed).
  --scan                  List nearby networks seen by recon, grouped by
                             SSID with an AP count (e.g. "Hotspot (3)") -
                             several BSSIDs sharing one name show as ONE
                             entry, not N separate ones. Read-only, no attack.
  --pick                  Smart guided mode: if not connected to a WiFi
                             network, scan and let you pick one of the
                             nearby SSIDs seen by recon (grouped the same
                             way as --scan - picking one attacks every AP
                             broadcasting that name); if connected, uses
                             that specific network directly. Targets ALL
                             clients by default - pass --target yourself
                             first if you want one specific client.
  --no-discover             Skip real client-MAC discovery (see below) -
                               broadcast target only, starts attacking
                               immediately instead of a brief listen first.
  --reactive                EVENT-TRIGGERED STRIKE MODE (needs a specific
                               --target, not the broadcast default - see
                               REACTIVE MODE below). Instead of a fixed-
                               interval polling loop, continuously watches
                               for the target's own auth/(re)association
                               frames and fires an immediate priority
                               burst the instant one is seen, instead of
                               waiting out a scheduled round. Built
                               because live testing showed burst
                               escalation (more packets per round) had
                               zero effect against a target that
                               reassociates in 1-2s - the real gap is
                               LATENCY between our attack cadence and
                               theirs, not attack strength. Detection
                               watches plain decoded tcpdump text (grep
                               on decoded output, deliberately NOT a
                               `wlan type mgt subtype ...` BPF filter
                               primitive - see run_reactive_strike()'s
                               own header comment for why) for
                               "Authentication"/"Assoc Request"/
                               "ReAssoc Request" - keyword list
                               LIVE-VERIFIED against this exact tcpdump
                               binary via a synthetic pcap (see
                               run_reactive_strike()'s header comment):
                               the decoder abbreviates (re)association
                               frames as "Assoc Request"/"ReAssoc
                               Request", NOT the full spec names this
                               originally grepped for - that mismatch
                               would have silently caught Authentication
                               frames only and missed every real
                               (re)association attempt. Fixed and
                               re-verified. A SECOND, more fundamental
                               bug found the same way: tcpdump's default
                               summary line carries NO MAC address at
                               all for these frame types unless run with
                               "-e" - meaning the target-MAC match that
                               runs after the keyword match would have
                               matched nothing, ever, even with the
                               keyword fix above. Both tcpdump calls now
                               pass "-e".
  --background             Run the attack detached - use --stop to end it.
  --stop                    Stop a background attack.
  --status                    Is an attack currently running?
  -y, --yes              Skip the authorization confirmation
  -h, --help               This help

Client-MAC discovery (on by default, --bssid/--ssid/--pick, when TARGET
is left at the default broadcast): before attacking, briefly listens on
the target's (locked) channel for real client MAC addresses actually
active there, and - if any are found - targets each of them directly
with a unicast deauth every round, ALONGSIDE the normal broadcast
target. WHY: Hak5's docs confirm FF:FF:FF:FF:FF:FF is meant to hit every
client, but real-world WiFi driver behavior around broadcast-addressed
management frames varies - some are pickier about them than about a
frame addressed to their own exact MAC. This closes that gap without
depending on recon's own client tracking (confirmed elsewhere in this
file to be empty/unreliable on this device). One-time cost per attack
start (~2s), not repeated every round. Not used by --all (would mean
re-discovering on every AP, every round - too expensive to be worth it
there; --all stays broadcast-only).

bluetooth.sh

Bluetooth recon + disruption over the Pager's internal radio.

bluetooth.sh - Bluetooth recon + disruption using the Pager's internal
radio (hci0, a MediaTek combo chip - confirmed live via `hciconfig -a`
and `lsusb`) and the standard BlueZ userspace tools that ship on this
device (bluez-utils/bluez-utils-extra - confirmed installed: hcitool,
l2ping, btmgmt, bluetoothctl).

IMPORTANT: unlike WiFi/PineAP, Hak5 does NOT document a dedicated
Bluetooth DuckyScript command family for the Pager (checked the full
docs - no BT_* commands exist). There is nothing to fabricate here - this
script just drives the real, standard Linux BlueZ stack directly, the
same tools you'd use on any Linux box with a Bluetooth adapter.

Four real disruption techniques:
  --flood     l2ping -f flood against ONE known/discoverable target MAC.
                Real L2CAP echo flood, works against classic BT devices
                (headphones/earbuds/speakers) that accept a connection.
                Runs continuously until stopped (Ctrl+C, or --stop when
                backgrounded) unless you set --duration.
  --jam-area    Scans first (classic AND BLE), then L2CAP-floods EVERY
                  device it found, on rotation, continuously - no target
                  to pick, jams whatever's discoverable nearby. Same stop
                  behavior as --flood.
  --advspam     Registers several rotating BLE advertising instances
                  (Bluetooth SIG's own reserved test company ID 0xFFFF,
                  not spoofing any real vendor/product) to disrupt nearby
                  BLE scanners/pairing UIs. Doesn't need a target either.
                  Unlike the other two, this hands the whole run off to
                  BlueZ itself (via btmgmt's own --duration) instead of
                  polling in a loop - btmgmt was found to hang
                  indefinitely if it's ever NOT the shell's direct
                  foreground process, so nothing here backgrounds it.
                  Runs for --duration (default 3600s) or until --stop.
  --disrupt     Real-world testing showed --flood/--jam-area don't touch
                  a device that's ALREADY connected+streaming to its real
                  owner (most peripherals refuse new connections outright
                  while busy - confirmed live, nothing to flood at all in
                  that case). This is different: no scan, no target, no
                  connection attempt of any kind. It puts the radio into
                  Direct Test Mode - a real, standard part of the
                  Bluetooth Core Spec every controller implements for RF
                  certification (HCI LE Transmitter Test / LE Test End,
                  confirmed live: accepted with status 0x00, i.e. the
                  controller genuinely entered continuous-TX mode) - and
                  cycles it across all 40 BLE channels (2402-2480MHz,
                  which is the ENTIRE 2.4GHz ISM band classic Bluetooth's
                  own frequency-hopping also lives in) continuously. This
                  doesn't care whether a device is connected, idle,
                  discoverable, or hidden - it just occupies the shared
                  spectrum directly. Still the Pager's own low-power BT
                  radio at its normal operating power (same hardware
                  --flood/--advspam already use) - not an external
                  amplifier or dedicated jamming device - so range is
                  short (close proximity), and it's genuinely not
                  guaranteed against every device. But it's the most
                  direct, real technique available that doesn't depend on
                  the target accepting a connection at all.
                  HONEST SCOPE NOTE: this specifically drives LE
                  (Bluetooth Low Energy) Direct Test Mode. Many audio
                  earbuds/headphones actually stream audio over CLASSIC
                  Bluetooth (BR/EDR, e.g. A2DP), which hops using its own
                  scheme across the same 2.4GHz band rather than BLE's -
                  the RF energy this produces still physically occupies
                  that same shared spectrum (there's only one 2.4GHz ISM
                  band, regardless of protocol label), but it isn't a
                  protocol-aware attack on the classic link specifically.
                  If disrupt still doesn't visibly affect a given device,
                  that's the most likely honest reason why - not
                  something a bigger --dwell alone can fully solve.

IMPORTANT: only use this against your own devices or ones you are
explicitly authorized to test. Disrupting other people's Bluetooth
devices may be illegal in your jurisdiction. --disrupt in particular
occupies shared spectrum other devices (yours and anyone else's in range)
rely on - keep it pointed at your own testing, not a shared/public space.

Usage:
  bluetooth.sh --scan [--ble] [--duration SECONDS]
  bluetooth.sh --flood MAC [--duration SECONDS] [--size BYTES]
  bluetooth.sh --jam-area [--scan-time SECONDS] [--duration SECONDS]
  bluetooth.sh --advspam [--duration SECONDS]
  bluetooth.sh --disrupt [--duration SECONDS] [--dwell SECONDS] [--focus]
  bluetooth.sh --stop
  bluetooth.sh --status
  bluetooth.sh                interactive mode

Options:
  --scan                  Discover nearby Bluetooth devices (classic).
  --ble                     With --scan, also do a BLE (Low Energy) scan.
  --flood MAC                L2CAP echo flood against one target (DoS).
  --jam-area                   Scan, then L2CAP-flood every device found,
                                  on rotation, no target needed.
  --advspam                    Flood the area with generic BLE adverts.
  --disrupt                      Direct Test Mode spectrum occupation across
                                    all 40 BLE channels - no scan, no target,
                                    works on connected devices too. See above.
  --dwell SECONDS                     With --disrupt: how long to hold each
                                         channel before moving to the next
                                         (default: 0.3). See the real-world
                                         fix note on run_disrupt() - this is
                                         the knob that actually matters.
  --focus                                With --disrupt: only occupy the 3
                                            FIXED BLE advertising channels
                                            (37/38/39) instead of sweeping
                                            all 40 - these are never hopped,
                                            so this guarantees overlap with
                                            any BLE scan/advertise/reconnect
                                            activity instead of chasing a
                                            moving target across 37 data
                                            channels that Bluetooth's own
                                            Adaptive Frequency Hopping will
                                            actively route away from noise.
  --scan-time SECONDS              How long to scan (classic + BLE) before --jam-area starts (default: 15).
  --burst SECONDS                    How long to flood EACH device per rotation in --jam-area
                                        (default: 8 - a handful of packets barely dents an
                                        active connection, this needs to be sustained).
  --duration SECONDS                 How long to run (default: 15 for --scan; until stopped
                                        for --flood/--jam-area/--disrupt; 3600s for --advspam).
  --size BYTES                         l2ping payload size for --flood (default: 600).
  --background                           Run detached; use --stop to end.
  --stop                                   Stop a background run.
  --status                                   Is a background run active?
  -y, --yes                                   Skip the authorization confirmation.
  -h, --help                                    This help.

deadnet.sh

Disconnect wired-LAN devices via ARP-cache poisoning + IPv6 dead-router spoofing.

deadnet.sh - Disconnect devices on the wired LAN via ARP-cache poisoning +
IPv6 dead-router spoofing (powered by deadnet, trimmed down from
https://github.com/flashnuke/deadnet - just the ~30KB core tool, not its
unrelated 71MB bundled Android app). For when you're ALREADY ON the
network - different tool/trust model than deauth.sh, which is for WiFi
clients you're NOT connected to.

This is the ONE place that actually runs deadnet - the deadnet_lan_kill
payload and the GUI both call this same script with the same arguments,
so behavior is identical no matter how you launch it.

Runs continuously until you press Ctrl+C - it does not stop on its own.
If started in the background (GUI/PayloadRunner --background), use
`deadnet.sh --stop` instead.

IMPORTANT: only run this against a network you are authorized to test.

Usage:
  deadnet.sh [--iface eth1] [--gateway IP] [--gateway-mac MAC] [--sleep SECONDS]
  deadnet.sh --discover [--iface eth1]      just scan and list live hosts, don't attack
  deadnet.sh --background                     run detached - use --stop to end it
  deadnet.sh --stop
  deadnet.sh --status
  deadnet.sh                interactive mode

Options:
  --iface IFACE       Wired interface to attack from (default: eth1)
  --gateway IP          Gateway IPv4 (default: auto-detected, x.x.x.1)
  --gateway-mac MAC       Gateway MAC (default: auto-detected via ARP)
  --sleep SECONDS           Re-poison interval (default: 5)
  --cidr N                    Subnet size to attack (default: 24)
  --disable-ipv6                 Skip the IPv6 dead-router-advertisement attack
  --no-discover                    Skip the pre-attack host discovery scan
  --discover                         Only discover live hosts, don't attack
  --background                         Run detached - see --stop/--status.
                                           Output still goes to /tmp/pager-deadnet.log.
  --stop                                Stop a run that was started in the background
  --status                                Is a LAN kill currently running?
  -y, --yes                                Skip the authorization confirmation
  -h, --help                                 This help

BUG FOUND AND FIXED (this file was previously excluded from bug-hunt/
improve passes as "good enough" - re-scoped in per user request): this
file's own header comment above used to say "If started in the
background (GUI/PayloadRunner --background), use `deadnet.sh --stop`
instead" - but no --background flag existed anywhere in the argument
parser. The deadnet_lan_kill payload worked around this by backgrounding
externally with a plain `&`, with no PIDFILE, no liveness check, and no
single-instance protection - the exact "black box, no feedback,
possible-double-launch" class of gap this toolkit's every OTHER
background-capable script (deauth.sh/sniff.sh/bluetooth.sh/tracer.sh/
EvilTwin.sh) already fixed for itself. Brought in line with that same
established PIDFILE + pid_running() + liveness-check pattern.

Host discovery: before attacking, this scans the subnet with nmap (a
quick ping sweep) and shows you exactly which real, live hosts are on
the LAN and about to be affected - not just "the whole subnet" as an
abstraction. The underlying deadnet attack still covers the full
subnet (that's how ARP poisoning works - you can't be selective about
who receives poisoned ARP replies without a different attack entirely),
but you now get told upfront who's actually there before you commit,
and the discovered host list is saved to loot alongside the run.

Clone this wiki locally