mirror of
https://github.com/vanhoefm/fragattacks.git
synced 2024-11-25 00:38:24 -05:00
7f93c1cec7
This will make it easier for users to clone the repository and will assure that they always use the correct version of libwifi.
170 lines
4.2 KiB
Python
170 lines
4.2 KiB
Python
#!/usr/bin/env python3
|
|
import struct, binascii
|
|
from .wifi import *
|
|
#from binascii import a2b_hex
|
|
#from struct import unpack,pack
|
|
|
|
from Crypto.Cipher import AES, ARC4
|
|
from scapy.layers.dot11 import Dot11, Dot11CCMP, Dot11QoS
|
|
|
|
import zlib
|
|
|
|
def pn2bytes(pn):
|
|
pn_bytes = [0] * 6
|
|
for i in range(6):
|
|
pn_bytes[i] = pn & 0xFF
|
|
pn >>= 8
|
|
return pn_bytes
|
|
|
|
def pn2bin(pn):
|
|
return struct.pack(">Q", pn)[2:]
|
|
|
|
def dot11ccmp_get_pn(p):
|
|
pn = p.PN5
|
|
pn = (pn << 8) | p.PN4
|
|
pn = (pn << 8) | p.PN3
|
|
pn = (pn << 8) | p.PN2
|
|
pn = (pn << 8) | p.PN1
|
|
pn = (pn << 8) | p.PN0
|
|
return pn
|
|
|
|
def ccmp_get_nonce(priority, addr, pn):
|
|
return struct.pack("B", priority) + addr2bin(addr) + pn2bin(pn)
|
|
|
|
def ccmp_get_aad(p, amsdu_spp=False):
|
|
# FC field with masked values
|
|
fc = raw(p)[:2]
|
|
fc = struct.pack("<BB", fc[0] & 0x8f, fc[1] & 0xc7)
|
|
|
|
# Sequence number is masked, but fragment number is included
|
|
sc = struct.pack("<H", p.SC & 0xf)
|
|
|
|
addr1 = addr2bin(p.addr1)
|
|
addr2 = addr2bin(p.addr2)
|
|
addr3 = addr2bin(p.addr3)
|
|
aad = fc + addr1 + addr2 + addr3 + sc
|
|
if Dot11QoS in p:
|
|
if not amsdu_spp:
|
|
# Everything except the TID is masked
|
|
aad += struct.pack("<H", p[Dot11QoS].TID)
|
|
else:
|
|
# TODO: Mask unrelated fields
|
|
aad += raw(p[Dot11QoS])[:2]
|
|
|
|
return aad
|
|
|
|
def Raw(x):
|
|
return x
|
|
|
|
def encrypt_ccmp(p, tk, pn, keyid=0, amsdu_spp=False):
|
|
"""Takes a plaintext Dot11 frame, encrypts it, and adds all the necessairy headers"""
|
|
|
|
# Update the FC field
|
|
p = p.copy()
|
|
p.FCfield |= Dot11(FCfield="protected").FCfield
|
|
if Dot11QoS in p:
|
|
payload = raw(p[Dot11QoS].payload)
|
|
p[Dot11QoS].remove_payload()
|
|
# Explicitly set TID so we can assume it's an integer
|
|
if p[Dot11QoS].TID == None:
|
|
p[Dot11QoS].TID = 0
|
|
priority = p[Dot11QoS].TID
|
|
else:
|
|
payload = raw(p.payload)
|
|
p.remove_payload()
|
|
priority = 0
|
|
|
|
# Add the CCMP header. res0 and res1 are by default set to zero.
|
|
newp = p/Dot11CCMP()
|
|
pn_bytes = pn2bytes(pn)
|
|
newp.PN0, newp.PN1, newp.PN2, newp.PN3, newp.PN4, newp.PN5 = pn_bytes
|
|
newp.key_id = keyid
|
|
newp.ext_iv = 1
|
|
|
|
# Generate the CCMP Header and AAD for encryption.
|
|
ccm_nonce = ccmp_get_nonce(priority, newp.addr2, pn)
|
|
ccm_aad = ccmp_get_aad(newp, amsdu_spp)
|
|
#print("CCM Nonce:", ccm_nonce.hex())
|
|
#print("CCM aad :", ccm_aad.hex())
|
|
|
|
# Encrypt the plaintext using AES in CCM Mode.
|
|
#print("Payload:", payload.hex())
|
|
cipher = AES.new(tk, AES.MODE_CCM, ccm_nonce, mac_len=8)
|
|
cipher.update(ccm_aad)
|
|
ciphertext = cipher.encrypt(payload)
|
|
digest = cipher.digest()
|
|
newp = newp/Raw(ciphertext)
|
|
newp = newp/Raw(digest)
|
|
|
|
#print("Ciphertext:", ciphertext.hex())
|
|
#print(repr(newp))
|
|
#print(raw(newp).hex())
|
|
|
|
return newp
|
|
|
|
def decrypt_ccmp(p, tk, verify=True):
|
|
"""Takes a Dot11CCMP frame and decrypts it"""
|
|
|
|
p = p.copy()
|
|
|
|
# Get used CCMP parameters
|
|
keyid = p.key_id
|
|
priority = dot11_get_priority(p)
|
|
pn = dot11ccmp_get_pn(p)
|
|
|
|
# TODO: Mask flags in p.FCfield that are not part of the AAD
|
|
fc = p.FCfield
|
|
payload = get_ccmp_payload(p)
|
|
|
|
if Dot11QoS in p:
|
|
p[Dot11QoS].remove_payload()
|
|
else:
|
|
p.remove_payload()
|
|
|
|
# Prepare for CCMP decryption
|
|
ccm_nonce = ccmp_get_nonce(priority, p.addr2, pn)
|
|
ccm_aad = ccmp_get_aad(p)
|
|
|
|
# Decrypt using AES in CCM Mode.
|
|
cipher = AES.new(tk, AES.MODE_CCM, ccm_nonce, mac_len=8)
|
|
cipher.update(ccm_aad)
|
|
plaintext = cipher.decrypt(payload[:-8])
|
|
|
|
try:
|
|
if verify:
|
|
cipher.verify(payload[-8:])
|
|
except ValueError:
|
|
return None
|
|
|
|
return p/LLC(plaintext)
|
|
|
|
def encrypt_wep(p, key, pn, keyid=0):
|
|
"""Takes a plaintext Dot11 frame, encrypts it, and adds all the necessairy headers"""
|
|
|
|
# Update the FC field --- XXX share this with encrypt_ccmp
|
|
p = p.copy()
|
|
p.FCfield |= Dot11(FCfield="protected").FCfield
|
|
if Dot11QoS in p:
|
|
payload = raw(p[Dot11QoS].payload)
|
|
p[Dot11QoS].remove_payload()
|
|
# Explicitly set TID so we can assume it's an integer
|
|
if p[Dot11QoS].TID == None:
|
|
p[Dot11QoS].TID = 0
|
|
priority = p[Dot11QoS].TID
|
|
else:
|
|
payload = raw(p.payload)
|
|
p.remove_payload()
|
|
priority = 0
|
|
|
|
# Add the WEP ICV which will be encrypted
|
|
payload += struct.pack("<I", zlib.crc32(payload) & 0xffffffff)
|
|
iv = struct.pack(">I", pn)[1:]
|
|
cipher = ARC4.new(iv + key)
|
|
ciphertext = cipher.encrypt(payload)
|
|
|
|
# Construct packet ourselves to avoid scapy bugs
|
|
newp = p/iv/struct.pack("<B", keyid)/ciphertext
|
|
|
|
return newp
|
|
|