Browse Source

Detect unsupport use of hardware decryption

Mathy Vanhoef 2 years ago
parent
commit
fda5b1cade
2 changed files with 42 additions and 5 deletions
  1. 39 4
      krackattack/krack-test-client.py
  2. 3 1
      krackattack/libwifi/wifi.py

+ 39 - 4
krackattack/krack-test-client.py

@@ -14,6 +14,8 @@ from libwifi import *
 import sys, socket, struct, time, subprocess, atexit, select, os.path
 from wpaspy import Ctrl
 
+warned_hardware_decryption = False
+
 # Concrete TODOs:
 # 0. Option to send plaintext retransmissions of message 3 (this is now easy to do)
 # 1. More reliable group key high RSC installation test in the 4-way HS (see below)
@@ -319,15 +321,46 @@ class KRAckAttackClient():
 		decap = header/plaintext[SNAP].payload
 		self.process_eth_rx(decap)
 
+	def check_hardware_encryption(self, p):
+		global warned_hardware_decryption
+
+		# Check for hardware decryption usage that prevents detection of IV reuse. The utility
+		# function get_ccmp_payload was made so that it returns the payload after an extended IV.
+		# Basically this script works if the plaintext starts at `get_ccmp_payload`, but if the
+		# plaintext starts at other locations, it's unknown what the layout of the frame is. In
+		# that case we should kill the script.
+		payload = get_ccmp_payload(p)
+		if dot11_is_encrypted_data(p) and payload != None and b"\xAA\xAA\x03\x00\x00\x00" in raw(p):
+			if payload.startswith(b"\xAA\xAA\x03\x00\x00\x00"):
+				if not warned_hardware_decryption:
+					# - With the ath9k_htc it was not possible to detect the usage of an all-zero key
+					#   when using hardware decryption. It seems as if the hardware tries decryption,
+					#   thereby scrambling the contect of the frame, and then sees that decryption failed.
+					#   This means userspace doesn't receive the original frame, but a frame where the
+					#   (encrypted) data is scrambled.
+					# - The ath9k_htc doesn't reset the (group) IV when installing a new key. this means
+					#   it will always think a device reinstalls the group key, because the ath9k_htc
+					#   will always use an incremented IV when though we reset the group key. We also
+					#   can't seem to detect this in userspace because the hardware may override the IV?
+					log(WARNING, f"Hardware decryption detected! Attemping to still detect IV reuse, but this is unreliable.")
+					log(ERROR, f"!!! Ideally you disable hardware decryption or use a different network card !!!")
+					log(WARNING, f"E.g., detecting all-zero key use may currently be unreliable, and with some network")
+					log(WARNING, f"      cards key reinstallations cannot be detected at all currently...")
+					warned_hardware_decryption = True
+			else:
+				# Whether an IV is included may depend on the direction of the packet. For instance,
+				# with the "Intel Tiger Lake PCH CNVi WiFi" there's no IV included in transmitted frames,
+				# but the IV *is* included in received frames.
+				log(ERROR, "Hardware decryption seems to be dropping the IV, meaning we cannot detect key reinstallations.")
+				log(ERROR, "Try to disable hardware decryption, use a different network card, or report this as a bug.")
+				log(WARNING, f"Frame causing the issue: {repr(p)} with raw data being {p}")
+				quit(1)
+
 	def handle_mon_rx(self):
 		p = self.sock_mon.recv()
 		if p == None: return
 		if p.type == 1: return
 
-		# Note: we cannot verify that the NIC is indeed reusing IVs when sending the broadcast
-		# ARP requests, because it may override them in the firmware/hardware (some Atheros
-		# Wi-Fi NICs do no properly reset the Tx group key IV when using hardware encryption).
-
 		# The first bit in FCfield is set if the frames is "to-DS"
 		clientmac, apmac = (p.addr1, p.addr2) if (p.FCfield & 2) != 0 else (p.addr2, p.addr1)
 		if apmac != self.apmac: return None
@@ -338,6 +371,8 @@ class KRAckAttackClient():
 
 		# Inspect encrypt frames for IV reuse & handle replayed frames rejected by the kernel
 		elif p.addr1 == self.apmac and dot11_is_encrypted_data(p):
+			self.check_hardware_encryption(p)
+
 			if not clientmac in self.clients:
 				self.clients[clientmac] = ClientState(clientmac, options=options)
 			client = self.clients[clientmac]

+ 3 - 1
krackattack/libwifi/wifi.py

@@ -371,8 +371,10 @@ def get_ccmp_payload(p):
 		return p[Dot11TKIP].data
 	elif Dot11Encrypted in p:
 		return p[Dot11Encrypted].data
-	else:
+	elif Raw in p:
 		return p[Raw].load
+	else:
+		return None
 
 class IvInfo():
 	def __init__(self, p):