Site icon WiFi Ninjas – Podcasts & Blogs

WN Blog 012 – Can You Crack 802.1X WPA2-Enterprise Wireless Data?

One of our clients has recently approached me with concerns about their new WiFi network that we were planning to put in. They were coming from a wired-only environment and were not sure if introducing EAP-TLS based corporate wireless was a good and safe idea. Additionally, while preparing for my CWAP exam I heard in one of the course videos that “you can’t decrypt 802.1x EAP, as there is no known key that we can enter to start 4-way handshake”. But is it really the case?

I will answer this question by first touching on the 802.1X EAP authentication framework recap that will help us understand conditions that must be met, and steps taken to decrypt WPA2-Enterprise data.

All modern EAP variations are using strong CCMP encryption. Instead of attacking it, we will focus on capturing RADIUS packets on the wire and extract a PMK from this transaction. We will then capture 4-way handshake to get Anonce and Snonce and use it together with PMK, Supplicant MAC and Authenticator MAC to derive PTK (Wireshark can do it for us) used to decrypt our wireless session.

802.1X EAP Recap

IEEE 802.1X is a standard for Network Access Control. It provides authentication (making sure that something is what it claims to be) mechanism to devices wishing to connect to a LAN or WLAN. 802.1X defines the encapsulation of the Extensive Authentication Protocol (EAP) over IEEE 802, that is known as EAP over LAN (EAPOL). 802.1X defines 3 roles: Supplicant (client), Authenticator/NAS (AP) and Authentication Server (RADIUS). Successful EAP transaction starts a process of 802.11 Security Keys Generation, that I tried to visualise in a diagram below, together with 802.11 Open Authentication, 802.11 Association, Tunnelled EAP Authentication and a 4-Way Handshake, collectively being part of an 802.1X standard.                   

802.1X EAP and 802.11 Security Keys Generation Process

Conditions

The following conditions must be met to decrypt 802.1X EAP encrypted captures:

1. RADIUS key must be known

2. Wireless capture of the session that we want to decrypt must be taken

3. Wired capture of RADIUS authentication must be taken

Lab Environment

Here is the lab setup and capture locations (both wired and wireless):

Lab Network Diagram

Steps

Here is a high-level summary of what we need to do to decrypt our WPA2-Enterprise wireless session:

Note: PTK is valid only for the duration of a single session. Session timeout, new association or re-association (roaming) would require to derive new PTK!

Assuming all conditions are met, let’s crack on!

1. Obtain RADIUS key

RADIUS key configured in Cisco ISE

2. Start capturing wireless traffic of interest

3. Start capturing wired RADIUS traffic between the Authentication Server and the Authenticator

4. Connect wireless device to the EAP SSID

5. Obtain Authenticator from the last Access-Request packet in wired RADIUS capture

Authenticator value from Access-Request wired RADIUS capture

6. Obtain MS-MPPE-Recv-Key from the Access-Accept packet in wired RADIUS capture

MS-MPPE-Recv-Key value from Access-Accept wired RADIUS capture

7. Compile PMKextract code, that we will use to extract PMK

#include <stdio.h>
#include <stdlib.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>

#define _WIN32_WINNT 0x0400
#include <wincrypt.h>

typedef unsigned int u32;
typedef unsigned char u8;

//
// This debugging function can be found in wpa_debug.c from the hostap package
//
//extern void wpa_hexdump_key(int level, const char *title, const u8 *buf, size_t len);

#define os_malloc(s) malloc((s))
#define os_free(p) free((p))
#define os_memcpy(d, s, n) memcpy((d), (s), (n))
#define MD5_MAC_LEN 16

static void cryptoapi_report_error(const char *msg)
{
 char *s, *pos;
 DWORD err = GetLastError();

 if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
     FORMAT_MESSAGE_FROM_SYSTEM,
     NULL, err, 0, (LPTSTR) &s, 0, NULL) == 0) {
   printf("CryptoAPI: %s: %d", msg, (int) err);
 }

 pos = s;
 while (*pos) {
  if (*pos == '\n' || *pos == '\r') {
   *pos = '\0';
   break;
  }
  pos++;
 }

 printf("CryptoAPI: %s: %d: (%s)", msg, (int) err, s);
 LocalFree(s);
}

int cryptoapi_hash_vector(ALG_ID alg, size_t hash_len, size_t num_elem,
     const u8 *addr[], const size_t *len, u8 *mac)
{
 HCRYPTPROV prov;
 HCRYPTHASH hash;
 size_t i;
 DWORD hlen;
 int ret = 0;

 if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, 0)) {
  cryptoapi_report_error("CryptAcquireContext");
  return -1;
 }

 if (!CryptCreateHash(prov, alg, 0, 0, &hash)) {
  cryptoapi_report_error("CryptCreateHash");
  CryptReleaseContext(prov, 0);
  return -1;
 }

 for (i = 0; i < num_elem; i++) {
  if (!CryptHashData(hash, (BYTE *) addr[i], len[i], 0)) {
   cryptoapi_report_error("CryptHashData");
   CryptDestroyHash(hash);
   CryptReleaseContext(prov, 0);
  }
 }

 hlen = hash_len;
 if (!CryptGetHashParam(hash, HP_HASHVAL, mac, &hlen, 0)) {
  cryptoapi_report_error("CryptGetHashParam");
  ret = -1;
 }

 CryptDestroyHash(hash);
 CryptReleaseContext(prov, 0);

 return ret;
}

int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
{
 return cryptoapi_hash_vector(CALG_MD5, 16, num_elem, addr, len, mac);
}

static u8 * decrypt_ms_key(const u8 *key, size_t len,
      const u8 *req_authenticator,
      const u8 *secret, size_t secret_len, size_t *reslen)
{
 u8 *plain, *ppos, *res;
 const u8 *pos;
 size_t left, plen;
 u8 hash[MD5_MAC_LEN];
 int i, first = 1;
 const u8 *addr[3];
 size_t elen[3];

//wpa_hexdump_key(1, "key", key, len);
//wpa_hexdump_key(1, "secret", key, len);
//wpa_hexdump_key(1, "auth", req_authenticator, MD5_MAC_LEN);

 /* key: 16-bit salt followed by encrypted key info */

 if (len < 2 + 16)
  return NULL;

 pos = key + 2;
 left = len - 2;
 if (left % 16) {
  printf("Invalid ms key len %lu\n", (unsigned long) left);
  return NULL;
 }

 plen = left;
 ppos = plain = (u8*)os_malloc(plen);
 if (plain == NULL)
  return NULL;
 plain[0] = 0;

 while (left > 0) {
  addr[0] = secret;
  elen[0] = secret_len;
  if (first) {
   addr[1] = req_authenticator;
   elen[1] = MD5_MAC_LEN;
   addr[2] = key;
   elen[2] = 2; /* Salt */
  } else {
   addr[1] = pos - MD5_MAC_LEN;
   elen[1] = MD5_MAC_LEN;
  }
  md5_vector(first ? 3 : 2, addr, elen, hash);
  first = 0;

  for (i = 0; i < MD5_MAC_LEN; i++)
   *ppos++ = *pos++ ^ hash[i];
  left -= MD5_MAC_LEN;
 }

 if (plain[0] == 0 || plain[0] > plen - 1) {
  printf("Failed to decrypt MPPE key\n");
  os_free(plain);
  return NULL;
 }

 res = (u8*)os_malloc(plain[0]);
 if (res == NULL) {
  os_free(plain);
  return NULL;
 }
 os_memcpy(res, plain + 1, plain[0]);
 if (reslen)
  *reslen = plain[0];
 os_free(plain);
 return res;
}

void processTokens(char*  authenticator,
       u8*   processedAuthenticator,
       char*  recvKey,
       u8*   processedRecvKey )
{
 // Handle authenticator
 char* ptr = strtok( authenticator, ":");
 int i = 0;
 while( ptr )
 {
  processedAuthenticator[i++] = ( u8 ) strtoul( ptr, NULL, 16 );
  ptr = strtok( NULL, ":");
 }

 // Handle key
 ptr = strtok( recvKey, ":");
 i = 0;
 while( ptr )
 {
  processedRecvKey[i++] = ( u8 ) strtoul( ptr, NULL, 16 );
  ptr = strtok( NULL, ":");
 }
}

void dumpPmk( const u8*  pmk )
{
 printf( "PMK is:\n" );
 for( int i = 0; i < 32; i++ )
  printf( "%02x", pmk[i] );
 printf( "\n" );
}

int main(int argc, char*argv[])
{
 if( argc != 4 )
 {
  printf( "Usage: %s secret authenticator recv-key", argv[0] );
  return( 1 );
 }

 if( strlen( argv[2] ) != 47 )
 {
  printf( "Bad authenticator length" );
  return( 1 );
 }

 if( strlen( argv[3] ) != 149 )
 {
  printf( "Bad recv-key length" );
  return( 1 );
 }

 u8 processedAuthenticator[16];
 u8 processedRecvKey[50];
 u8* pmk;
 u32 pmklen = 0;

 processTokens(argv[2], processedAuthenticator, argv[3], processedRecvKey );

 pmk = decrypt_ms_key(processedRecvKey, 50,
      processedAuthenticator,
      (u8*)argv[1], strlen(argv[1]), &pmklen);

 dumpPmk( pmk );

 os_free(pmk);

 return(1);
}
Adding PMKextract.cpp to the Solution Explorer
Unsafe function or variable warning
Allow project built by modifying Preprocessor Definitions
Successful build of the PMK extracting code

I then copied compiled ‘Project1.exe’ file to C:\Geekwifi and renamed it to ‘PMKextract.exe’.

8. Extract PMK

PMKextract usage
Extracting PMK using all values that we gathered: RADIUS secret, authenticator and Recv-Key

9. Specify decryption key in wireless captures in Wireshark

Provide Wireshark with extracted PMK in ‘Edit > Preferences > Protocols > IEEE 802.11 > Decryption keys > Edit’

10. Enjoy decrypted wireless captures!

Decrypted 802.1X WPA2-Enterprise session

Conclusion

Properly configured and physically secured WPA2-Enterptise wireless network, especially where client and server certs are involved, is still considered highly secure. Specific conditions must be met to decrypt 802.1X EAP wireless session captures, where RADIUS key must be known, and the attacker would have to be able to capture RADIUS conversation on the wire and 4-Way Handshake on wireless to make it possible. Additionally, decrypting WPA2-Enterprise session does not necessarily mean we could eavesdrop on the meaningful user data, as it might be encrypted on a data level, i.e. using TLS or SSL, that do not rely on WiFi infrastructure encryption.

Literature

Exit mobile version