linuxwifidriverwpa

Extracting the PMK from a WiFi client under EAP-PEAP authentication, for analysis on Wireshark


I'm starting a research project related to WiFi, which requires me to analyze the exchange of frames in between a WiFi client and AP during connection setup.

Experimental setup: I have a laptop M, capturing WiFi traffic with Wireshark on monitor mode, a device A (running Linux) connecting to a WiFi network via 802.1x authentication, specifically using the EAP-PEAP protocol.

I only control the laptop M and device A, but not the APs nor the network (which includes the RADIUS servers).

Objective: I'd like to decode the WiFi data frames captured by Wireshark on laptop M. According to this Wireshark how to, I would need to somehow extract the PMK (Pairwise Master Key) exchanged between the network and device A.

Since I only control device A, I would need to extract the PMK from it.

Problem: I don't know how to extract that PMK from device A (assuming a Linux operating system), specially considering we're talking about a EAP-PEAP session, and I haven't been able to find any conclusive hints while searching on the Web.

I could find this discussion, which implies the PMK is stored directly at the WiFi NIC or the driver.

Questions:

P.S.: I'd like to note that I don't consider this to be a 'hacking' question (and thus not at odds with the policies of stackoverflow.com), since I control the client device (not only the monitoring device).


Solution

  • I found 2 ways of having wpa_supplicant output the PMK:

    1. Easy way: Call wpa_supplicant with the -K option, together with some debug option (e.g. -dd). This will include keys (passwords, etc.) in debug output.
    2. Hard way: Alter the code of wpa_supplicant to output keys. Given that the -K option exists, this way is unnecessarily hard, but it was the first thing I tried (don't ask). I've summarized how I've done this below, which may be interesting for someone wishing to alter the code of wpa_supplicant.

    2) Hard Way: Altering wpa_supplicant Code

    I was able to find an answer by altering the code of wpa_supplicant, making it output the PMK during authentication. In my case, the client device was a Raspberry Pi model B+, V1 2, running Raspbian GNU/Linux 7 (wheezy), which used an old version of wpa_supplicant, namely v1.0, 2012-05-10.

    For anyone wondering, I've altered the source file src/rsn_supp/wpa.c, function wpa_supplicant_key_neg_complete(), to output the PMK, as follows:

    static void wpa_supplicant_key_neg_complete(struct wpa_sm *sm,
                        const u8 *addr, int secure)
    {
        // pass the pmk (pairwise master key) to a hex string.
        int i;
        // hex str to hold pmk. 1024 bit 
        // should be enough (the pmk is supposed to be 256 bit, thus 32 byte, thus 
        // 64 hex chars)
        char pmk_str[1024] = {'\0'};
        char * pmk_ptr = pmk_str;
        // use os_snprintf() (as used by other methods in wpa.c)
        for (i = 0; i < sm->pmk_len; i++) {
            // wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
            //  "WPA: pmk[%d]: %02X", i, sm->pmk[i]);
            pmk_ptr += sprintf(pmk_ptr, "%02X", sm->pmk[i]);
        }
        *(pmk_ptr + 1) = '\0';
    
        wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
            "WPA: Key negotiation completed with "MACSTR": \
            \n\tPMK=%s \
            \n\t[PTK=%s GTK=%s]", 
            MAC2STR(addr),
            pmk_str,
            wpa_cipher_txt(sm->pairwise_cipher),
            wpa_cipher_txt(sm->group_cipher));
    (...)
    
    }
    

    This customization is available on my github.