Jump to content
Fi8sVrs

KRACK Detector - Detect and prevent KRACK attacks in your network

Recommended Posts

  • Active Members

15dkat.jpg

 

KRACK Detector is a Python script to detect possible KRACK attacks against client devices on your network. The script is meant to be run on the Access Point rather than the client devices. It listens on the Wi-Fi interface and waits for duplicate message 3 of the 4-way handshake. It then disconnects the suspected device, preventing it from sending any further sensitive data to the Access Point.

KRACK Detector currently supports Linux Access Points with hostapd. It uses Python 2 for compatibility with older operating systems. No external Python packages are required.

 

Usage:

Run as root and pass the Wi-Fi interface as a single argument. It is important to use the actual Wi-Fi interface and not any bridge interface it connects to.

python krack_detect.py wlan0

If you do not wish to disconnect suspected devices, use the -n flag

python krack_detect.py -n wlan0

 

Known Issues

Message 3 of the 4-way handshake might be retransmitted even if no attack is perfomed. In such a case the client device will be disconnected from the Wi-Fi network. Some client devices will take some time to re-authenticate themselves, losing the Wi-Fi connection for a few seconds.

 

Download: krackdetector-master.zip

Mirror:

krack_detect.py

Spoiler

#!/usr/bin/env python
import os
import sys
import socket
import struct
import ctypes
import subprocess
from optparse import OptionParser

usage = "usage: %prog INTERFACE"

# Capture only EAPOL packets (Ethernet type 0x888e)
#     ldh   [12]
#     jeq   #0x800  jt 2    jf 3
#     ret   #0x0040000
#     ret   #0
eapol_filter = \
    [struct.pack('HBBI', 0x28, 0, 0, 0x0000000c),
     struct.pack('HBBI', 0x15, 0, 1, 0x0000888e),
     struct.pack('HBBI', 0x06, 0, 0, 0x00040000),
     struct.pack('HBBI', 0x06, 0, 0, 0x00000000)]

# Defined in asm-generic/socket.h
SO_ATTACH_FILTER = 26

# Defined in linux/if_ether.h
ETH_P_ALL = 0x0003
ETH_P_PAE = 0x888e

# Defined in eapol_common.h in wpa_supplicant
IEEE802_1X_TYPE_EAPOL_KEY = 3
# Defined in wpa_common.h in wpa_supplicant
WPA_KEY_INFO_KEY_TYPE = 1 << 3
WPA_KEY_INFO_INSTALL  = 1 << 6
WPA_KEY_INFO_ACK      = 1 << 7
WPA_KEY_INFO_MIC      = 1 << 8

ETHERNET_STRUCT = '>6s6sH'
ETHERNET_STRUCT_SIZE = struct.calcsize(ETHERNET_STRUCT)
IEEE802_1X_STRUCT = '>BBH'
IEEE802_1X_STRUCT_SIZE = struct.calcsize(IEEE802_1X_STRUCT)
WPA_KEY_STRUCT = '>BHHQ32s'
WPA_KEY_STRUCT_SIZE = struct.calcsize(WPA_KEY_STRUCT)

EAPOL_PKT_MIN_SIZE = ETHERNET_STRUCT_SIZE + \
                     IEEE802_1X_STRUCT_SIZE + \
                     WPA_KEY_STRUCT_SIZE

class KRACKDetector(object):
    def __init__(self, iface, dry_run):
        self.iface = iface
        self.dry_run = dry_run
        self.nonces = []
        self.sock = self._create_socket()

    def _create_socket(self):
        sock = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, 0)
        bpf = ctypes.create_string_buffer(''.join(eapol_filter))
        prog = struct.pack('HL', len(eapol_filter), ctypes.addressof(bpf))
        sock.setsockopt(socket.SOL_SOCKET, SO_ATTACH_FILTER, prog)
        sock.bind((self.iface, ETH_P_ALL))
        return sock

    def _disassociate(self, mac):
        args = ['hostapd_cli', '-i', self.iface, 'disassociate', mac]
        try:
            output = subprocess.check_output(args)
            if "OK" not in output:
                print "hostapd_cli failed: %s" % output
            else:
                print '%s disassociated' % mac
        except subprocess.CalledProcessError:
            print "hostapd_cli failed"

    def _deauthenticate(self, mac):
        args = ['hostapd_cli', '-i', self.iface, 'deauthenticate', mac]
        try:
            output = subprocess.check_output(args)
            if "OK" not in output:
                print "hostapd_cli failed: %s" % output
            else:
                print '%s deauthenticated' % mac
        except subprocess.CalledProcessError:
            print "hostapd_cli failed"

    def _process_eapol_packet(self, pkt):
        if len(pkt) < EAPOL_PKT_MIN_SIZE:
            return

        # Extract Ethernet layer
        eth = pkt[:ETHERNET_STRUCT_SIZE]
        pkt = pkt[ETHERNET_STRUCT_SIZE:]
        dst_mac, src_mac, eth_type = struct.unpack('>6s6sH', eth)
        if eth_type != ETH_P_PAE:
            return

        # Extract IEEE802.1x header
        ieee802_1x = pkt[:IEEE802_1X_STRUCT_SIZE]
        pkt = pkt[IEEE802_1X_STRUCT_SIZE:]
        ver, pkt_type, pkt_len = struct.unpack('>BBH', ieee802_1x)
        if pkt_type != IEEE802_1X_TYPE_EAPOL_KEY:
            return

        # Extract WPA key data
        wpa_key = pkt[:WPA_KEY_STRUCT_SIZE]
        key_type, key_info, key_len, key_replay, key_nonce = \
            struct.unpack('>BHHQ32s', wpa_key)

        if key_info & WPA_KEY_INFO_KEY_TYPE:
            if key_info & WPA_KEY_INFO_INSTALL and \
               key_info & WPA_KEY_INFO_ACK and \
               key_info & WPA_KEY_INFO_MIC:
                sta = ':'.join(map(lambda x: x.encode('hex'), dst_mac))
                print 'Detected packet 3/4 to %s, nonce %s' % (sta, key_nonce.encode('hex'))
                if key_nonce in self.nonces:
                    print 'Detected duplicate packet 3!'
                    if not self.dry_run:
                        self._disassociate(sta)
                        self._deauthenticate(sta)
                else:
                    self.nonces.append(key_nonce)

    def detect(self):
        while True:
            pkt, addr = self.sock.recvfrom(1024)
            self._process_eapol_packet(pkt)

def main():
    parser = OptionParser(usage)
    parser.add_option("-n", "--dry-run", dest="dry_run",
                      action="store_true", help="Do not disconnect suspected devices")
    (options, args) = parser.parse_args()

    if len(args) != 1:
        parser.error("Incorrect number of arguments")

    if os.getuid() != 0:
        print "Please run as root"
        sys.exit(1)

    iface = args[0]
    dry_run = options.dry_run

    KRACKDetector(iface, dry_run).detect()

if __name__ == "__main__":
main()

 

 

Sources:

 

Edited by Fi8sVrs
[img]
  • Upvote 3
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.



×
×
  • Create New...