diff options
author | Alexander Wetzel <alexander.wetzel@web.de> | 2015-11-01 18:49:42 +0100 |
---|---|---|
committer | Alexis La Goutte <alexis.lagoutte@gmail.com> | 2015-11-09 11:28:14 +0000 |
commit | cb3dd958af31099772c8934179e113929ae0c020 (patch) | |
tree | f65bfcfea17b718ad59ea727968c6669453c489b | |
parent | d4985a5acb63460797f4d009366a2038b0797570 (diff) |
WPA/WPA2 decoding fixes and improvements
- start decoding when we have eapol1+2 packets
Do not insist on a complete captured handshake, decode what we can.
- more robust way to detect eapol #2 packets
At least Win 10 is violating the spec on rekey by setting the secure
bit in #2. Unpatched version shows and handles #2 as #4, breaking
decoding after rekey.
- fixed eapol rekey key handling
Inital patch (see https://code.wireshark.org/review/8268)
is adding redundant keys, since it scans all the time
and not only once.
- ignore tailing garbage after eapol sections in frame
See https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=9065#c8
Included testcase to test decode for incomplete handshakes and eapol2
packets with secure bit set on rekey.
Ping-Bug: 9065
Change-Id: Id775088db9b5aaa80da9efdeed6902d024b5c0cd
Reviewed-on: https://code.wireshark.org/review/11484
Reviewed-by: Michael Mann <mmann78@netscape.net>
Petri-Dish: Michael Mann <mmann78@netscape.net>
Tested-by: Petri Dish Buildbot <buildbot-no-reply@wireshark.org>
Reviewed-by: Alexis La Goutte <alexis.lagoutte@gmail.com>
-rw-r--r-- | epan/crypt/airpdcap.c | 367 | ||||
-rw-r--r-- | epan/crypt/airpdcap_debug.h | 2 | ||||
-rw-r--r-- | epan/crypt/airpdcap_system.h | 3 | ||||
-rw-r--r-- | epan/crypt/airpdcap_user.h | 11 | ||||
-rw-r--r-- | epan/dissectors/packet-ieee80211.c | 87 | ||||
-rw-r--r-- | test/captures/wpa-test-decode.pcap.gz | bin | 0 -> 167294 bytes | |||
-rw-r--r-- | test/config/80211_keys.tmpl | 1 | ||||
-rwxr-xr-x | test/suite-decryption.sh | 17 |
8 files changed, 227 insertions, 261 deletions
diff --git a/epan/crypt/airpdcap.c b/epan/crypt/airpdcap.c index a5cc0fdf4e..42c67ddf3c 100644 --- a/epan/crypt/airpdcap.c +++ b/epan/crypt/airpdcap.c @@ -523,7 +523,7 @@ static INT AirPDcapScanForKeys( /* get and check the body length (IEEE 802.1X-2004, pg. 25) */ bodyLength=pntoh16(data+offset+2); - if ((tot_len-offset-4) > bodyLength) { + if ((tot_len-offset-4) < bodyLength) { /* Only check if frame is long enough for eapol header, ignore tailing garbage, see bug 9065 */ AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapScanForKeys", "EAPOL body too short", AIRPDCAP_DEBUG_LEVEL_3); return AIRPDCAP_RET_NO_VALID_HANDSHAKE; } @@ -548,6 +548,7 @@ static INT AirPDcapScanForKeys( /* search for a cached Security Association for current BSSID and AP */ sa = AirPDcapGetSaPtr(ctx, &id); if (sa == NULL){ + AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapScanForKeys", "No SA for BSSID found", AIRPDCAP_DEBUG_LEVEL_3); return AIRPDCAP_RET_UNSUCCESS; } @@ -618,11 +619,12 @@ INT AirPDcapPacketProcess( UCHAR *decrypt_data, guint *decrypt_len, PAIRPDCAP_KEY_ITEM key, - gboolean mngHandshake, - gboolean mngDecrypt) + gboolean scanHandshake) { const UCHAR *addr; AIRPDCAP_SEC_ASSOCIATION_ID id; + UCHAR tmp_data[AIRPDCAP_MAX_CAPLEN]; + guint tmp_len; #ifdef _DEBUG #define MSGBUF_LEN 255 @@ -657,9 +659,6 @@ INT AirPDcapPacketProcess( /* get BSSID */ if ( (addr=AirPDcapGetBssidAddress((const AIRPDCAP_MAC_FRAME_ADDR4 *)(data))) != NULL) { memcpy(id.bssid, addr, AIRPDCAP_MAC_LEN); -#ifdef _DEBUG - g_snprintf(msgbuf, MSGBUF_LEN, "BSSID: %2X.%2X.%2X.%2X.%2X.%2X\t", id.bssid[0],id.bssid[1],id.bssid[2],id.bssid[3],id.bssid[4],id.bssid[5]); -#endif AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapPacketProcess", msgbuf, AIRPDCAP_DEBUG_LEVEL_3); } else { AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapPacketProcess", "BSSID not found", AIRPDCAP_DEBUG_LEVEL_5); @@ -669,9 +668,6 @@ INT AirPDcapPacketProcess( /* get STA address */ if ( (addr=AirPDcapGetStaAddress((const AIRPDCAP_MAC_FRAME_ADDR4 *)(data))) != NULL) { memcpy(id.sta, addr, AIRPDCAP_MAC_LEN); -#ifdef _DEBUG - g_snprintf(msgbuf, MSGBUF_LEN, "ST_MAC: %2X.%2X.%2X.%2X.%2X.%2X\t", id.sta[0],id.sta[1],id.sta[2],id.sta[3],id.sta[4],id.sta[5]); -#endif AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapPacketProcess", msgbuf, AIRPDCAP_DEBUG_LEVEL_3); } else { AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapPacketProcess", "SA not found", AIRPDCAP_DEBUG_LEVEL_5); @@ -679,77 +675,78 @@ INT AirPDcapPacketProcess( } /* check if data is encrypted (use the WEP bit in the Frame Control field) */ - if (AIRPDCAP_WEP(data[1])==0) - { - if (mngHandshake) { + if (AIRPDCAP_WEP(data[1])==0) { + if (scanHandshake) { /* data is sent in cleartext, check if is an authentication message or end the process */ AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapPacketProcess", "Unencrypted data", AIRPDCAP_DEBUG_LEVEL_3); return (AirPDcapScanForKeys(ctx, data, mac_header_len, tot_len, id, key)); } } else { - if (mngDecrypt) { - PAIRPDCAP_SEC_ASSOCIATION sa; - int offset = 0; + PAIRPDCAP_SEC_ASSOCIATION sa; + int offset = 0; - /* get the Security Association structure for the STA and AP */ - sa = AirPDcapGetSaPtr(ctx, &id); - if (sa == NULL){ - return AIRPDCAP_RET_UNSUCCESS; - } + /* get the Security Association structure for the STA and AP */ + sa = AirPDcapGetSaPtr(ctx, &id); + if (sa == NULL){ + return AIRPDCAP_RET_UNSUCCESS; + } - /* cache offset in the packet data (to scan encryption data) */ - offset = mac_header_len; + /* cache offset in the packet data (to scan encryption data) */ + offset = mac_header_len; - if (decrypt_data==NULL) - return AIRPDCAP_RET_UNSUCCESS; + if (decrypt_data==NULL) { + AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapPacketProcess", "no decrypt buffer, use local", AIRPDCAP_DEBUG_LEVEL_3); + decrypt_data=tmp_data; + decrypt_len=&tmp_len; + } - /* create new header and data to modify */ - *decrypt_len = tot_len; - memcpy(decrypt_data, data, *decrypt_len); + /* create new header and data to modify */ + *decrypt_len = tot_len; + memcpy(decrypt_data, data, *decrypt_len); - /* encrypted data */ - AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapPacketProcess", "Encrypted data", AIRPDCAP_DEBUG_LEVEL_3); + /* encrypted data */ + AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapPacketProcess", "Encrypted data", AIRPDCAP_DEBUG_LEVEL_3); - /* check the Extension IV to distinguish between WEP encryption and WPA encryption */ - /* refer to IEEE 802.11i-2004, 8.2.1.2, pag.35 for WEP, */ - /* IEEE 802.11i-2004, 8.3.2.2, pag. 45 for TKIP, */ - /* IEEE 802.11i-2004, 8.3.3.2, pag. 57 for CCMP */ - if (AIRPDCAP_EXTIV(data[offset+3])==0) { - AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapPacketProcess", "WEP encryption", AIRPDCAP_DEBUG_LEVEL_3); - return AirPDcapWepMng(ctx, decrypt_data, mac_header_len, decrypt_len, key, sa, offset); - } else { - AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapPacketProcess", "TKIP or CCMP encryption", AIRPDCAP_DEBUG_LEVEL_3); + /* check the Extension IV to distinguish between WEP encryption and WPA encryption */ + /* refer to IEEE 802.11i-2004, 8.2.1.2, pag.35 for WEP, */ + /* IEEE 802.11i-2004, 8.3.2.2, pag. 45 for TKIP, */ + /* IEEE 802.11i-2004, 8.3.3.2, pag. 57 for CCMP */ + if (AIRPDCAP_EXTIV(data[offset+3])==0) { + AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapPacketProcess", "WEP encryption", AIRPDCAP_DEBUG_LEVEL_3); + return AirPDcapWepMng(ctx, decrypt_data, mac_header_len, decrypt_len, key, sa, offset); + } else { + AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapPacketProcess", "TKIP or CCMP encryption", AIRPDCAP_DEBUG_LEVEL_3); - /* If index >= 1, then use the group key. This will not work if the AP is using - more than one group key simultaneously. I've not seen this in practice, however. - Usually an AP will rotate between the two key index values of 1 and 2 whenever - it needs to change the group key to be used. */ - if (AIRPDCAP_KEY_INDEX(data[offset+3])>=1){ + /* If index >= 1, then use the group key. This will not work if the AP is using + more than one group key simultaneously. I've not seen this in practice, however. + Usually an AP will rotate between the two key index values of 1 and 2 whenever + it needs to change the group key to be used. */ + if (AIRPDCAP_KEY_INDEX(data[offset+3])>=1){ - AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapPacketProcess", "The key index = 1. This is encrypted with a group key.", AIRPDCAP_DEBUG_LEVEL_3); + AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapPacketProcess", "The key index = 1. This is encrypted with a group key.", AIRPDCAP_DEBUG_LEVEL_3); - /* force STA address to broadcast MAC so we load the SA for the groupkey */ - memcpy(id.sta, broadcast_mac, AIRPDCAP_MAC_LEN); + /* force STA address to broadcast MAC so we load the SA for the groupkey */ + memcpy(id.sta, broadcast_mac, AIRPDCAP_MAC_LEN); #ifdef _DEBUG - g_snprintf(msgbuf, MSGBUF_LEN, "ST_MAC: %2X.%2X.%2X.%2X.%2X.%2X\t", id.sta[0],id.sta[1],id.sta[2],id.sta[3],id.sta[4],id.sta[5]); - AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapPacketProcess", msgbuf, AIRPDCAP_DEBUG_LEVEL_3); + g_snprintf(msgbuf, MSGBUF_LEN, "ST_MAC: %2X.%2X.%2X.%2X.%2X.%2X\t", id.sta[0],id.sta[1],id.sta[2],id.sta[3],id.sta[4],id.sta[5]); + AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapPacketProcess", msgbuf, AIRPDCAP_DEBUG_LEVEL_3); #endif - /* search for a cached Security Association for current BSSID and broadcast MAC */ - sa = AirPDcapGetSaPtr(ctx, &id); - if (sa == NULL){ - return AIRPDCAP_RET_UNSUCCESS; - } - } + /* search for a cached Security Association for current BSSID and broadcast MAC */ + sa = AirPDcapGetSaPtr(ctx, &id); + if (sa == NULL) + return AIRPDCAP_RET_UNSUCCESS; + } - /* Decrypt the packet using the appropriate SA */ - if (AirPDcapRsnaMng(decrypt_data, mac_header_len, decrypt_len, key, sa, offset) == AIRPDCAP_RET_SUCCESS) - { - /* If we successfully decrypted a packet, scan it to see if it contains a key handshake. - The group key handshake could be sent at any time the AP wants to change the key (such as when - it is using key rotation) and it also could be a rekey for the Pairwise key. So we must scan every packet. */ - AirPDcapScanForKeys(ctx, decrypt_data, mac_header_len, *decrypt_len, id, NULL); + /* Decrypt the packet using the appropriate SA */ + if (AirPDcapRsnaMng(decrypt_data, mac_header_len, decrypt_len, key, sa, offset) == AIRPDCAP_RET_SUCCESS) { + /* If we successfully decrypted a packet, scan it to see if it contains a key handshake. + The group key handshake could be sent at any time the AP wants to change the key (such as when + it is using key rotation) and it also could be a rekey for the Pairwise key. So we must scan every packet. */ + if (scanHandshake) { + return (AirPDcapScanForKeys(ctx, decrypt_data, mac_header_len, *decrypt_len, id, NULL)); + } else { return AIRPDCAP_RET_SUCCESS; } } @@ -980,8 +977,9 @@ AirPDcapRsnaMng( AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapRsnaMng", "No key associated", AIRPDCAP_DEBUG_LEVEL_3); return AIRPDCAP_RET_REQ_DATA; } - if (sa->validKey==FALSE) { - AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapRsnaMng", "Key not yet valid", AIRPDCAP_DEBUG_LEVEL_3); + + if (*decrypt_len > try_data_len) { + AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapRsnaMng", "Invalid decryption length", AIRPDCAP_DEBUG_LEVEL_3); return AIRPDCAP_RET_UNSUCCESS; } @@ -991,11 +989,10 @@ AirPDcapRsnaMng( /* start of loop added by GCS */ for(/* sa */; sa != NULL ;sa=sa->next) { - if (*decrypt_len > try_data_len) { - AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapRsnaMng", "Invalid decryption length", AIRPDCAP_DEBUG_LEVEL_3); - g_free(try_data); - return AIRPDCAP_RET_UNSUCCESS; - } + if (sa->validKey==FALSE) { + AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapRsnaMng", "Key not yet valid", AIRPDCAP_DEBUG_LEVEL_3); + continue; + } /* copy the encrypted data into a temp buffer */ memcpy(try_data, decrypt_data, *decrypt_len); @@ -1107,8 +1104,7 @@ AirPDcapWepMng( } /* obviously, try only WEP keys... */ - if (tmp_key->KeyType==AIRPDCAP_KEY_TYPE_WEP) - { + if (tmp_key->KeyType==AIRPDCAP_KEY_TYPE_WEP) { AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapWepMng", "Try WEP key...", AIRPDCAP_DEBUG_LEVEL_3); memset(wep_key, 0, sizeof(wep_key)); @@ -1202,13 +1198,6 @@ AirPDcapRsna4WHandshake( /* TODO timeouts? */ - /* This saves the sa since we are reauthenticating which will overwrite our current sa GCS*/ - if(sa->handshake == 4) { - tmp_sa= g_new(AIRPDCAP_SEC_ASSOCIATION, 1); - memcpy(tmp_sa, sa, sizeof(AIRPDCAP_SEC_ASSOCIATION)); - sa->next=tmp_sa; - } - /* TODO consider key-index */ /* TODO considera Deauthentications */ @@ -1229,6 +1218,14 @@ AirPDcapRsna4WHandshake( /* local value, the Supplicant discards the message. */ /* -> not checked, the Authenticator will be send another Message 1 (hopefully!) */ + /* This saves the sa since we are reauthenticating which will overwrite our current sa GCS*/ + if( sa->handshake >= 2) { + tmp_sa= g_new(AIRPDCAP_SEC_ASSOCIATION, 1); + memcpy(tmp_sa, sa, sizeof(AIRPDCAP_SEC_ASSOCIATION)); + sa->validKey=FALSE; + sa->next=tmp_sa; + } + /* save ANonce (from authenticator) to derive the PTK with the SNonce (from the 2 message) */ memcpy(sa->wpa.nonce, data+offset+12, 32); @@ -1245,139 +1242,117 @@ AirPDcapRsna4WHandshake( AIRPDCAP_EAP_ACK(data[offset+1])==0 && AIRPDCAP_EAP_MIC(data[offset])==1) { - if (AIRPDCAP_EAP_SEC(data[offset])==0) { - - /* PATCH: some implementations set secure bit to 0 also in the 4th message */ - /* to recognize which message is this check if wep_key data length is 0 */ - /* in the 4th message */ - if (data[offset+92]!=0 || data[offset+93]!=0) { - /* message 2 */ - AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapRsna4WHandshake", "4-way handshake message 2", AIRPDCAP_DEBUG_LEVEL_3); - - /* On reception of Message 2, the Authenticator checks that the key replay counter corresponds to the */ - /* outstanding Message 1. If not, it silently discards the message. */ - /* If the calculated MIC does not match the MIC that the Supplicant included in the EAPOL-Key frame, */ - /* the Authenticator silently discards Message 2. */ - /* -> not checked; the Supplicant will send another message 2 (hopefully!) */ - - /* now you can derive the PTK */ - for (key_index=0; key_index<(INT)ctx->keys_nr || useCache; key_index++) { - /* use the cached one, or try all keys */ - if (!useCache) { - AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapRsna4WHandshake", "Try WPA key...", AIRPDCAP_DEBUG_LEVEL_3); - tmp_key=&ctx->keys[key_index]; + /* Check nonce to differentiate between message 2 or 4 + * nonce will be non zero for message 2 and zero for message 4. + * At least needed for Windows, since it is setting the secure bit on message 2 when rekeying */ + if (!memiszero(data+offset+12, 32)) { + /* message 2 */ + AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapRsna4WHandshake", "4-way handshake message 2", AIRPDCAP_DEBUG_LEVEL_3); + + /* On reception of Message 2, the Authenticator checks that the key replay counter corresponds to the */ + /* outstanding Message 1. If not, it silently discards the message. */ + /* If the calculated MIC does not match the MIC that the Supplicant included in the EAPOL-Key frame, */ + /* the Authenticator silently discards Message 2. */ + /* -> not checked; the Supplicant will send another message 2 (hopefully!) */ + + /* now you can derive the PTK */ + for (key_index=0; key_index<(INT)ctx->keys_nr || useCache; key_index++) { + /* use the cached one, or try all keys */ + if (!useCache) { + AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapRsna4WHandshake", "Try WPA key...", AIRPDCAP_DEBUG_LEVEL_3); + tmp_key=&ctx->keys[key_index]; + } else { + /* there is a cached key in the security association, if it's a WPA key try it... */ + if (sa->key!=NULL && + (sa->key->KeyType==AIRPDCAP_KEY_TYPE_WPA_PWD || + sa->key->KeyType==AIRPDCAP_KEY_TYPE_WPA_PSK || + sa->key->KeyType==AIRPDCAP_KEY_TYPE_WPA_PMK)) { + AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapRsna4WHandshake", "Try cached WPA key...", AIRPDCAP_DEBUG_LEVEL_3); + tmp_key=sa->key; } else { - /* there is a cached key in the security association, if it's a WPA key try it... */ - if (sa->key!=NULL && - (sa->key->KeyType==AIRPDCAP_KEY_TYPE_WPA_PWD || - sa->key->KeyType==AIRPDCAP_KEY_TYPE_WPA_PSK || - sa->key->KeyType==AIRPDCAP_KEY_TYPE_WPA_PMK)) { - AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapRsna4WHandshake", "Try cached WPA key...", AIRPDCAP_DEBUG_LEVEL_3); - tmp_key=sa->key; - } else { - AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapRsna4WHandshake", "Cached key is of a wrong type, try WPA key...", AIRPDCAP_DEBUG_LEVEL_3); - tmp_key=&ctx->keys[key_index]; - } - } - - /* obviously, try only WPA keys... */ - if (tmp_key->KeyType==AIRPDCAP_KEY_TYPE_WPA_PWD || - tmp_key->KeyType==AIRPDCAP_KEY_TYPE_WPA_PSK || - tmp_key->KeyType==AIRPDCAP_KEY_TYPE_WPA_PMK) - { - if (tmp_key->KeyType == AIRPDCAP_KEY_TYPE_WPA_PWD && tmp_key->UserPwd.SsidLen == 0 && ctx->pkt_ssid_len > 0 && ctx->pkt_ssid_len <= AIRPDCAP_WPA_SSID_MAX_LEN) { - /* We have a "wildcard" SSID. Use the one from the packet. */ - memcpy(&pkt_key, tmp_key, sizeof(pkt_key)); - memcpy(&pkt_key.UserPwd.Ssid, ctx->pkt_ssid, ctx->pkt_ssid_len); - pkt_key.UserPwd.SsidLen = ctx->pkt_ssid_len; - AirPDcapRsnaPwd2Psk(pkt_key.UserPwd.Passphrase, pkt_key.UserPwd.Ssid, - pkt_key.UserPwd.SsidLen, pkt_key.KeyData.Wpa.Psk); - tmp_pkt_key = &pkt_key; - } else { - tmp_pkt_key = tmp_key; - } - - /* derive the PTK from the BSSID, STA MAC, PMK, SNonce, ANonce */ - AirPDcapRsnaPrfX(sa, /* authenticator nonce, bssid, station mac */ - tmp_pkt_key->KeyData.Wpa.Psk, /* PSK == PMK */ - data+offset+12, /* supplicant nonce */ - 512, - sa->wpa.ptk); - - /* verify the MIC (compare the MIC in the packet included in this message with a MIC calculated with the PTK) */ - eapol_len=pntoh16(data+offset-3)+4; - memcpy(eapol, &data[offset-5], (eapol_len<AIRPDCAP_EAPOL_MAX_LEN?eapol_len:AIRPDCAP_EAPOL_MAX_LEN)); - ret_value=AirPDcapRsnaMicCheck(eapol, /* eapol frame (header also) */ - eapol_len, /* eapol frame length */ - sa->wpa.ptk, /* Key Confirmation Key */ - AIRPDCAP_EAP_KEY_DESCR_VER(data[offset+1])); /* EAPOL-Key description version */ - - /* If the MIC is valid, the Authenticator checks that the RSN information element bit-wise matches */ - /* that from the (Re)Association Request message. */ - /* i) TODO If these are not exactly the same, the Authenticator uses MLME-DEAUTHENTICATE.request */ - /* primitive to terminate the association. */ - /* ii) If they do match bit-wise, the Authenticator constructs Message 3. */ + AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapRsna4WHandshake", "Cached key is of a wrong type, try WPA key...", AIRPDCAP_DEBUG_LEVEL_3); + tmp_key=&ctx->keys[key_index]; } + } - if (!ret_value && - (tmp_key->KeyType==AIRPDCAP_KEY_TYPE_WPA_PWD || - tmp_key->KeyType==AIRPDCAP_KEY_TYPE_WPA_PSK || - tmp_key->KeyType==AIRPDCAP_KEY_TYPE_WPA_PMK)) - { - /* the temporary key is the correct one, cached in the Security Association */ - - sa->key=tmp_key; - - if (key!=NULL) { - memcpy(key, tmp_key, sizeof(AIRPDCAP_KEY_ITEM)); - if (AIRPDCAP_EAP_KEY_DESCR_VER(data[offset+1])==AIRPDCAP_WPA_KEY_VER_NOT_CCMP) - key->KeyType=AIRPDCAP_KEY_TYPE_TKIP; - else if (AIRPDCAP_EAP_KEY_DESCR_VER(data[offset+1])==AIRPDCAP_WPA_KEY_VER_AES_CCMP) - key->KeyType=AIRPDCAP_KEY_TYPE_CCMP; - } - - break; + /* obviously, try only WPA keys... */ + if (tmp_key->KeyType==AIRPDCAP_KEY_TYPE_WPA_PWD || + tmp_key->KeyType==AIRPDCAP_KEY_TYPE_WPA_PSK || + tmp_key->KeyType==AIRPDCAP_KEY_TYPE_WPA_PMK) + { + if (tmp_key->KeyType == AIRPDCAP_KEY_TYPE_WPA_PWD && tmp_key->UserPwd.SsidLen == 0 && ctx->pkt_ssid_len > 0 && ctx->pkt_ssid_len <= AIRPDCAP_WPA_SSID_MAX_LEN) { + /* We have a "wildcard" SSID. Use the one from the packet. */ + memcpy(&pkt_key, tmp_key, sizeof(pkt_key)); + memcpy(&pkt_key.UserPwd.Ssid, ctx->pkt_ssid, ctx->pkt_ssid_len); + pkt_key.UserPwd.SsidLen = ctx->pkt_ssid_len; + AirPDcapRsnaPwd2Psk(pkt_key.UserPwd.Passphrase, pkt_key.UserPwd.Ssid, + pkt_key.UserPwd.SsidLen, pkt_key.KeyData.Wpa.Psk); + tmp_pkt_key = &pkt_key; } else { - /* the cached key was not valid, try other keys */ - - if (useCache==TRUE) { - useCache=FALSE; - key_index--; - } + tmp_pkt_key = tmp_key; } - } - if (ret_value) { - AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapRsna4WHandshake", "handshake step failed", AIRPDCAP_DEBUG_LEVEL_3); - return AIRPDCAP_RET_NO_VALID_HANDSHAKE; + /* derive the PTK from the BSSID, STA MAC, PMK, SNonce, ANonce */ + AirPDcapRsnaPrfX(sa, /* authenticator nonce, bssid, station mac */ + tmp_pkt_key->KeyData.Wpa.Psk, /* PSK == PMK */ + data+offset+12, /* supplicant nonce */ + 512, + sa->wpa.ptk); + + /* verify the MIC (compare the MIC in the packet included in this message with a MIC calculated with the PTK) */ + eapol_len=pntoh16(data+offset-3)+4; + memcpy(eapol, &data[offset-5], (eapol_len<AIRPDCAP_EAPOL_MAX_LEN?eapol_len:AIRPDCAP_EAPOL_MAX_LEN)); + ret_value=AirPDcapRsnaMicCheck(eapol, /* eapol frame (header also) */ + eapol_len, /* eapol frame length */ + sa->wpa.ptk, /* Key Confirmation Key */ + AIRPDCAP_EAP_KEY_DESCR_VER(data[offset+1])); /* EAPOL-Key description version */ + + /* If the MIC is valid, the Authenticator checks that the RSN information element bit-wise matches */ + /* that from the (Re)Association Request message. */ + /* i) TODO If these are not exactly the same, the Authenticator uses MLME-DEAUTHENTICATE.request */ + /* primitive to terminate the association. */ + /* ii) If they do match bit-wise, the Authenticator constructs Message 3. */ } - sa->handshake=2; + if (!ret_value && + (tmp_key->KeyType==AIRPDCAP_KEY_TYPE_WPA_PWD || + tmp_key->KeyType==AIRPDCAP_KEY_TYPE_WPA_PSK || + tmp_key->KeyType==AIRPDCAP_KEY_TYPE_WPA_PMK)) + { + /* the temporary key is the correct one, cached in the Security Association */ - return AIRPDCAP_RET_SUCCESS_HANDSHAKE; - } else { - /* message 4 */ + sa->key=tmp_key; - /* TODO "Note that when the 4-Way Handshake is first used Message 4 is sent in the clear." */ + if (key!=NULL) { + memcpy(key, tmp_key, sizeof(AIRPDCAP_KEY_ITEM)); + if (AIRPDCAP_EAP_KEY_DESCR_VER(data[offset+1])==AIRPDCAP_WPA_KEY_VER_NOT_CCMP) + key->KeyType=AIRPDCAP_KEY_TYPE_TKIP; + else if (AIRPDCAP_EAP_KEY_DESCR_VER(data[offset+1])==AIRPDCAP_WPA_KEY_VER_AES_CCMP) + key->KeyType=AIRPDCAP_KEY_TYPE_CCMP; + } - /* TODO check MIC and Replay Counter */ - /* On reception of Message 4, the Authenticator verifies that the Key Replay Counter field value is one */ - /* that it used on this 4-Way Handshake; if it is not, it silently discards the message. */ - /* If the calculated MIC does not match the MIC that the Supplicant included in the EAPOL-Key frame, the */ - /* Authenticator silently discards Message 4. */ + break; + } else { + /* the cached key was not valid, try other keys */ - AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapRsna4WHandshake", "4-way handshake message 4 (patched)", AIRPDCAP_DEBUG_LEVEL_3); + if (useCache==TRUE) { + useCache=FALSE; + key_index--; + } + } + } - sa->handshake=4; + if (ret_value) { + AIRPDCAP_DEBUG_PRINT_LINE("AirPDcapRsna4WHandshake", "handshake step failed", AIRPDCAP_DEBUG_LEVEL_3); + return AIRPDCAP_RET_NO_VALID_HANDSHAKE; + } - sa->validKey=TRUE; + sa->handshake=2; + sa->validKey=TRUE; /* we can use the key to decode, even if we have not captured the other eapol packets */ - return AIRPDCAP_RET_SUCCESS_HANDSHAKE; - } - /* END OF PATCH */ - /* */ + return AIRPDCAP_RET_SUCCESS_HANDSHAKE; } else { - /* message 4 */ + /* message 4 */ /* TODO "Note that when the 4-Way Handshake is first used Message 4 is sent in the clear." */ @@ -1391,8 +1366,6 @@ AirPDcapRsna4WHandshake( sa->handshake=4; - sa->validKey=TRUE; - return AIRPDCAP_RET_SUCCESS_HANDSHAKE; } } @@ -1536,7 +1509,6 @@ AirPDcapGetSa( AIRPDCAP_SEC_ASSOCIATION_ID *id) { INT sa_index; - if (ctx->sa_index!=-1) { /* at least one association was stored */ /* search for the association from sa_index to 0 (most recent added) */ @@ -1559,7 +1531,6 @@ AirPDcapStoreSa( AIRPDCAP_SEC_ASSOCIATION_ID *id) { INT last_free; - if (ctx->first_free_index>=AIRPDCAP_MAX_SEC_ASSOCIATIONS_NR) { /* there is no empty space available. FAILURE */ return -1; diff --git a/epan/crypt/airpdcap_debug.h b/epan/crypt/airpdcap_debug.h index 40d82b5424..fbfe28ec02 100644 --- a/epan/crypt/airpdcap_debug.h +++ b/epan/crypt/airpdcap_debug.h @@ -89,7 +89,7 @@ void print_debug_line(const CHAR *function, const CHAR *msg, const INT level); #endif #endif -static inline void DEBUG_DUMP(x,y,z) +static inline void DEBUG_DUMP(const char* x, guint8* y, int z) { char* tmp_str = bytes_to_str(NULL, (const guint8 *) y, (z)); g_warning("%s: %s", x, tmp_str); diff --git a/epan/crypt/airpdcap_system.h b/epan/crypt/airpdcap_system.h index 08405fda21..0eda91b0a1 100644 --- a/epan/crypt/airpdcap_system.h +++ b/epan/crypt/airpdcap_system.h @@ -233,8 +233,7 @@ extern INT AirPDcapPacketProcess( UCHAR *decrypt_data, guint32 *decrypt_len, PAIRPDCAP_KEY_ITEM key, - gboolean mngHandshake, - gboolean mngDecrypt) + gboolean scanHandshake) ; /** diff --git a/epan/crypt/airpdcap_user.h b/epan/crypt/airpdcap_user.h index aeca3f08c4..1ba30dd76a 100644 --- a/epan/crypt/airpdcap_user.h +++ b/epan/crypt/airpdcap_user.h @@ -78,6 +78,17 @@ /* */ /* */ /******************************************************************************/ +static inline gboolean memiszero (const void *ptr, size_t count) { + const guint8 *p = (const guint8 *)ptr; + + while (count != 0) { + if (*p != 0) + return FALSE; + p++; + count--; + } + return TRUE; +} /******************************************************************************/ /* Type definitions */ diff --git a/epan/dissectors/packet-ieee80211.c b/epan/dissectors/packet-ieee80211.c index 8102b2d380..fba50b3438 100644 --- a/epan/dissectors/packet-ieee80211.c +++ b/epan/dissectors/packet-ieee80211.c @@ -17709,7 +17709,8 @@ dissect_ieee80211_common (tvbuff_t *tvb, packet_info *pinfo, /* and WPA2 decryption */ if (enable_decryption && !pinfo->fd->flags.visited) { const guint8 *enc_data = tvb_get_ptr(tvb, 0, hdr_len+reported_len); - AirPDcapPacketProcess(&airpdcap_ctx, enc_data, hdr_len, hdr_len+reported_len, NULL, 0, NULL, TRUE, FALSE); + AirPDcapPacketProcess(&airpdcap_ctx, enc_data, hdr_len, hdr_len+reported_len, NULL, 0, NULL, TRUE); + } /* Davide Schiera -------------------------------------------------------- */ @@ -18532,7 +18533,7 @@ static gint ett_wlan_rsna_eapol_keydes_data = -1; static const true_false_string keyinfo_key_type_tfs = { "Pairwise Key", "Group Key" }; static int -dissect_wlan_rsna_eapol_wpa_or_rsn_key(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gboolean is_rsn) +dissect_wlan_rsna_eapol_wpa_or_rsn_key(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) { int offset = 0; guint16 keyinfo; @@ -18553,54 +18554,32 @@ dissect_wlan_rsna_eapol_wpa_or_rsn_key(tvbuff_t *tvb, packet_info *pinfo, proto_ col_set_str(pinfo->cinfo, COL_INFO, "Key (Request, Error)"); } else if (keyinfo & KEY_INFO_KEY_TYPE_MASK) { guint16 masked; + /* At least Windows is incorrectly setting Secure bit on message 2 when rekeying + * we'll ignore the secure bit also for RSN and use the key nonce to differentiate between message 2 and 4 */ masked = keyinfo & - (KEY_INFO_INSTALL_MASK | KEY_INFO_KEY_ACK_MASK | - KEY_INFO_KEY_MIC_MASK | KEY_INFO_SECURE_MASK); - - if (!is_rsn) { - /* WPA */ - switch (masked) { - case KEY_INFO_KEY_ACK_MASK: - col_set_str(pinfo->cinfo, COL_INFO, "Key (Message 1 of 4)"); - break; - - case KEY_INFO_KEY_MIC_MASK: - /* Check start of nonce for check if it is message 2 or 4 - According to the IEEE specification, sections 11.6.6.3 and 11.6.6.5 - define the value for the WPA Key Nonce as following: - Message #2, Key Nonce = SNonce (Supplicant Nonce) - Message #4, Key Nonce = 0 */ - start_nonce = tvb_get_ntohl(tvb, offset+12); - if (start_nonce) - col_set_str(pinfo->cinfo, COL_INFO, "Key (Message 2 of 4)"); - else - col_set_str(pinfo->cinfo, COL_INFO, "Key (Message 4 of 4)"); - break; + (KEY_INFO_INSTALL_MASK | KEY_INFO_KEY_ACK_MASK | KEY_INFO_KEY_MIC_MASK); - case (KEY_INFO_INSTALL_MASK | KEY_INFO_KEY_ACK_MASK | KEY_INFO_KEY_MIC_MASK): - col_set_str(pinfo->cinfo, COL_INFO, "Key (Message 3 of 4)"); - break; - } - } else { - /* RSN */ - switch (masked) { - - case KEY_INFO_KEY_ACK_MASK: - col_set_str(pinfo->cinfo, COL_INFO, "Key (Message 1 of 4)"); - break; + switch (masked) { + case KEY_INFO_KEY_ACK_MASK: + col_set_str(pinfo->cinfo, COL_INFO, "Key (Message 1 of 4)"); + break; - case KEY_INFO_KEY_MIC_MASK: + case KEY_INFO_KEY_MIC_MASK: + /* Check start of nonce for check if it is message 2 or 4 + According to the IEEE specification, sections 11.6.6.3 and 11.6.6.5 + define the value for the WPA Key Nonce as following: + Message #2, Key Nonce = SNonce (Supplicant Nonce) + Message #4, Key Nonce = 0 */ + start_nonce = tvb_get_ntohl(tvb, offset+12); + if (start_nonce) col_set_str(pinfo->cinfo, COL_INFO, "Key (Message 2 of 4)"); - break; - - case (KEY_INFO_INSTALL_MASK | KEY_INFO_KEY_ACK_MASK | KEY_INFO_KEY_MIC_MASK | KEY_INFO_SECURE_MASK): - col_set_str(pinfo->cinfo, COL_INFO, "Key (Message 3 of 4)"); - break; - - case (KEY_INFO_KEY_MIC_MASK | KEY_INFO_SECURE_MASK): + else col_set_str(pinfo->cinfo, COL_INFO, "Key (Message 4 of 4)"); - break; - } + break; + + case (KEY_INFO_INSTALL_MASK | KEY_INFO_KEY_ACK_MASK | KEY_INFO_KEY_MIC_MASK): + col_set_str(pinfo->cinfo, COL_INFO, "Key (Message 3 of 4)"); + break; } } else { if (keyinfo & KEY_INFO_KEY_ACK_MASK) @@ -18671,18 +18650,6 @@ dissect_wlan_rsna_eapol_wpa_or_rsn_key(tvbuff_t *tvb, packet_info *pinfo, proto_ return tvb_captured_length(tvb); } -static int -dissect_wlan_rsna_eapol_wpa_key(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) -{ - return dissect_wlan_rsna_eapol_wpa_or_rsn_key(tvb, pinfo, tree, FALSE); -} - -static int -dissect_wlan_rsna_eapol_rsn_key(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) -{ - return dissect_wlan_rsna_eapol_wpa_or_rsn_key(tvb, pinfo, tree, TRUE); -} - /* Davide Schiera (2006-11-26): this function will try to decrypt with WEP or */ /* WPA and return a tvb to the caller to add a new tab. It returns the */ /* algorithm used for decryption (WEP, TKIP, CCMP) and the header and */ @@ -18703,7 +18670,7 @@ try_decrypt(tvbuff_t *tvb, guint offset, guint len, guint8 *algorithm, guint32 * /* process packet with AirPDcap */ if (AirPDcapPacketProcess(&airpdcap_ctx, enc_data, offset, offset+len, dec_data, &dec_caplen, - used_key, FALSE, TRUE)==AIRPDCAP_RET_SUCCESS) + used_key, FALSE)==AIRPDCAP_RET_SUCCESS) { guint8 *tmp; *algorithm=used_key->KeyType; @@ -27294,10 +27261,10 @@ proto_reg_handoff_ieee80211(void) /* * EAPOL key descriptor types. */ - wlan_rsna_eapol_wpa_key_handle = new_create_dissector_handle(dissect_wlan_rsna_eapol_wpa_key, + wlan_rsna_eapol_wpa_key_handle = new_create_dissector_handle(dissect_wlan_rsna_eapol_wpa_or_rsn_key, proto_wlan_rsna_eapol); dissector_add_uint("eapol.keydes.type", EAPOL_WPA_KEY, wlan_rsna_eapol_wpa_key_handle); - wlan_rsna_eapol_rsn_key_handle = new_create_dissector_handle(dissect_wlan_rsna_eapol_rsn_key, + wlan_rsna_eapol_rsn_key_handle = new_create_dissector_handle(dissect_wlan_rsna_eapol_wpa_or_rsn_key, proto_wlan_rsna_eapol); dissector_add_uint("eapol.keydes.type", EAPOL_RSN_KEY, wlan_rsna_eapol_rsn_key_handle); diff --git a/test/captures/wpa-test-decode.pcap.gz b/test/captures/wpa-test-decode.pcap.gz Binary files differnew file mode 100644 index 0000000000..050f94ec4a --- /dev/null +++ b/test/captures/wpa-test-decode.pcap.gz diff --git a/test/config/80211_keys.tmpl b/test/config/80211_keys.tmpl index 450da23791..63593e5849 100644 --- a/test/config/80211_keys.tmpl +++ b/test/config/80211_keys.tmpl @@ -1,5 +1,6 @@ # Keys needed for the decryption test suite "wpa-pwd","Induction" +"wpa-pwd","test0815" "wpa-psk","a5001e18e0b3f792278825bc3abff72d7021d7c157b600470ef730e2490835d4" "wpa-psk","79258f6ceeecedd3482b92deaabdb675f09bcb4003ef5074f5ddb10a94ebe00a" "wpa-psk","23a9ee58c7810546ae3e7509fda9f97435778d689e53a54891c56d02f18ca162" diff --git a/test/suite-decryption.sh b/test/suite-decryption.sh index dd4202dae3..7795c2238d 100755 --- a/test/suite-decryption.sh +++ b/test/suite-decryption.sh @@ -89,6 +89,22 @@ decryption_step_80211_wpa_eap() { fi test_step_ok } +# WPA decode with message1+2 only and secure bit set on message 2 +# Included in git sources test/captures/wpa-test-decode.pcap.gz +decryption_step_80211_wpa_eapol_incomplete_rekeys() { + $TESTS_DIR/run_and_catch_crashes env $TS_DC_ENV $TSHARK $TS_DC_ARGS \ + -o "wlan.enable_decryption: TRUE" \ + -r "$CAPTURE_DIR/wpa-test-decode.pcap.gz" \ + -Y "icmp.resp_to == 4263" \ + | grep "Echo" > /dev/null 2>&1 + RETURNVALUE=$? + if [ ! $RETURNVALUE -eq $EXIT_OK ]; then + test_step_failed "Not able to follow rekey with missing eapol frames" + return + fi + test_step_ok +} + # DTLS # https://wiki.wireshark.org/SampleCaptures?action=AttachFile&do=view&target=snakeoil.tgz @@ -266,6 +282,7 @@ decryption_step_http2() { tshark_decryption_suite() { test_step_add "IEEE 802.11 WPA PSK Decryption" decryption_step_80211_wpa_psk + test_step_add "IEEE 802.11 WPA PSK Decryption2 (EAPOL frames missing for Win 10 client)" decryption_step_80211_wpa_eapol_incomplete_rekeys test_step_add "IEEE 802.11 WPA EAP Decryption" decryption_step_80211_wpa_eap test_step_add "DTLS Decryption" decryption_step_dtls test_step_add "SSL Decryption (private key)" decryption_step_ssl |