无线网络越来越普及,从此涉及到的业务也出现了多个。从安全圈子来讲,破解、劫持、嗅探等等都开始玩的不亦乐乎。本篇文章详细说明有了数据包如何还原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。
2015/9/29 | Tags:WAP,WPA2,WIFI解密,EAPOL,WPADecrypt,WPA2 Decrypt,C | C/C++代码 | 查看评论(1)