• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1## This file is part of Scapy
2## See http://www.secdev.org/projects/scapy for more informations
3## Copyright (C) Philippe Biondi <phil@secdev.org>
4## This program is published under a GPLv2 license
5
6"""
7Wireless LAN according to IEEE 802.11.
8"""
9
10from __future__ import print_function
11import re,struct
12from zlib import crc32
13
14from scapy.config import conf, crypto_validator
15from scapy.data import *
16from scapy.compat import *
17from scapy.packet import *
18from scapy.fields import *
19from scapy.ansmachine import *
20from scapy.plist import PacketList
21from scapy.layers.l2 import *
22from scapy.layers.inet import IP, TCP
23from scapy.error import warning
24
25
26if conf.crypto_valid:
27    from cryptography.hazmat.backends import default_backend
28    from cryptography.hazmat.primitives.ciphers import Cipher, algorithms
29else:
30    default_backend = Ciphers = algorithms = None
31    log_loading.info("Can't import python-cryptography v1.7+. Disabled WEP decryption/encryption.")
32
33
34### Layers
35
36
37class PrismHeader(Packet):
38    """ iwpriv wlan0 monitor 3 """
39    name = "Prism header"
40    fields_desc = [ LEIntField("msgcode",68),
41                    LEIntField("len",144),
42                    StrFixedLenField("dev","",16),
43                    LEIntField("hosttime_did",0),
44                  LEShortField("hosttime_status",0),
45                  LEShortField("hosttime_len",0),
46                    LEIntField("hosttime",0),
47                    LEIntField("mactime_did",0),
48                  LEShortField("mactime_status",0),
49                  LEShortField("mactime_len",0),
50                    LEIntField("mactime",0),
51                    LEIntField("channel_did",0),
52                  LEShortField("channel_status",0),
53                  LEShortField("channel_len",0),
54                    LEIntField("channel",0),
55                    LEIntField("rssi_did",0),
56                  LEShortField("rssi_status",0),
57                  LEShortField("rssi_len",0),
58                    LEIntField("rssi",0),
59                    LEIntField("sq_did",0),
60                  LEShortField("sq_status",0),
61                  LEShortField("sq_len",0),
62                    LEIntField("sq",0),
63                    LEIntField("signal_did",0),
64                  LEShortField("signal_status",0),
65                  LEShortField("signal_len",0),
66              LESignedIntField("signal",0),
67                    LEIntField("noise_did",0),
68                  LEShortField("noise_status",0),
69                  LEShortField("noise_len",0),
70                    LEIntField("noise",0),
71                    LEIntField("rate_did",0),
72                  LEShortField("rate_status",0),
73                  LEShortField("rate_len",0),
74                    LEIntField("rate",0),
75                    LEIntField("istx_did",0),
76                  LEShortField("istx_status",0),
77                  LEShortField("istx_len",0),
78                    LEIntField("istx",0),
79                    LEIntField("frmlen_did",0),
80                  LEShortField("frmlen_status",0),
81                  LEShortField("frmlen_len",0),
82                    LEIntField("frmlen",0),
83                    ]
84    def answers(self, other):
85        if isinstance(other, PrismHeader):
86            return self.payload.answers(other.payload)
87        else:
88            return self.payload.answers(other)
89
90class RadioTap(Packet):
91    name = "RadioTap dummy"
92    fields_desc = [ ByteField('version', 0),
93                    ByteField('pad', 0),
94                    FieldLenField('len', None, 'notdecoded', '<H', adjust=lambda pkt,x:x+8),
95                    FlagsField('present', None, -32, ['TSFT','Flags','Rate','Channel','FHSS','dBm_AntSignal',
96                                                     'dBm_AntNoise','Lock_Quality','TX_Attenuation','dB_TX_Attenuation',
97                                                      'dBm_TX_Power', 'Antenna', 'dB_AntSignal', 'dB_AntNoise',
98                                                     'b14', 'b15','b16','b17','b18','b19','b20','b21','b22','b23',
99                                                     'b24','b25','b26','b27','b28','b29','b30','Ext']),
100                    StrLenField('notdecoded', "", length_from= lambda pkt:pkt.len-8) ]
101
102class PPI(Packet):
103    name = "Per-Packet Information header (partial)"
104    fields_desc = [ ByteField("version", 0),
105                    ByteField("flags", 0),
106                    FieldLenField("len", None, fmt="<H", length_of="notdecoded", adjust=lambda pkt,x:x+8),
107                    LEIntField("dlt", 0),
108                    StrLenField("notdecoded", "", length_from = lambda pkt:pkt.len-8)
109                    ]
110
111
112class Dot11(Packet):
113    name = "802.11"
114    fields_desc = [
115        BitField("subtype", 0, 4),
116        BitEnumField("type", 0, 2, ["Management", "Control", "Data",
117                                    "Reserved"]),
118        BitField("proto", 0, 2),
119        FlagsField("FCfield", 0, 8, ["to-DS", "from-DS", "MF", "retry",
120                                     "pw-mgt", "MD", "wep", "order"]),
121        ShortField("ID",0),
122        MACField("addr1", ETHER_ANY),
123        ConditionalField(
124            MACField("addr2", ETHER_ANY),
125            lambda pkt: (pkt.type != 1 or
126                         pkt.subtype in [0x9, 0xb, 0xa, 0xe, 0xf]),
127        ),
128        ConditionalField(
129            MACField("addr3", ETHER_ANY),
130            lambda pkt: pkt.type in [0, 2],
131        ),
132        ConditionalField(LEShortField("SC", 0), lambda pkt: pkt.type != 1),
133        ConditionalField(
134            MACField("addr4", ETHER_ANY),
135            lambda pkt: (pkt.type == 2 and
136                         pkt.FCfield & 3 == 3),  ## from-DS+to-DS
137        ),
138    ]
139    def mysummary(self):
140        return self.sprintf("802.11 %Dot11.type% %Dot11.subtype% %Dot11.addr2% > %Dot11.addr1%")
141    def guess_payload_class(self, payload):
142        if self.type == 0x02 and (0x08 <= self.subtype <= 0xF and self.subtype != 0xD):
143            return Dot11QoS
144        elif self.FCfield & 0x40:
145            return Dot11WEP
146        else:
147            return Packet.guess_payload_class(self, payload)
148    def answers(self, other):
149        if isinstance(other,Dot11):
150            if self.type == 0: # management
151                if self.addr1.lower() != other.addr2.lower(): # check resp DA w/ req SA
152                    return 0
153                if (other.subtype,self.subtype) in [(0,1),(2,3),(4,5)]:
154                    return 1
155                if self.subtype == other.subtype == 11: # auth
156                    return self.payload.answers(other.payload)
157            elif self.type == 1: # control
158                return 0
159            elif self.type == 2: # data
160                return self.payload.answers(other.payload)
161            elif self.type == 3: # reserved
162                return 0
163        return 0
164    def unwep(self, key=None, warn=1):
165        if self.FCfield & 0x40 == 0:
166            if warn:
167                warning("No WEP to remove")
168            return
169        if  isinstance(self.payload.payload, NoPayload):
170            if key or conf.wepkey:
171                self.payload.decrypt(key)
172            if isinstance(self.payload.payload, NoPayload):
173                if warn:
174                    warning("Dot11 can't be decrypted. Check conf.wepkey.")
175                return
176        self.FCfield &= ~0x40
177        self.payload=self.payload.payload
178
179
180class Dot11QoS(Packet):
181    name = "802.11 QoS"
182    fields_desc = [ BitField("Reserved",None,1),
183                    BitField("Ack Policy",None,2),
184                    BitField("EOSP",None,1),
185                    BitField("TID",None,4),
186                    ByteField("TXOP",None) ]
187    def guess_payload_class(self, payload):
188        if isinstance(self.underlayer, Dot11):
189            if self.underlayer.FCfield & 0x40:
190                return Dot11WEP
191        return Packet.guess_payload_class(self, payload)
192
193
194capability_list = [ "res8", "res9", "short-slot", "res11",
195                    "res12", "DSSS-OFDM", "res14", "res15",
196                   "ESS", "IBSS", "CFP", "CFP-req",
197                   "privacy", "short-preamble", "PBCC", "agility"]
198
199reason_code = {0:"reserved",1:"unspec", 2:"auth-expired",
200               3:"deauth-ST-leaving",
201               4:"inactivity", 5:"AP-full", 6:"class2-from-nonauth",
202               7:"class3-from-nonass", 8:"disas-ST-leaving",
203               9:"ST-not-auth"}
204
205status_code = {0:"success", 1:"failure", 10:"cannot-support-all-cap",
206               11:"inexist-asso", 12:"asso-denied", 13:"algo-unsupported",
207               14:"bad-seq-num", 15:"challenge-failure",
208               16:"timeout", 17:"AP-full",18:"rate-unsupported" }
209
210class Dot11Beacon(Packet):
211    name = "802.11 Beacon"
212    fields_desc = [ LELongField("timestamp", 0),
213                    LEShortField("beacon_interval", 0x0064),
214                    FlagsField("cap", 0, 16, capability_list) ]
215
216
217class Dot11Elt(Packet):
218    name = "802.11 Information Element"
219    fields_desc = [ ByteEnumField("ID", 0, {0:"SSID", 1:"Rates", 2: "FHset", 3:"DSset", 4:"CFset", 5:"TIM", 6:"IBSSset", 16:"challenge",
220                                            42:"ERPinfo", 46:"QoS Capability", 47:"ERPinfo", 48:"RSNinfo", 50:"ESRates",221:"vendor",68:"reserved"}),
221                    FieldLenField("len", None, "info", "B"),
222                    StrLenField("info", "", length_from=lambda x:x.len) ]
223    def mysummary(self):
224        if self.ID == 0:
225            ssid = repr(self.info)
226            if ssid[:2] in ['b"', "b'"]:
227                ssid = ssid[1:]
228            return "SSID=%s" % ssid, [Dot11]
229        else:
230            return ""
231
232class Dot11ATIM(Packet):
233    name = "802.11 ATIM"
234
235class Dot11Disas(Packet):
236    name = "802.11 Disassociation"
237    fields_desc = [ LEShortEnumField("reason", 1, reason_code) ]
238
239class Dot11AssoReq(Packet):
240    name = "802.11 Association Request"
241    fields_desc = [ FlagsField("cap", 0, 16, capability_list),
242                    LEShortField("listen_interval", 0x00c8) ]
243
244
245class Dot11AssoResp(Packet):
246    name = "802.11 Association Response"
247    fields_desc = [ FlagsField("cap", 0, 16, capability_list),
248                    LEShortField("status", 0),
249                    LEShortField("AID", 0) ]
250
251class Dot11ReassoReq(Packet):
252    name = "802.11 Reassociation Request"
253    fields_desc = [ FlagsField("cap", 0, 16, capability_list),
254                    LEShortField("listen_interval", 0x00c8),
255                    MACField("current_AP", ETHER_ANY) ]
256
257
258class Dot11ReassoResp(Dot11AssoResp):
259    name = "802.11 Reassociation Response"
260
261class Dot11ProbeReq(Packet):
262    name = "802.11 Probe Request"
263
264class Dot11ProbeResp(Packet):
265    name = "802.11 Probe Response"
266    fields_desc = [ LELongField("timestamp", 0),
267                    LEShortField("beacon_interval", 0x0064),
268                    FlagsField("cap", 0, 16, capability_list) ]
269
270class Dot11Auth(Packet):
271    name = "802.11 Authentication"
272    fields_desc = [ LEShortEnumField("algo", 0, ["open", "sharedkey"]),
273                    LEShortField("seqnum", 0),
274                    LEShortEnumField("status", 0, status_code) ]
275    def answers(self, other):
276        if self.seqnum == other.seqnum+1:
277            return 1
278        return 0
279
280class Dot11Deauth(Packet):
281    name = "802.11 Deauthentication"
282    fields_desc = [ LEShortEnumField("reason", 1, reason_code) ]
283
284
285
286class Dot11WEP(Packet):
287    name = "802.11 WEP packet"
288    fields_desc = [ StrFixedLenField("iv", b"\0\0\0", 3),
289                    ByteField("keyid", 0),
290                    StrField("wepdata",None,remain=4),
291                    IntField("icv",None) ]
292
293    @crypto_validator
294    def decrypt(self, key=None):
295        if key is None:
296            key = conf.wepkey
297        if key:
298            d = Cipher(
299                algorithms.ARC4(self.iv + key.encode("utf8")),
300                None,
301                default_backend(),
302            ).decryptor()
303            self.add_payload(LLC(d.update(self.wepdata) + d.finalize()))
304
305    def post_dissect(self, s):
306        self.decrypt()
307
308    def build_payload(self):
309        if self.wepdata is None:
310            return Packet.build_payload(self)
311        return b""
312
313    @crypto_validator
314    def encrypt(self, p, pay, key=None):
315        if key is None:
316            key = conf.wepkey
317        if key:
318            if self.icv is None:
319                pay += struct.pack("<I", crc32(pay) & 0xffffffff)
320                icv = b""
321            else:
322                icv = p[4:8]
323            e = Cipher(
324                algorithms.ARC4(self.iv + key.encode("utf8")),
325                None,
326                default_backend(),
327            ).encryptor()
328            return p[:4] + e.update(pay) + e.finalize() + icv
329        else:
330            warning("No WEP key set (conf.wepkey).. strange results expected..")
331            return b""
332
333    def post_build(self, p, pay):
334        if self.wepdata is None:
335            p = self.encrypt(p, raw(pay))
336        return p
337
338
339class Dot11Ack(Packet):
340    name = "802.11 Ack packet"
341
342
343bind_layers( PrismHeader,   Dot11,         )
344bind_layers( RadioTap,      Dot11,         )
345bind_layers( PPI,           Dot11,         dlt=105)
346bind_layers( Dot11,         LLC,           type=2)
347bind_layers( Dot11QoS,      LLC,           )
348bind_layers( Dot11,         Dot11AssoReq,    subtype=0, type=0)
349bind_layers( Dot11,         Dot11AssoResp,   subtype=1, type=0)
350bind_layers( Dot11,         Dot11ReassoReq,  subtype=2, type=0)
351bind_layers( Dot11,         Dot11ReassoResp, subtype=3, type=0)
352bind_layers( Dot11,         Dot11ProbeReq,   subtype=4, type=0)
353bind_layers( Dot11,         Dot11ProbeResp,  subtype=5, type=0)
354bind_layers( Dot11,         Dot11Beacon,     subtype=8, type=0)
355bind_layers( Dot11,         Dot11ATIM,       subtype=9, type=0)
356bind_layers( Dot11,         Dot11Disas,      subtype=10, type=0)
357bind_layers( Dot11,         Dot11Auth,       subtype=11, type=0)
358bind_layers( Dot11,         Dot11Deauth,     subtype=12, type=0)
359bind_layers( Dot11,         Dot11Ack,        subtype=13, type=1)
360bind_layers( Dot11Beacon,     Dot11Elt,    )
361bind_layers( Dot11AssoReq,    Dot11Elt,    )
362bind_layers( Dot11AssoResp,   Dot11Elt,    )
363bind_layers( Dot11ReassoReq,  Dot11Elt,    )
364bind_layers( Dot11ReassoResp, Dot11Elt,    )
365bind_layers( Dot11ProbeReq,   Dot11Elt,    )
366bind_layers( Dot11ProbeResp,  Dot11Elt,    )
367bind_layers( Dot11Auth,       Dot11Elt,    )
368bind_layers( Dot11Elt,        Dot11Elt,    )
369
370
371conf.l2types.register(DLT_IEEE802_11, Dot11)
372conf.l2types.register_num2layer(801, Dot11)
373conf.l2types.register(DLT_PRISM_HEADER, PrismHeader)
374conf.l2types.register_num2layer(802, PrismHeader)
375conf.l2types.register(DLT_IEEE802_11_RADIO, RadioTap)
376conf.l2types.register_num2layer(803, RadioTap)
377conf.l2types.register(DLT_PPI, PPI)
378
379
380class WiFi_am(AnsweringMachine):
381    """Before using this, initialize "iffrom" and "ifto" interfaces:
382iwconfig iffrom mode monitor
383iwpriv orig_ifto hostapd 1
384ifconfig ifto up
385note: if ifto=wlan0ap then orig_ifto=wlan0
386note: ifto and iffrom must be set on the same channel
387ex:
388ifconfig eth1 up
389iwconfig eth1 mode monitor
390iwconfig eth1 channel 11
391iwpriv wlan0 hostapd 1
392ifconfig wlan0ap up
393iwconfig wlan0 channel 11
394iwconfig wlan0 essid dontexist
395iwconfig wlan0 mode managed
396"""
397    function_name = "airpwn"
398    filter = None
399
400    def parse_options(self, iffrom=conf.iface, ifto=conf.iface, replace="",
401                            pattern="", ignorepattern=""):
402        self.iffrom = iffrom
403        self.ifto = ifto
404        self.ptrn = re.compile(pattern.encode())
405        self.iptrn = re.compile(ignorepattern.encode())
406        self.replace = replace
407
408    def is_request(self, pkt):
409        if not isinstance(pkt,Dot11):
410            return 0
411        if not pkt.FCfield & 1:
412            return 0
413        if not pkt.haslayer(TCP):
414            return 0
415        ip = pkt.getlayer(IP)
416        tcp = pkt.getlayer(TCP)
417        pay = raw(tcp.payload)
418        if not self.ptrn.match(pay):
419            return 0
420        if self.iptrn.match(pay) == True:
421            return 0
422        return True
423
424    def make_reply(self, p):
425        ip = p.getlayer(IP)
426        tcp = p.getlayer(TCP)
427        pay = raw(tcp.payload)
428        del(p.payload.payload.payload)
429        p.FCfield="from-DS"
430        p.addr1,p.addr2 = p.addr2,p.addr1
431        p /= IP(src=ip.dst,dst=ip.src)
432        p /= TCP(sport=tcp.dport, dport=tcp.sport,
433                 seq=tcp.ack, ack=tcp.seq+len(pay),
434                 flags="PA")
435        q = p.copy()
436        p /= self.replace
437        q.ID += 1
438        q.getlayer(TCP).flags="RA"
439        q.getlayer(TCP).seq+=len(self.replace)
440        return [p,q]
441
442    def print_reply(self, query, *reply):
443        p = reply[0][0]
444        print(p.sprintf("Sent %IP.src%:%IP.sport% > %IP.dst%:%TCP.dport%"))
445
446    def send_reply(self, reply):
447        sendp(reply, iface=self.ifto, **self.optsend)
448
449    def sniff(self):
450        sniff(iface=self.iffrom, **self.optsniff)
451
452
453conf.stats_dot11_protocols += [Dot11WEP, Dot11Beacon, ]
454
455
456
457
458
459class Dot11PacketList(PacketList):
460    def __init__(self, res=None, name="Dot11List", stats=None):
461        if stats is None:
462            stats = conf.stats_dot11_protocols
463
464        PacketList.__init__(self, res, name, stats)
465    def toEthernet(self):
466        data = [x[Dot11] for x in self.res if Dot11 in x and x.type == 2]
467        r2 = []
468        for p in data:
469            q = p.copy()
470            q.unwep()
471            r2.append(Ether()/q.payload.payload.payload) #Dot11/LLC/SNAP/IP
472        return PacketList(r2,name="Ether from %s"%self.listname)
473
474
475