Using Scapy to send WLAN frames

Scapy[1] is one mighty python tool to create, receive and manipulate various network packets and it comes with a very handy CLI as well. If you ever had the need to create specific network packets within a program, I suggest you use python together with scapy to do so. Let’s start with a short introduction to the scapy CLI and end with a small example of scapy within python code. With the right kind of WLAN interface it is even possible to create a connection to an unencrypted SSID and I am going to publish the steps to do so in another post.

Pleate note: The following commands were made on a Fedora 23 Linux system with an TP-Link TL-WN727N (v4.1) WLAN USB Stick. The scapy package can be installed as python-scapy (Ubuntu) or just scapy (Fedora), wireshark is also required. Your linux and python skills should be quite solid and include knowledge about the use of ifconfig and iwconfig in linux as well as class definition and inheritance in python.

Scapy CLI

After using a bash to call “scapy”, the CLI should show up and should look like this (Your version might be different):

Welcome to Scapy (2.2.0)
>>>

You can take a look at all the predefined protocols via the “ls()” command…

>>> ls()
ARP : ARP
ASN1_Packet : None
BOOTP : BOOTP
CookedLinux : cooked linux
DHCP : DHCP options [...]

… and get some details about them via “ls(<PROTOCOL>)” e.g. ls(ARP):

>>> ls(ARP)
hwtype : XShortField = (1)
ptype : XShortEnumField = (2048)
hwlen : ByteField = (6)
plen : ByteField = (4)
op : ShortEnumField = (1)
hwsrc : ARPSourceMACField = (None)
psrc : SourceIPField = (None)
hwdst : MACField = ('00:00:00:00:00:00')
pdst : IPField = ('0.0.0.0')

Since we want to send WLAN packets, the following predefined protocols can be used “out of the box”:

Dot11 : 802.11
Dot11ATIM : 802.11 ATIM
Dot11AssoReq : 802.11 Association Request
Dot11AssoResp : 802.11 Association Response
Dot11Auth : 802.11 Authentication
Dot11Beacon : 802.11 Beacon
Dot11Deauth : 802.11 Deauthentication
Dot11Disas : 802.11 Disassociation
Dot11Elt : 802.11 Information Element
Dot11ProbeReq : 802.11 Probe Request
Dot11ProbeResp : 802.11 Probe Response
Dot11QoS : 802.11 QoS
Dot11ReassoReq : 802.11 Reassociation Request
Dot11ReassoResp : 802.11 Reassociation Response
Dot11WEP : 802.11 WEP packet
[...]
RadioTap : RadioTap dummy

A quick example of how to send a Dot11 frame, e.g. a Null Frame, from an AP (e.g. MAC = 00:a0:57:98:76:54) to a Client (STA) (e.g. MAC = 00:a0:57:12:34:56) is:

>>> ls(Dot11)
subtype : BitField = (0)
type : BitEnumField = (0)
proto : BitField = (0)
FCfield : FlagsField = (0)
ID : ShortField = (0)
addr1 : MACField = ('00:00:00:00:00:00')
addr2 : Dot11Addr2MACField = ('00:00:00:00:00:00')
addr3 : Dot11Addr3MACField = ('00:00:00:00:00:00')
SC : Dot11SCField = (0)
addr4 : Dot11Addr4MACField = ('00:00:00:00:00:00')
>>> packet = Dot11(addr1="00:a0:57:12:34:56", addr2="00:a0:57:98:76:54", addr3="00:a0:57:98:76:54", type=2, subtype=4)

The addr1 field defines the destination/receiver, addr2 the source/transmitter and addr3 the BSSID of the frame. The Null frame is defined as type 2 and subtype 4. One of the most powerful possibilities of the scapy CLI is the direct call of wireshark to inspect a packet. This is as handy as:

>>> wireshark(packet)

A wireshark window should open and display the packet as shown in the following screenshot:
Scapy Packet in Wireshark

 

Creating a customized packet

Although there are a lot of predefined protocols/packets, we do need to create certain packets/protocols like e.g. information fields manually. This is where python comes into play and a more elaborate introduction can be found at [2].

We import scapy and create a class that is derived from scapy.Packet. Besides the class member name, the fields_desc is the most important variable. With the help of ByteField from scapy, we can create new information elements like ID and length of the included elements as well as the supported rates themselves by setting up a list of supported rates in hexadecimal notation and insert these values in ByteFields. Since each ByteField is unique, the names of the fields for supported rates need to differ, that’s why we enumerate the values and create them with supported_rate1, supported_rate2 and so on.

from scapy.all import *

class Dot11EltRates(Packet):
    """ Our own definition for the supported rates field """
    name = "802.11 Rates Information Element"
    # Our Test STA supports the rates 6, 9, 12, 18, 24, 36, 48 and 54 Mbps
    supported_rates = [0x0c, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c]
    fields_desc = [ByteField("ID", 1), ByteField("len", len(supported_rates))]
    for index, rate in enumerate(supported_rates):
        fields_desc.append(ByteField("supported_rate{0}".format(index + 1),
                                     rate))

You can either enter the values of the defined fields as hexadecimal ones with “0x” in front or as integers like the value of the “ID” ByteField in the example above. It is important that the byte value of the “len” field matches length of the successive byte elements.

Sending a (customized) packet

If we want to send a packet via python/scapy, we first have to set up our monitoring interface, which will be our USB WLAN Stick. We can use the command “iwconfig” in a bash to find the all the installed WLAN interfaces, use “ifconfig” to identify the right one via MAC address if you got more than one WLAN interface. In my case the TP-Link WLAN USB Stick is named “wlp0s29u1u7”. For the following steps, you need to be root or use sudo to configure the stick to the required monitor mode.

1) Turn the interface down via “ifconfig wlp0s29u1u7 down”
2) Set the device to monitor mode via “iwconfig wlp0s29u1u7 mode monitor”
3) Turn the interface back on via “ifconfig wlp0s29u1u7 up”
4) Set the interface to a certain channel via “iwconfig wlp0s29u1u7 chan 6”
5) If the settings were correctly adapted, the output of “iwconfig wlp0s29u1u7” includes “Mode:Monitor” and “Frequency:2.437 GHz” (for channel 6 in this example).

We can now use this interface to send packets via scapy. The following python code builds an association request packet via the predefined Dot11, Dot11AssoReq and Dot11Elt packets and extends the packet with rate information that is defined in the class given above. Afterwards we use the “sendp()” command to send the packet on the defined monitoring interface.

packet = Dot11(
    addr1="00:a0:57:98:76:54",
    addr2="00:a0:57:12:34:56",
    addr3="00:a0:57:98:76:54") / Dot11AssoReq(
        cap=0x1100, listen_interval=0x00a) / Dot11Elt(
            ID=0, info="MY_BSSID")
packet /= Dot11EltRates()
sendp(packet, iface="wlp0s29u1u7")
packet.show()

The code has to be executed as root or via sudo due to the control over the network interface. The script will output a warning, that no route for IPv6 destination :: could be found (happens during import of scapy and can be ignored) afterwards a “.” and the message “Sent 1 packets” should be visible, which means that the created packet was sent.

WARNING: No route found for IPv6 destination :: (no default route?). This affects only IPv6
.
Sent 1 packets.

Due to the packet.show() command at the end of our code, scapy will also print the packet to the standard output:

###[ 802.11 ]###
 subtype = 0
 type = Management
 proto = 0
 FCfield = 
 ID = 0
 addr1 = 00:a0:57:98:76:54
 addr2 = 00:a0:57:12:34:56
 addr3 = 00:a0:57:98:76:54
 SC = 0
 addr4 = 00:00:00:00:00:00
###[ 802.11 Association Request ]###
 cap = ESS+privacy
 listen_interval= 10
###[ 802.11 Information Element ]###
 ID = SSID
 len = None
 info = 'MY_BSSID'
###[ 802.11 Rates Information Element ]###
 ID = 1
 len = 8
 supported_rate1= 12
 supported_rate2= 18
 supported_rate3= 24
 supported_rate4= 36
 supported_rate5= 48
 supported_rate6= 72
 supported_rate7= 96
 supported_rate8= 108

You might have noticed that scapy does not print the length of the 802.11 Information Element containing the SSID info. The length is calculated automatically while sending the packet and not while it is printed. See the following screenshot in the Conclusion for proof.

Conclusion

Within the examples given in this post, we are able to create packets based on predefined ones in scapy and are able modify or create certain elements via python. If you are able to sniff the packet on the air, we can take a look at it in wireshark:

Scapy Assoc Packet in Wireshark

Following up is the complete code.

from scapy.all import *

class Dot11EltRates(Packet):
    """ Our own definition for the supported rates field """
    name = "802.11 Rates Information Element"
    # Our Test STA supports the rates 6, 9, 12, 18, 24, 36, 48 and 54 Mbps
    supported_rates = [0x0c, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c]
    fields_desc = [ByteField("ID", 1), ByteField("len", len(supported_rates))]
    for index, rate in enumerate(supported_rates):
        fields_desc.append(ByteField("supported_rate{0}".format(index + 1),
                                     rate))

packet = Dot11(
    addr1="00:a0:57:98:76:54",
    addr2="00:a0:57:12:34:56",
    addr3="00:a0:57:98:76:54") / Dot11AssoReq(
        cap=0x1100, listen_interval=0x00a) / Dot11Elt(
            ID=0, info="MY_BSSID")
packet /= Dot11EltRates()
sendp(packet, iface="wlp0s29u1u7")
packet.show()

Links

[1] http://www.secdev.org/projects/scapy/
[2] http://www.secdev.org/projects/scapy/doc/build_dissect.html

Advertisements

5 thoughts on “Using Scapy to send WLAN frames

  1. Hi, thank you for your detailed post.
    I also created a script to send 80211 frames using Scapy some
    time ago, but I’m facing a problem:

    You’re defining the adapters capabilities and supported rates in the variable “supported_rates”.
    But what if I don’t know which wifi adapter I will use or if I want to develop a script that is compatible to a large number of wifi adapters?

    Do you know a way how to read the current wifi adapters capabilities with python in order to automatically use those for sending Association frames?

    Regards,
    Bastian

    Like

    • Hello Bastian,
      sorry for this quite late response. I don’t think that there should be a problem with the wifi adapters since they usually support all 11a/g rates. Nonetheless, there might be some WLAN APs with configurations that exclude certain 11a/g rates within the basic rate field for various reasons.

      You could sniff the beacons from the WLAN AP you want to attack and adjust the supported rates field accordingly, which can be done either manually or via script. However, I found it rather complicated to modify this field dynamically and did not succeed with my initial attempt to implement that. But I guess it’s still possible to do.

      Like

  2. hello ! i find how to send packet due to your post, Thank.
    and I’d like to receive packet by using srp function. Do you know how to receive Probe response, Authtification, Association response packet?
    I capture packet on wireshark, Probe responses, Auth packetes are sended to LAN card that i forge packet’s MAC address

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s