# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # Copyright (C) Philippe Biondi """ Wireless LAN according to IEEE 802.11. """ from __future__ import print_function import math import re import struct from zlib import crc32 from scapy.config import conf, crypto_validator from scapy.data import ETHER_ANY, DLT_IEEE802_11, DLT_PRISM_HEADER, \ DLT_IEEE802_11_RADIO from scapy.compat import raw, plain_str, orb, chb from scapy.packet import Packet, bind_layers, bind_top_down, NoPayload from scapy.fields import ByteField, LEShortField, BitField, LEShortEnumField, \ ByteEnumField, X3BytesField, FlagsField, LELongField, StrField, \ StrLenField, IntField, XByteField, LEIntField, StrFixedLenField, \ LESignedIntField, ReversePadField, ConditionalField, PacketListField, \ ShortField, BitEnumField, FieldLenField, LEFieldLenField, \ FieldListField, XStrFixedLenField, PacketField, FCSField, \ ScalingField from scapy.ansmachine import AnsweringMachine from scapy.plist import PacketList from scapy.layers.l2 import Ether, LLC, MACField from scapy.layers.inet import IP, TCP from scapy.error import warning, log_loading from scapy.sendrecv import sniff, sendp if conf.crypto_valid: from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.ciphers import Cipher, algorithms else: default_backend = Ciphers = algorithms = None log_loading.info("Can't import python-cryptography v1.7+. Disabled WEP decryption/encryption. (Dot11)") # noqa: E501 # Layers class PrismHeader(Packet): """ iwpriv wlan0 monitor 3 """ name = "Prism header" fields_desc = [LEIntField("msgcode", 68), LEIntField("len", 144), StrFixedLenField("dev", "", 16), LEIntField("hosttime_did", 0), LEShortField("hosttime_status", 0), LEShortField("hosttime_len", 0), LEIntField("hosttime", 0), LEIntField("mactime_did", 0), LEShortField("mactime_status", 0), LEShortField("mactime_len", 0), LEIntField("mactime", 0), LEIntField("channel_did", 0), LEShortField("channel_status", 0), LEShortField("channel_len", 0), LEIntField("channel", 0), LEIntField("rssi_did", 0), LEShortField("rssi_status", 0), LEShortField("rssi_len", 0), LEIntField("rssi", 0), LEIntField("sq_did", 0), LEShortField("sq_status", 0), LEShortField("sq_len", 0), LEIntField("sq", 0), LEIntField("signal_did", 0), LEShortField("signal_status", 0), LEShortField("signal_len", 0), LESignedIntField("signal", 0), LEIntField("noise_did", 0), LEShortField("noise_status", 0), LEShortField("noise_len", 0), LEIntField("noise", 0), LEIntField("rate_did", 0), LEShortField("rate_status", 0), LEShortField("rate_len", 0), LEIntField("rate", 0), LEIntField("istx_did", 0), LEShortField("istx_status", 0), LEShortField("istx_len", 0), LEIntField("istx", 0), LEIntField("frmlen_did", 0), LEShortField("frmlen_status", 0), LEShortField("frmlen_len", 0), LEIntField("frmlen", 0), ] def answers(self, other): if isinstance(other, PrismHeader): return self.payload.answers(other.payload) else: return self.payload.answers(other) # RadioTap class _RadiotapReversePadField(ReversePadField): def __init__(self, fld): # Quote from https://www.radiotap.org/: # ""Radiotap requires that all fields in the radiotap header are aligned to natural boundaries. # noqa: E501 # For radiotap, that means all 8-, 16-, 32-, and 64-bit fields must begin on 8-, 16-, 32-, and 64-bit boundaries, respectively."" # noqa: E501 if isinstance(fld, BitField): _align = int(math.ceil(fld.i2len(None, None))) else: _align = struct.calcsize(fld.fmt) ReversePadField.__init__( self, fld, _align, padwith=b"\x00" ) def _next_radiotap_extpm(pkt, lst, cur, s): """Generates the next RadioTapExtendedPresenceMask""" if cur is None or (cur.present and cur.present.Ext): st = len(lst) + (cur is not None) return lambda *args: RadioTapExtendedPresenceMask(*args, index=st) return None class RadioTapExtendedPresenceMask(Packet): """RadioTapExtendedPresenceMask should be instantiated by passing an `index=` kwarg, stating which place the item has in the list. Passing index will update the b[x] fields accordingly to the index. e.g. >>> a = RadioTapExtendedPresenceMask(present="b0+b12+b29+Ext") >>> b = RadioTapExtendedPresenceMask(index=1, present="b33+b45+b59+b62") >>> pkt = RadioTap(present="Ext", Ext=[a, b]) """ name = "RadioTap Extended presence mask" fields_desc = [FlagsField('present', None, -32, ["b%s" % i for i in range(0, 31)] + ["Ext"])] def __init__(self, _pkt=None, index=0, **kwargs): self._restart_indentation(index) Packet.__init__(self, _pkt, **kwargs) def _restart_indentation(self, index): st = index * 32 self.fields_desc[0].names = ["b%s" % (i + st) for i in range(0, 31)] + ["Ext"] # noqa: E501 def guess_payload_class(self, pay): return conf.padding_layer # RadioTap constants _rt_present = ['TSFT', 'Flags', 'Rate', 'Channel', 'FHSS', 'dBm_AntSignal', 'dBm_AntNoise', 'Lock_Quality', 'TX_Attenuation', 'dB_TX_Attenuation', 'dBm_TX_Power', 'Antenna', 'dB_AntSignal', 'dB_AntNoise', 'RXFlags', 'TXFlags', 'b17', 'b18', 'ChannelPlus', 'MCS', 'A_MPDU', 'VHT', 'timestamp', 'HE', 'HE_MU', 'HE_MU_other_user', 'zero_length_psdu', 'L_SIG', 'b28', 'RadiotapNS', 'VendorNS', 'Ext'] # Note: Inconsistencies with wireshark # Wireshark ignores the suggested fields, whereas we implement some of them # (some are well-used even though not accepted) # However, flags that conflicts with Wireshark are not and MUST NOT be # implemented -> b17, b18 _rt_flags = ['CFP', 'ShortPreamble', 'wep', 'fragment', 'FCS', 'pad', 'badFCS', 'ShortGI'] _rt_channelflags = ['res1', 'res2', 'res3', 'res4', 'Turbo', 'CCK', 'OFDM', '2GHz', '5GHz', 'Passive', 'Dynamic_CCK_OFDM', 'GFSK', 'GSM', 'StaticTurbo', '10MHz', '5MHz'] _rt_rxflags = ["res1", "BAD_PLCP", "res2"] _rt_txflags = ["TX_FAIL", "CTS", "RTS", "NOACK", "NOSEQ"] _rt_channelflags2 = ['res1', 'res2', 'res3', 'res4', 'Turbo', 'CCK', 'OFDM', '2GHz', '5GHz', 'Passive', 'Dynamic_CCK_OFDM', 'GFSK', 'GSM', 'StaticTurbo', '10MHz', '5MHz', '20MHz', '40MHz_ext_channel_above', '40MHz_ext_channel_below', 'res5', 'res6', 'res7', 'res8', 'res9'] _rt_knownmcs = ['MCS_bandwidth', 'MCS_index', 'guard_interval', 'HT_format', 'FEC_type', 'STBC_streams', 'Ness', 'Ness_MSB'] _rt_bandwidth = {0: "20MHz", 1: "40MHz", 2: "ht40Mhz-", 3: "ht40MHz+"} _rt_a_mpdu_flags = ['Report0Subframe', 'Is0Subframe', 'KnownLastSubframe', 'LastSubframe', 'CRCerror', 'EOFsubframe', 'KnownEOF', 'res1', 'res2', 'res3', 'res4', 'res5', 'res6', 'res7', 'res8'] _rt_vhtbandwidth = { 0: "20MHz", 1: "40MHz", 2: "40MHz", 3: "40MHz", 4: "80MHz", 5: "80MHz", 6: "80MHz", 7: "80MHz", 8: "80MHz", 9: "80MHz", 10: "80MHz", 11: "160MHz", 12: "160MHz", 13: "160MHz", 14: "160MHz", 15: "160MHz", 16: "160MHz", 17: "160MHz", 18: "160MHz", 19: "160MHz", 20: "160MHz", 21: "160MHz", 22: "160MHz", 23: "160MHz", 24: "160MHz", 25: "160MHz" } _rt_knownvht = ['STBC', 'TXOP_PS_NOT_ALLOWED', 'GuardInterval', 'SGINsysmDis', 'LDPCextraOFDM', 'Beamformed', 'Bandwidth', 'GroupID', 'PartialAID', 'res1', 'res2', 'res3', 'res4', 'res5', 'res6', 'res7'] _rt_presentvht = ['STBC', 'TXOP_PS_NOT_ALLOWED', 'GuardInterval', 'SGINsysmDis', 'LDPCextraOFDM', 'Beamformed', 'res1', 'res2'] _rt_hemuother_per_user_known = { 'user field position', 'STA-ID', 'NSTS', 'Tx Beamforming', 'Spatial Configuration', 'MCS', 'DCM', 'Coding', } class RadioTap(Packet): name = "RadioTap" deprecated_fields = { "Channel": ("ChannelFrequency", "2.4.3"), "ChannelFlags2": ("ChannelPlusFlags", "2.4.3"), "ChannelNumber": ("ChannelPlusNumber", "2.4.3"), } fields_desc = [ ByteField('version', 0), ByteField('pad', 0), LEShortField('len', None), FlagsField('present', None, -32, _rt_present), # noqa: E501 # Extended presence mask ConditionalField(PacketListField("Ext", [], next_cls_cb=_next_radiotap_extpm), lambda pkt: pkt.present and pkt.present.Ext), # noqa: E501 # RadioTap fields - each starts with a _RadiotapReversePadField # to handle padding # TSFT ConditionalField( _RadiotapReversePadField( LELongField("mac_timestamp", 0) ), lambda pkt: pkt.present and pkt.present.TSFT), # Flags ConditionalField( _RadiotapReversePadField( FlagsField("Flags", None, -8, _rt_flags) ), lambda pkt: pkt.present and pkt.present.Flags), # Rate ConditionalField( _RadiotapReversePadField( ByteField("Rate", 0) ), lambda pkt: pkt.present and pkt.present.Rate), # Channel ConditionalField( _RadiotapReversePadField( LEShortField("ChannelFrequency", 0) ), lambda pkt: pkt.present and pkt.present.Channel), ConditionalField( FlagsField("ChannelFlags", None, -16, _rt_channelflags), lambda pkt: pkt.present and pkt.present.Channel), # dBm_AntSignal ConditionalField( _RadiotapReversePadField( ScalingField("dBm_AntSignal", 0, offset=-256, unit="dBm", fmt="B") ), lambda pkt: pkt.present and pkt.present.dBm_AntSignal), # dBm_AntNoise ConditionalField( _RadiotapReversePadField( ScalingField("dBm_AntNoise", 0, offset=-256, unit="dBm", fmt="B") ), lambda pkt: pkt.present and pkt.present.dBm_AntNoise), # Lock_Quality ConditionalField( _RadiotapReversePadField( LEShortField("Lock_Quality", 0), ), lambda pkt: pkt.present and pkt.present.Lock_Quality), # Antenna ConditionalField( _RadiotapReversePadField( ByteField("Antenna", 0) ), lambda pkt: pkt.present and pkt.present.Antenna), # RX Flags ConditionalField( _RadiotapReversePadField( FlagsField("RXFlags", None, -16, _rt_rxflags) ), lambda pkt: pkt.present and pkt.present.RXFlags), # TX Flags ConditionalField( _RadiotapReversePadField( FlagsField("TXFlags", None, -16, _rt_txflags) ), lambda pkt: pkt.present and pkt.present.TXFlags), # ChannelPlus ConditionalField( _RadiotapReversePadField( FlagsField("ChannelPlusFlags", None, -32, _rt_channelflags2) ), lambda pkt: pkt.present and pkt.present.ChannelPlus), ConditionalField( LEShortField("ChannelPlusFrequency", 0), lambda pkt: pkt.present and pkt.present.ChannelPlus), ConditionalField( ByteField("ChannelPlusNumber", 0), lambda pkt: pkt.present and pkt.present.ChannelPlus), # MCS ConditionalField( _RadiotapReversePadField( FlagsField("knownMCS", None, -8, _rt_knownmcs) ), lambda pkt: pkt.present and pkt.present.MCS), ConditionalField( BitField("Ness_LSB", 0, 1), lambda pkt: pkt.present and pkt.present.MCS), ConditionalField( BitField("STBC_streams", 0, 2), lambda pkt: pkt.present and pkt.present.MCS), ConditionalField( BitEnumField("FEC_type", 0, 1, {0: "BCC", 1: "LDPC"}), lambda pkt: pkt.present and pkt.present.MCS), ConditionalField( BitEnumField("HT_format", 0, 1, {0: "mixed", 1: "greenfield"}), lambda pkt: pkt.present and pkt.present.MCS), ConditionalField( BitEnumField("guard_interval", 0, 1, {0: "Long_GI", 1: "Short_GI"}), # noqa: E501 lambda pkt: pkt.present and pkt.present.MCS), ConditionalField( BitEnumField("MCS_bandwidth", 0, 2, _rt_bandwidth), lambda pkt: pkt.present and pkt.present.MCS), ConditionalField( ByteField("MCS_index", 0), lambda pkt: pkt.present and pkt.present.MCS), # A_MPDU ConditionalField( _RadiotapReversePadField( LEIntField("A_MPDU_ref", 0) ), lambda pkt: pkt.present and pkt.present.A_MPDU), ConditionalField( FlagsField("A_MPDU_flags", None, -32, _rt_a_mpdu_flags), lambda pkt: pkt.present and pkt.present.A_MPDU), # VHT ConditionalField( _RadiotapReversePadField( FlagsField("KnownVHT", None, -16, _rt_knownvht) ), lambda pkt: pkt.present and pkt.present.VHT), ConditionalField( FlagsField("PresentVHT", None, -8, _rt_presentvht), lambda pkt: pkt.present and pkt.present.VHT), ConditionalField( ByteEnumField("VHT_bandwidth", 0, _rt_vhtbandwidth), lambda pkt: pkt.present and pkt.present.VHT), ConditionalField( StrFixedLenField("mcs_nss", 0, length=5), lambda pkt: pkt.present and pkt.present.VHT), ConditionalField( ByteField("GroupID", 0), lambda pkt: pkt.present and pkt.present.VHT), ConditionalField( ShortField("PartialAID", 0), lambda pkt: pkt.present and pkt.present.VHT), # timestamp ConditionalField( _RadiotapReversePadField( LELongField("timestamp", 0) ), lambda pkt: pkt.present and pkt.present.timestamp), ConditionalField( LEShortField("ts_accuracy", 0), lambda pkt: pkt.present and pkt.present.timestamp), ConditionalField( ByteField("ts_position", 0), lambda pkt: pkt.present and pkt.present.timestamp), ConditionalField( ByteField("ts_flags", 0), lambda pkt: pkt.present and pkt.present.timestamp), # HE - XXX not complete ConditionalField( _RadiotapReversePadField( ShortField("he_data1", 0) ), lambda pkt: pkt.present and pkt.present.HE), ConditionalField( ShortField("he_data2", 0), lambda pkt: pkt.present and pkt.present.HE), ConditionalField( ShortField("he_data3", 0), lambda pkt: pkt.present and pkt.present.HE), ConditionalField( ShortField("he_data4", 0), lambda pkt: pkt.present and pkt.present.HE), ConditionalField( ShortField("he_data5", 0), lambda pkt: pkt.present and pkt.present.HE), ConditionalField( ShortField("he_data6", 0), lambda pkt: pkt.present and pkt.present.HE), # HE_MU ConditionalField( _RadiotapReversePadField( LEShortField("hemu_flags1", 0) ), lambda pkt: pkt.present and pkt.present.HE_MU), ConditionalField( LEShortField("hemu_flags2", 0), lambda pkt: pkt.present and pkt.present.HE_MU), ConditionalField( FieldListField("RU_channel1", [], ByteField, count_from=lambda x: 4), lambda pkt: pkt.present and pkt.present.HE_MU), ConditionalField( FieldListField("RU_channel2", [], ByteField, count_from=lambda x: 4), lambda pkt: pkt.present and pkt.present.HE_MU), # HE_MU_other_user ConditionalField( _RadiotapReversePadField( LEShortField("hemuou_per_user_1", 0x7fff) ), lambda pkt: pkt.present and pkt.present.HE_MU_other_user), ConditionalField( LEShortField("hemuou_per_user_2", 0x003f), lambda pkt: pkt.present and pkt.present.HE_MU_other_user), ConditionalField( ByteField("hemuou_per_user_position", 0), lambda pkt: pkt.present and pkt.present.HE_MU_other_user), ConditionalField( FlagsField("hemuou_per_user_known", 0, -16, _rt_hemuother_per_user_known), lambda pkt: pkt.present and pkt.present.HE_MU_other_user), # L_SIG ConditionalField( _RadiotapReversePadField( FlagsField("lsig_data1", 0, -16, ["rate", "length"]) ), lambda pkt: pkt.present and pkt.present.L_SIG), ConditionalField( BitField("lsig_length", 0, 12), lambda pkt: pkt.present and pkt.present.L_SIG), ConditionalField( BitField("lsig_rate", 0, 4), lambda pkt: pkt.present and pkt.present.L_SIG), # Remaining StrLenField('notdecoded', "", length_from=lambda pkt: 0) ] def guess_payload_class(self, payload): if self.present and self.present.Flags and self.Flags.FCS: return Dot11FCS return Dot11 def post_dissect(self, s): length = max(self.len - len(self.original) + len(s), 0) self.notdecoded = s[:length] return s[length:] def post_build(self, p, pay): if self.len is None: p = p[:2] + struct.pack("!H", len(p))[::-1] + p[4:] return p + pay class Dot11(Packet): name = "802.11" fields_desc = [ BitField("subtype", 0, 4), BitEnumField("type", 0, 2, ["Management", "Control", "Data", "Reserved"]), BitField("proto", 0, 2), FlagsField("FCfield", 0, 8, ["to-DS", "from-DS", "MF", "retry", "pw-mgt", "MD", "protected", "order"]), ShortField("ID", 0), MACField("addr1", ETHER_ANY), ConditionalField( MACField("addr2", ETHER_ANY), lambda pkt: (pkt.type != 1 or pkt.subtype in [0x8, 0x9, 0xa, 0xb, 0xe, 0xf]), ), ConditionalField( MACField("addr3", ETHER_ANY), lambda pkt: pkt.type in [0, 2], ), ConditionalField(LEShortField("SC", 0), lambda pkt: pkt.type != 1), ConditionalField( MACField("addr4", ETHER_ANY), lambda pkt: (pkt.type == 2 and pkt.FCfield & 3 == 3), # from-DS+to-DS ) ] def mysummary(self): # Supports both Dot11 and Dot11FCS return self.sprintf("802.11 %%%s.type%% %%%s.subtype%% %%%s.addr2%% > %%%s.addr1%%" % ((self.__class__.__name__,) * 4)) # noqa: E501 def guess_payload_class(self, payload): if self.type == 0x02 and (0x08 <= self.subtype <= 0xF and self.subtype != 0xD): # noqa: E501 return Dot11QoS elif self.FCfield.protected: # When a frame is handled by encryption, the Protected Frame bit # (previously called WEP bit) is set to 1, and the Frame Body # begins with the appropriate cryptographic header. return Dot11Encrypted else: return Packet.guess_payload_class(self, payload) def answers(self, other): if isinstance(other, Dot11): if self.type == 0: # management if self.addr1.lower() != other.addr2.lower(): # check resp DA w/ req SA # noqa: E501 return 0 if (other.subtype, self.subtype) in [(0, 1), (2, 3), (4, 5)]: return 1 if self.subtype == other.subtype == 11: # auth return self.payload.answers(other.payload) elif self.type == 1: # control return 0 elif self.type == 2: # data return self.payload.answers(other.payload) elif self.type == 3: # reserved return 0 return 0 def unwep(self, key=None, warn=1): if self.FCfield & 0x40 == 0: if warn: warning("No WEP to remove") return if isinstance(self.payload.payload, NoPayload): if key or conf.wepkey: self.payload.decrypt(key) if isinstance(self.payload.payload, NoPayload): if warn: warning("Dot11 can't be decrypted. Check conf.wepkey.") return self.FCfield &= ~0x40 self.payload = self.payload.payload class Dot11FCS(Dot11): name = "802.11-FCS" match_subclass = True fields_desc = Dot11.fields_desc + [FCSField("fcs", None, fmt="= 3: length = orb(s[1]) if length > 0 and length <= 255: self.info = s[2:2 + length] return s def post_build(self, p, pay): if self.len is None: p = p[:1] + chb(len(p) - 2) + p[2:] return p + pay class RSNCipherSuite(Packet): name = "Cipher suite" fields_desc = [ X3BytesField("oui", 0x000fac), ByteEnumField("cipher", 0x04, { 0x00: "Use group cipher suite", 0x01: "WEP-40", 0x02: "TKIP", 0x03: "Reserved", 0x04: "CCMP", 0x05: "WEP-104" }) ] def extract_padding(self, s): return "", s class AKMSuite(Packet): name = "AKM suite" fields_desc = [ X3BytesField("oui", 0x000fac), ByteEnumField("suite", 0x01, { 0x00: "Reserved", 0x01: "IEEE 802.1X / PMKSA caching", 0x02: "PSK" }) ] def extract_padding(self, s): return "", s class PMKIDListPacket(Packet): name = "PMKIDs" fields_desc = [ LEFieldLenField("nb_pmkids", 0, count_of="pmk_id_list"), FieldListField( "pmkid_list", None, XStrFixedLenField("", "", length=16), count_from=lambda pkt: pkt.nb_pmkids ) ] def extract_padding(self, s): return "", s class Dot11EltRSN(Dot11Elt): name = "802.11 RSN information" match_subclass = True fields_desc = [ ByteField("ID", 48), ByteField("len", None), LEShortField("version", 1), PacketField("group_cipher_suite", RSNCipherSuite(), RSNCipherSuite), LEFieldLenField( "nb_pairwise_cipher_suites", 1, count_of="pairwise_cipher_suites" ), PacketListField( "pairwise_cipher_suites", [RSNCipherSuite()], RSNCipherSuite, count_from=lambda p: p.nb_pairwise_cipher_suites ), LEFieldLenField( "nb_akm_suites", 1, count_of="akm_suites" ), PacketListField( "akm_suites", [AKMSuite()], AKMSuite, count_from=lambda p: p.nb_akm_suites ), BitField("mfp_capable", 0, 1), BitField("mfp_required", 0, 1), BitField("gtksa_replay_counter", 0, 2), BitField("ptksa_replay_counter", 0, 2), BitField("no_pairwise", 0, 1), BitField("pre_auth", 0, 1), BitField("reserved", 0, 8), ConditionalField( PacketField("pmkids", None, PMKIDListPacket), lambda pkt: ( 0 if pkt.len is None else pkt.len - (12 + (pkt.nb_pairwise_cipher_suites * 4) + (pkt.nb_akm_suites * 4)) >= 18) ) ] class Dot11EltCountryConstraintTriplet(Packet): name = "802.11 Country Constraint Triplet" fields_desc = [ ByteField("first_channel_number", 1), ByteField("num_channels", 24), ByteField("mtp", 0) ] def extract_padding(self, s): return b"", s class Dot11EltCountry(Dot11Elt): name = "802.11 Country" match_subclass = True fields_desc = [ ByteField("ID", 7), ByteField("len", None), StrFixedLenField("country_string", b"\0\0\0", length=3), PacketListField( "descriptors", [], Dot11EltCountryConstraintTriplet, length_from=lambda pkt: ( pkt.len - 3 - (pkt.len % 3) ) ), ConditionalField( ByteField("pad", 0), lambda pkt: (len(pkt.descriptors) + 1) % 2 ) ] class Dot11EltMicrosoftWPA(Dot11Elt): name = "802.11 Microsoft WPA" match_subclass = True fields_desc = [ ByteField("ID", 221), ByteField("len", None), X3BytesField("oui", 0x0050f2), XByteField("type", 0x01), LEShortField("version", 1), PacketField("group_cipher_suite", RSNCipherSuite(), RSNCipherSuite), LEFieldLenField( "nb_pairwise_cipher_suites", 1, count_of="pairwise_cipher_suites" ), PacketListField( "pairwise_cipher_suites", RSNCipherSuite(), RSNCipherSuite, count_from=lambda p: p.nb_pairwise_cipher_suites ), LEFieldLenField( "nb_akm_suites", 1, count_of="akm_suites" ), PacketListField( "akm_suites", AKMSuite(), AKMSuite, count_from=lambda p: p.nb_akm_suites ) ] class Dot11EltRates(Dot11Elt): name = "802.11 Rates" match_subclass = True fields_desc = [ ByteField("ID", 1), ByteField("len", None), FieldListField( "rates", [], XByteField("", 0), count_from=lambda p: p.len ) ] class Dot11EltVendorSpecific(Dot11Elt): name = "802.11 Vendor Specific" match_subclass = True fields_desc = [ ByteField("ID", 221), ByteField("len", None), X3BytesField("oui", 0x000000), StrLenField("info", "", length_from=lambda x: x.len - 3) ] class Dot11ATIM(Packet): name = "802.11 ATIM" class Dot11Disas(Packet): name = "802.11 Disassociation" fields_desc = [LEShortEnumField("reason", 1, reason_code)] class Dot11AssoReq(Packet): name = "802.11 Association Request" fields_desc = [FlagsField("cap", 0, 16, capability_list), LEShortField("listen_interval", 0x00c8)] class Dot11AssoResp(Packet): name = "802.11 Association Response" fields_desc = [FlagsField("cap", 0, 16, capability_list), LEShortField("status", 0), LEShortField("AID", 0)] class Dot11ReassoReq(Packet): name = "802.11 Reassociation Request" fields_desc = [FlagsField("cap", 0, 16, capability_list), LEShortField("listen_interval", 0x00c8), MACField("current_AP", ETHER_ANY)] class Dot11ReassoResp(Dot11AssoResp): name = "802.11 Reassociation Response" class Dot11ProbeReq(Packet): name = "802.11 Probe Request" class Dot11ProbeResp(_Dot11NetStats): name = "802.11 Probe Response" class Dot11Auth(Packet): name = "802.11 Authentication" fields_desc = [LEShortEnumField("algo", 0, ["open", "sharedkey"]), LEShortField("seqnum", 0), LEShortEnumField("status", 0, status_code)] def answers(self, other): if self.seqnum == other.seqnum + 1: return 1 return 0 class Dot11Deauth(Packet): name = "802.11 Deauthentication" fields_desc = [LEShortEnumField("reason", 1, reason_code)] class Dot11Encrypted(Packet): name = "802.11 Encrypted (unknown algorithm)" fields_desc = [StrField("data", None)] @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): # Extracted from # https://github.com/wireshark/wireshark/blob/master/epan/dissectors/packet-ieee80211.c # noqa: E501 KEY_EXTIV = 0x20 EXTIV_LEN = 8 if _pkt and len(_pkt) >= 3: if (orb(_pkt[3]) & KEY_EXTIV) and (len(_pkt) >= EXTIV_LEN): if orb(_pkt[1]) == ((orb(_pkt[0]) | 0x20) & 0x7f): # IS_TKIP return Dot11TKIP elif orb(_pkt[2]) == 0: # IS_CCMP return Dot11CCMP else: # Unknown encryption algorithm return Dot11Encrypted else: return Dot11WEP return conf.raw_layer class Dot11WEP(Dot11Encrypted): name = "802.11 WEP packet" fields_desc = [StrFixedLenField("iv", b"\0\0\0", 3), ByteField("keyid", 0), StrField("wepdata", None, remain=4), IntField("icv", None)] def decrypt(self, key=None): if key is None: key = conf.wepkey if key and conf.crypto_valid: d = Cipher( algorithms.ARC4(self.iv + key.encode("utf8")), None, default_backend(), ).decryptor() self.add_payload(LLC(d.update(self.wepdata) + d.finalize())) def post_dissect(self, s): self.decrypt() def build_payload(self): if self.wepdata is None: return Packet.build_payload(self) return b"" @crypto_validator def encrypt(self, p, pay, key=None): if key is None: key = conf.wepkey if key: if self.icv is None: pay += struct.pack(" LE = reversed order BitField("res", 0, 5), # # ext_iv - 4 bytes ConditionalField(ByteField("TSC2", 0), lambda pkt: pkt.ext_iv), ConditionalField(ByteField("TSC3", 0), lambda pkt: pkt.ext_iv), ConditionalField(ByteField("TSC4", 0), lambda pkt: pkt.ext_iv), ConditionalField(ByteField("TSC5", 0), lambda pkt: pkt.ext_iv), # data StrField("data", None), ] class Dot11CCMP(Dot11Encrypted): name = "802.11 TKIP packet" fields_desc = [ # iv - 8 bytes ByteField("PN0", 0), ByteField("PN1", 0), ByteField("res0", 0), BitField("key_id", 0, 2), # BitField("ext_iv", 0, 1), # => LE = reversed order BitField("res1", 0, 5), # ByteField("PN2", 0), ByteField("PN3", 0), ByteField("PN4", 0), ByteField("PN5", 0), # data StrField("data", None), ] class Dot11Ack(Packet): name = "802.11 Ack packet" bind_top_down(RadioTap, Dot11FCS, present=2, Flags=16) bind_layers(PrismHeader, Dot11,) bind_layers(Dot11, LLC, type=2) bind_layers(Dot11QoS, LLC,) bind_layers(Dot11, Dot11AssoReq, subtype=0, type=0) bind_layers(Dot11, Dot11AssoResp, subtype=1, type=0) bind_layers(Dot11, Dot11ReassoReq, subtype=2, type=0) bind_layers(Dot11, Dot11ReassoResp, subtype=3, type=0) bind_layers(Dot11, Dot11ProbeReq, subtype=4, type=0) bind_layers(Dot11, Dot11ProbeResp, subtype=5, type=0) bind_layers(Dot11, Dot11Beacon, subtype=8, type=0) bind_layers(Dot11, Dot11ATIM, subtype=9, type=0) bind_layers(Dot11, Dot11Disas, subtype=10, type=0) bind_layers(Dot11, Dot11Auth, subtype=11, type=0) bind_layers(Dot11, Dot11Deauth, subtype=12, type=0) bind_layers(Dot11, Dot11Ack, subtype=13, type=1) bind_layers(Dot11Beacon, Dot11Elt,) bind_layers(Dot11AssoReq, Dot11Elt,) bind_layers(Dot11AssoResp, Dot11Elt,) bind_layers(Dot11ReassoReq, Dot11Elt,) bind_layers(Dot11ReassoResp, Dot11Elt,) bind_layers(Dot11ProbeReq, Dot11Elt,) bind_layers(Dot11ProbeResp, Dot11Elt,) bind_layers(Dot11Auth, Dot11Elt,) bind_layers(Dot11Elt, Dot11Elt,) bind_layers(Dot11TKIP, conf.raw_layer) bind_layers(Dot11CCMP, conf.raw_layer) conf.l2types.register(DLT_IEEE802_11, Dot11) conf.l2types.register_num2layer(801, Dot11) conf.l2types.register(DLT_PRISM_HEADER, PrismHeader) conf.l2types.register_num2layer(802, PrismHeader) conf.l2types.register(DLT_IEEE802_11_RADIO, RadioTap) conf.l2types.register_num2layer(803, RadioTap) class WiFi_am(AnsweringMachine): """Before using this, initialize "iffrom" and "ifto" interfaces: iwconfig iffrom mode monitor iwpriv orig_ifto hostapd 1 ifconfig ifto up note: if ifto=wlan0ap then orig_ifto=wlan0 note: ifto and iffrom must be set on the same channel ex: ifconfig eth1 up iwconfig eth1 mode monitor iwconfig eth1 channel 11 iwpriv wlan0 hostapd 1 ifconfig wlan0ap up iwconfig wlan0 channel 11 iwconfig wlan0 essid dontexist iwconfig wlan0 mode managed """ function_name = "airpwn" filter = None def parse_options(self, iffrom=conf.iface, ifto=conf.iface, replace="", pattern="", ignorepattern=""): self.iffrom = iffrom self.ifto = ifto self.ptrn = re.compile(pattern.encode()) self.iptrn = re.compile(ignorepattern.encode()) self.replace = replace def is_request(self, pkt): if not isinstance(pkt, Dot11): return 0 if not pkt.FCfield & 1: return 0 if not pkt.haslayer(TCP): return 0 tcp = pkt.getlayer(TCP) pay = raw(tcp.payload) if not self.ptrn.match(pay): return 0 if self.iptrn.match(pay) is True: return 0 return True def make_reply(self, p): ip = p.getlayer(IP) tcp = p.getlayer(TCP) pay = raw(tcp.payload) del(p.payload.payload.payload) p.FCfield = "from-DS" p.addr1, p.addr2 = p.addr2, p.addr1 p /= IP(src=ip.dst, dst=ip.src) p /= TCP(sport=tcp.dport, dport=tcp.sport, seq=tcp.ack, ack=tcp.seq + len(pay), flags="PA") q = p.copy() p /= self.replace q.ID += 1 q.getlayer(TCP).flags = "RA" q.getlayer(TCP).seq += len(self.replace) return [p, q] def print_reply(self, query, *reply): p = reply[0][0] print(p.sprintf("Sent %IP.src%:%IP.sport% > %IP.dst%:%TCP.dport%")) def send_reply(self, reply): sendp(reply, iface=self.ifto, **self.optsend) def sniff(self): sniff(iface=self.iffrom, **self.optsniff) conf.stats_dot11_protocols += [Dot11WEP, Dot11Beacon, ] class Dot11PacketList(PacketList): def __init__(self, res=None, name="Dot11List", stats=None): if stats is None: stats = conf.stats_dot11_protocols PacketList.__init__(self, res, name, stats) def toEthernet(self): data = [x[Dot11] for x in self.res if Dot11 in x and x.type == 2] r2 = [] for p in data: q = p.copy() q.unwep() r2.append(Ether() / q.payload.payload.payload) # Dot11/LLC/SNAP/IP return PacketList(r2, name="Ether from %s" % self.listname)