Fulfill
your WiFi Dreams!
:}
Stay up to date with the WiFi Ninjas
Never miss a blog or podcast again!
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.
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.
The following conditions must be met to decrypt 802.1X EAP encrypted captures:
Here is the lab setup and capture locations (both wired and wireless):
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!
#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);
}
I then copied compiled ‘Project1.exe’ file to C:\Geekwifi and renamed it to ‘PMKextract.exe’.
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.
Never miss a blog or podcast again!
Never miss a blog or podcast again!