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.
Conditions
The following conditions must be met to decrypt 802.1X EAP encrypted captures:
1. RADIUS key must be known
- Brute force against the RADIUS capture is an option but strong RADIUS key would make it unpractical. Make sure the key is strong!
- Social engineering attack – get network engineer’s contact details and ask him/her about a RADIUS key saying you’re from NOC tshooting an issue or a contractor working on upgrading RADIUS server and see what happens. Make sure staff is trained how to handle social engineering attacks and that their contact details like phones, mails and positions within the company are well secured!
- Access to RADIUS server – some mainstream RADIUS servers (MS NPS, FreeRADIUS) store the key unencrypted in a file. Cisco ISE can show you password when you’re logged in. Make sure access to the RADIUS servers and logon credentials are properly secured!
2. Wireless capture of the session that we want to decrypt must be taken
- Session must include 4-way handshake, so must include both packets coming from the client and AP, meaning that the potential attacker would need to be physically close to both.
3. Wired capture of RADIUS authentication must be taken
- Capture of RADIUS authentication on the wire is essential, as PMK is never sent over the wireless, so it can’t be eavesdropped. We’ll extract it from RADIUS wired captures.
- Capturing RADIUS traffic would require physical access to the LAN to plug a collector into or access to the network infra to configure SPAN. Make sure that the admin access to the network equipment is secure and that all infrastructure is physically locked!
Lab Environment
Here is the lab setup and capture locations (both wired and wireless):
- Wireless Captures: Cisco AP in sniffer mode placed between my wireless test client and client serving AP, configured to send captures to my Windows Server VM running Wireshark.
- Wired Captures: Cisco switch with SPAN monitoring session and port facing my ESXi server (with Cisco ISE RADIUS VM running there) being a SPAN source and switch port facing my laptop being a SPAN destination.
Steps
Here is a high-level summary of what we need to do to decrypt our WPA2-Enterprise wireless session:
- Extract PMK with wired RADIUS captures; use RADIUS Shared Secret, Request Authenticator from the final Access-Request RADIUS frame and MS-MPPE-Recv-Key from the RADIUS Access-Accept frame.
- Capture 4-Way Handshake with wireless captures.
- Use extracted PMK and 4-Way Handshake to derive PTK with Wireshark and use it to decrypt user data.
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
- Since I’m using ISE, here is where I would look to get it:
2. Start capturing wireless traffic of interest
- Position capturing device so it can capture both wireless client and AP traffic.
3. Start capturing wired RADIUS traffic between the Authentication Server and the Authenticator
- Ensure to capture full RADIUS exchange for the wireless device authentication.
4. Connect wireless device to the EAP SSID
- If it’s your test device, disconnect and then connect again.
- If it’s not your test device, you could try to force the device to re-connect by sending a de-auth; SSID must not use management frames protection mechanism, and they usually don’t for compatibility reasons.
5. Obtain Authenticator from the last Access-Request packet in wired RADIUS capture
- Go to RADIUS Protocol > Authenticator.
- Here it’s 00:0d:42:73:f6:19:5c:d3:88:73:cf:b3:2c:76:5d:16 (you can copy value in hex straight from the capture).
6. Obtain MS-MPPE-Recv-Key from the Access-Accept packet in wired RADIUS capture
- Go to RADIUS Protocol > Attribute Value Pairs > AVP: t=Vendor Specific (last one)
- Here it’s cf:6f:b5:06:da:57:b1:9c:e4:6d:76:af:93:51:59:7e:2c:f8:cd:79:c6:2b:e1:a5:4f:ab:28:bd:ed:d3:81:d3:a9:57:dd:74:f8:d1:41:b8:ec:50:ea:d7:27:75:85:d3:1e:d3
7. Compile PMKextract code, that we will use to extract PMK
- I used Visual Studio in Windows to build the code mentioned earlier. There are also Python versions of similar code available but since I’ve already had Visual and it worked, I focused on this approach.
- Create a new C++ project/file in Visual and paste this code:
#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);
}
- Save .cpp file. I saved it as “PMKextract.cpp”:
- If you try to build it now, you’d probably see the error:
- To allow project to build successfully, add _CRT_SECURE_NO_WARNINGS under Project -> Properties -> C/C++ -> Preprocessor -> Preprocessor Definitions:
- Build > Build project should now be successful:
I then copied compiled ‘Project1.exe’ file to C:\Geekwifi and renamed it to ‘PMKextract.exe’.
8. Extract PMK
- Navigate to the location of your PMKextract.exe file. It takes all required attributes as shown:
- Get the PMK:
9. Specify decryption key in wireless captures in Wireshark
- Finally, open wireless captures and use our extracted PMK as a wpa-psk key.
10. Enjoy decrypted wireless captures!
- We can see all the usual stuff – probes, authentication and association requests and responses, EAP process with EAP Success at the end, 4-way handshake and then decrypted data. Happy days!
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
- CWAP Courseware by Peter MacKenzie
- Code to extract PMK from sniffed RADIUS exchange found on Wirewatchers blog
- Hacking Exposed Wireless, Third Edition