WPA/WPA2数据包解密还原
无线网络越来越普及,从此涉及到的业务也出现了多个。从安全圈子来讲,破解、劫持、嗅探等等都开始玩的不亦乐乎。本篇文章详细说明有了数据包如何还原WPA/WPA2加密的密文数据。此文章仅代表博主对WPA/WPA2加密解密的理解,用于学习流程,也为共同研究的同学提供关键字查询更多资料,如有错误请多包涵。
关键字:WAP,WPA2,解密,EAPOL,WPADecrypt。
开放wifi就不提了,直接通过嗅探可以看到所有数据。
加密算法,首先说下常用的有三种:WEP、WPA、WPA2。
WEP这里也不提了,因为爆出了漏洞,所以几乎没有人再使用此加密,更多的是使用WPA/WPA2加密算法。
至于WPA/WPA2的基础知识就不科普了,看这篇文章的朋友都应该有所了解。
接下来直奔主题,如果通过数据包还原出来WPA/WPA2加密的数据内容。WPA和WPA2的流程是一样的,只是部分算法不同,以下流程均可用,如何区别会额外说明。
说解密就要先了解加密。加密流程比较复杂,乱七八糟协议等不多说了,直接说关键流程,文字描述为:
1.输入ssid和密码,生成pmk(Pairwise Master Key)。
2.EAPOL握手包,四次握手,主要信息是里面的A-NONCE、S-NONCE、MIC。EAPOL的四次握手包的流程为:
eapol message 1:AP->Client,AP随机生成32个字节的A-NONCE发往客户端。有用的信息是A-NONCE和EncryptType(用于区分WPA还是WPA2)。
eapol message 2:Client->AP,客服端随机生成32个字节的S-NONCE,并用A-NONCE、S-NONCE和第一步生成的pmk一起生成ptk(Pairwise Transient Key),然后使用ptk加密整个数据流生成mic(Message Integrity Code,类似于tcp数据包的checksum)并填到数据中,发送到AP。有用的信息为S-NONCE、MIC和EncryptType。
eapol message 3:AP->Client,AP验证完上一个包通过后,会生成gtk(Group Temporal Key,这个是解密的关键,通俗来讲可以认为是接下来数据包通信加密用的sessionKey),并用上个握手包生成的ptk加密后,发送到客户端。此包里面的A-NONCE跟message 1里面的A-NONCE一致。有用的信息就是这个gtk(需要用ptk解密)。
eapol message 4:Client->AP,没有什么大作用了,只是跟message 2类似,用ptk加密数据包生成mic发送到AP,用于验证。
3.完成四次EAPOL握手后,就通过了验证开始正常使用。数据包就使用上述生成的gtk来加密。
WPA和WPA2的主要区别在于
1.eapol message 3里面解密还原出gtk,WPA用的是rc4,WPA2用的是aes
2.解密数据包,WPA用的是rc4,WPA2用的是aes
看文字描述有些头大,贴一张图。
难懂的几个关键名词解释:
pmk,使用ssid和密码生成,客户端和AP均生成,用于生成ptk。
ptk,在eapol握手包1和2交互完毕后,AP和客户端均生成。
mic,相当于tcp包里面的checksum,用ptk加密整个数据包生成的。
gtk,解密后续数据包的SessionKey,由AP生成并用ptk加密后发给客户端。
个人理解就是mic是登录wifi输入密码的时候,双方验证密码的checksum信息,客户端和AP用密码共同加密同一份数据(a-nonce、s-nonce、apmac、clientmac),然后出来一个结果交换看看一致不,一致就通过了密码验证。通过验证后AP生成gtk,接下来的数据包都用gtk来加密。
大致流程说了,接下来就是解密和算法了(基于openssl库)。
pmk生成算法:
void makepmk(char *ssid, char *psk, unsigned char *pmk) { //calc pmk(Pairwise Master Key) PKCS5_PBKDF2_HMAC_SHA1(psk, strlen(psk), (const unsigned char *)ssid, strlen(ssid), 4096, 32, (unsigned char *)pmk); //printf("PMK from %s:%s -- %02X %02X %02X %02X ... %02X%02X%02X%02X\r\n", // ssid, psk, // pmk[0], pmk[1], pmk[2], pmk[3], pmk[28], pmk[29], pmk[30], pmk[31]); }
ptk生成算法:
void makeptk(UINT8 *apmac, UINT8 *cmac, UINT8 *anonce, UINT8 *snonce, char *pmk, unsigned char *ptk) { UINT8 i; UINT8 R[100]; int offset=sizeof("Pairwise key expansion"); memset(R, 0, 100); memcpy(R, "Pairwise key expansion", offset); /* Min(AA, SPA) || Max(AA, SPA) */ if (memcmp(cmac, apmac, MAC_ADDR_LEN) < 0) { memcpy(R + offset, cmac, MAC_ADDR_LEN); memcpy(R + offset+MAC_ADDR_LEN, apmac, MAC_ADDR_LEN); } else { memcpy(R + offset, apmac, MAC_ADDR_LEN); memcpy(R + offset+MAC_ADDR_LEN, cmac, MAC_ADDR_LEN); } offset+=MAC_ADDR_LEN*2; /* Min(ANonce,SNonce) || Max(ANonce,SNonce) */ if( memcmp(snonce, anonce, 32) < 0 ) { memcpy(R + offset, snonce, 32); memcpy(R + offset + 32, anonce, 32); } else { memcpy(R + offset, anonce, 32); memcpy(R + offset + 32, snonce, 32); } offset+=32*2; for(i = 0; i < 4; i++) { R[offset] = i; HMAC(EVP_sha1(), pmk, 32, R, 100, ptk + i * 20, 0); } }
解密生成gtk:
UINT8 AES_unwrap(unsigned char *kek, UINT16 key_len, char *cipher_text, UINT16 cipher_len, UINT8 *output) { UINT8 a[8], b[16]; UINT8 *r; char *c; UINT16 i, j, n; AES_KEY ctx; if (! kek || cipher_len < 16 || ! cipher_text || ! output) { /* We don't do anything with the return value */ return 1; } /* Initialize variables */ n = (cipher_len/8)-1; /* the algorithm works on 64-bits at a time */ memcpy(a, cipher_text, 8); r = output; c = cipher_text; memcpy(r, c+8, cipher_len - 8); /* Compute intermediate values */ for (j=5; j >= 0; --j) { r = output + (n - 1) * 8; /* DEBUG_DUMP("r1", (r-8), 8); */ /* DEBUG_DUMP("r2", r, 8); */ for (i = n; i >= 1; --i) { UINT16 t = (n*j) + i; /* DEBUG_DUMP("a", a, 8); */ memcpy(b, a, 8); b[7] ^= t; /* DEBUG_DUMP("a plus t", b, 8); */ memcpy(b+8, r, 8); AES_set_decrypt_key(kek, 128, &ctx); AES_decrypt(b, b, &ctx); /* NOTE: we are using the same src and dst buffer. It's ok. */ /* DEBUG_DUMP("aes decrypt", b, 16) */ memcpy(a,b,8); memcpy(r, b+8, 8); r -= 8; } } /* DEBUG_DUMP("a", a, 8); */ /* DEBUG_DUMP("output", output, cipher_len - 8); */ return 0; } void decryptgtk(eapol_data *pdata, unsigned char* ptk) { UINT16 key_bytes_len = 0; UINT8 key_version; unsigned char *decryption_key = ptk + 16; key_version = pdata->kif_desp; if (key_version == WPA_KEY_VER_NOT_CCMP) { /* TKIP */ key_bytes_len = pdata->it_eapollen; } else if (key_version == WPA_KEY_VER_AES_CCMP) { /* AES */ key_bytes_len = pdata->it_eapolwpakeydatalen; } if (key_bytes_len > TKIP_GROUP_KEYBYTES_LEN_MAX || key_bytes_len == 0) { return; } if (key_version == WPA_KEY_VER_NOT_CCMP) { UINT8 new_key[32]; memcpy(new_key, pdata->it_eapolkeyiv, 16); memcpy(new_key+16, decryption_key, 16); UINT8 dummy[256]; rc4_state_struct rc4_state; crypt_rc4_init(&rc4_state, new_key, sizeof(new_key)); crypt_rc4(&rc4_state, dummy, 256); crypt_rc4(&rc4_state, (unsigned char *)pdata->it_wpadata, key_bytes_len); } else if (key_version == WPA_KEY_VER_AES_CCMP) { UINT8 key_found; UINT16 key_index; UINT8 *decrypted_data; decrypted_data = (UINT8 *)malloc(key_bytes_len); AES_unwrap(decryption_key, 16, pdata->it_wpadata, key_bytes_len, decrypted_data); key_found = 0; key_index = 0; while(key_index < key_bytes_len && !key_found) { UINT8 rsn_id; /* Get RSN ID */ rsn_id = decrypted_data[key_index]; if (rsn_id != 0xdd) key_index += decrypted_data[key_index+1]+2; else key_found = 1; } if (key_found) { memcpy(pdata->it_wpadata, decrypted_data+key_index+8, key_bytes_len-key_index-8); } free(decrypted_data); } memset(ptk, 0, 80); memcpy(ptk+32, pdata->it_wpadata, key_bytes_len); }
解密数据包这里,目前完成了WPA2的算法:
#define XOR_BLOCK(b, a, len) \ {\ int __i__;\ for (__i__ = 0; __i__ < (int)(len); __i__++)\ (b)[__i__] ^= (a)[__i__];\ } #define CCMP_DECRYPT(_i, _b, _b0, _pos, _a, _len) {\ /* Decrypt, with counter */ \ _b0[14] = (UINT8)((_i >> 8) & 0xff); \ _b0[15] = (UINT8)(_i & 0xff); \ AES_encrypt(_b0, _b, &key); \ XOR_BLOCK(_pos, _b, _len);\ /* Authentication */\ XOR_BLOCK(_a, _pos, _len);\ AES_encrypt(_a, _a, &key);\ } void ccmp_init_blocks(AES_KEY *ctx, qos_data *pdata, size_t dlen, UINT8 b0[AES_BLOCK_LEN], UINT8 aad[2 * AES_BLOCK_LEN], UINT8 a[AES_BLOCK_LEN], UINT8 b[AES_BLOCK_LEN]) { memset(aad, 0, 2*AES_BLOCK_LEN); /* CCM Initial Block: * Flag (Include authentication header, M=3 (8-octet MIC), * L=1 (2-octet Dlen)) * Nonce: 0x00 | A2 | PN * Dlen */ b0[0] = 0x59; /* NB: b0[1] set below */ memcpy(b0 + 2, pdata->header.it_sa, MAC_ADDR_LEN); b0[8] = pdata->it_ivp[7]; b0[9] = pdata->it_ivp[6]; b0[10] = pdata->it_ivp[5]; b0[11] = pdata->it_ivp[4]; b0[12] = pdata->it_ivp[1]; b0[13] = pdata->it_ivp[0]; b0[14] = (UINT8)((UINT8)(dlen >> 8) & 0xff); b0[15] = (UINT8)(dlen & 0xff); /* AAD: * FC with bits 4..6 and 11..13 masked to zero; 14 is always one * A1 | A2 | A3 * SC with bits 4..15 (seq#) masked to zero * A4 (if present) * QC (if present) */ aad[0] = 0; /* AAD length >> 8 */ /* NB: aad[1] set below */ UINT8 fc[2]; memcpy(fc, &(pdata->header.control), 2); aad[2] = (UINT8)(fc[0] & 0x8f); /* XXX magic #s */ aad[3] = (UINT8)(fc[1] & 0xc7); /* XXX magic #s */ /* NB: we know 3 addresses are contiguous */ memcpy(aad + 4, pdata->header.it_da, 3 * MAC_ADDR_LEN); aad[22] = (UINT8)(pdata->header.it_seq[0] & 0x0f); aad[23] = 0; /* all bits masked */ /* * Construct variable-length portion of AAD based * on whether this is a 4-address frame/QOS frame. * We always zero-pad to 32 bytes before running it * through the cipher. * * We also fill in the priority bits of the CCM * initial block as we know whether or not we have * a QOS frame. */ aad[24] = (UINT8)(pdata->it_qosctrl[0] & 0x0f); /* just priority bits */ aad[25] = 0; b0[1] = aad[24]; aad[1] = 22 + 2; memset(&aad[26], 0, 4); /* Start with the first block and AAD */ AES_encrypt(b0, a, ctx); XOR_BLOCK(a, aad, AES_BLOCK_LEN); AES_encrypt(a, a, ctx); XOR_BLOCK(a, &aad[AES_BLOCK_LEN], AES_BLOCK_LEN); AES_encrypt(a, a, ctx); b0[0] &= 0x07; b0[14] = b0[15] = 0; AES_encrypt(b0, b, ctx); /** //XOR( m + len - 8, b, 8 ); **/ } UINT8 ccmpdecryptqosdata(qos_data *pdata, UINT16 qosdatalen, const unsigned char *tk/*16 bytes*/) { UINT8 aad[2 * AES_BLOCK_LEN]; UINT8 b0[AES_BLOCK_LEN], b[AES_BLOCK_LEN], a[AES_BLOCK_LEN]; UINT8 mic[AES_BLOCK_LEN]; UINT8 *pos; UINT32 i; size_t data_len; AES_KEY key; AES_set_encrypt_key(tk, 128, &key); data_len = qosdatalen - sizeof(qos_data) - CCMP_TRAILER_LEN; if (data_len < 1) return 0; ccmp_init_blocks(&key, pdata, data_len, b0, aad, a, b); memcpy(mic, (UINT8 *)pdata + qosdatalen - CCMP_TRAILER_LEN, CCMP_TRAILER_LEN); XOR_BLOCK(mic, b, CCMP_TRAILER_LEN); i = 1; pos = (UINT8 *)pdata->it_buf; while (data_len >= AES_BLOCK_LEN) { CCMP_DECRYPT(i, b, b0, pos, a, AES_BLOCK_LEN); pos += AES_BLOCK_LEN; data_len -= AES_BLOCK_LEN; i++; } if (data_len != 0) /* short last block */ CCMP_DECRYPT(i, b, b0, pos, a, data_len); /*MIC Key ?= MIC*/ if (memcmp(mic, a, CCMP_TRAILER_LEN) == 0) { return 1; } /* TODO replay check(IEEE 802.11i-2004, pg. 62)*/ /* TODO PN must be incremental (IEEE 802.11i-2004, pg. 62)*/ return 0; }
总结:
解密的关键就是gtk,gtk是用ptk解密的,ptk使用pmk生成的,pmk使用ssid和pass生成的,所以要想解密,必须需要以下几个信息:
1.ssid和对应的密码
2.登录验证的四次eapol包中的a-nonce和s-nonce,以及加密算法区分WAP或者WPA2。
只要有了这几个信息,解密就是分分钟的事情了WPADecrypt.exe。
更多详细技术讨论,联系博主q 345 3824 62。
留言列表: