全网首一份!你最需要的PPTP MS-CHAP V2 挑战响应编程模拟计算教程!代码基于RFC2759,附全部源码!
本文基于网络密码课上的实验
本来想水一水就过去,代码就网上找找,不行就GPT写,但是!一份都找不到,找到的代码都是跑不了的,总会是就是乱七八糟。所以准备认真的写一份。
代码编译成功的前提是要预先装好openssl库!
本随笔主要有三个内容:
- 编写程序,模拟计算NTResponse、AuthenticatorResponse,
- 根据前期PPTP实验中捕获的数据包中CHAP协议的挑战响应认证数据,在未知用户口令情况下编程实现CHAP认证口令的破解
在单向数据条件下(仅能截获用户数据)实现CHAP认证口令的破解
首先放一个我自己抓的包,可以看到,这是chap协议挑战响应的三次握手,
那么我们继续进行,编程模拟,就要先搞清楚每个字段代表的什么,文档中第一个包的描述,给的是Authenticator challenge
也就是我住的第一个包里的value
这是第二个包,16字节peer-challenge,8位的0,24位的NT-Response
value内的值,对应看
第三个包,内容是s=authticator-response
接下来我们开始编程实现,每一个字段都是由对应的函数计算得出
一、编写程序,模拟计算NTResponse、AuthenticatorResponse
1.查阅RFC2759文档,找到描述的计算NTResponse的函数
NT-Response的值是由GenerateNTResponse()计算得出,看到该函数有四个输入,分别是AuthenticatorChallenge(16)、PeerChallenge(16)、UserName和Password,一个输出Response(24)
此外,有三个函数对输入进行处理:ChallengeHash:对两个挑战值hash,结果放到challenge中。NtPasswordHash:对password做hash,结果放到password中。ChallengeResponse:对challenge和passwordhash做运算,结果得到NT-Response。
2.根据文档描述编写代码
GenerateNTResponse()函数
1 void GenerateNTResponse(const HCRYPTPROV hProv, const BYTE* auth_challenge, const BYTE* peer_challenge, const char* user_name, const wchar_t* password, BYTE* response) 2 { 3 BYTE challenge[8]; 4 BYTE password_hash[16]; 5 6 _ChallengeHash(hProv, peer_challenge, auth_challenge, user_name, challenge); 7 _NtPasswordHash(hProv, password, password_hash); 8 _ChallengeResponse(hProv, challenge, password_hash, response); 9 }
_ChallengeHash()函数,对peer challenge、auth challenge、user name连接然后进行sha1哈希,结果放到challenge中返回
1 void _ChallengeHash(const HCRYPTPROV hProv, const BYTE* peer_challenge, const BYTE* auth_challenge, const char* user_name, BYTE* challenge) 2 { 3 HCRYPTHASH hHash = 0; 4 if (!CryptCreateHash(hProv, CALG_SHA1, 0, 0, &hHash)) 5 throw "CryptCreateHash failed (SHA1)"; 6 if (!CryptHashData(hHash, peer_challenge, 16, 0)) 7 throw "CryptHashData failed (peer challenge)"; 8 if (!CryptHashData(hHash, auth_challenge, 16, 0)) 9 throw "CryptHashData failed (auth challenge)"; 10 if (!CryptHashData(hHash, (const BYTE*)user_name, 11 (DWORD)strlen(user_name), 0)) 12 throw "CryptHashData failed (user name)"; 13 DWORD hash_len = SHA1LEN; 14 BYTE hash_buffer[SHA1LEN]; 15 if (!CryptGetHashParam(hHash, HP_HASHVAL, hash_buffer, 16 &hash_len, 0)) 17 throw "CryptGetHashParam failed (challenge hash)"; 18 memcpy(challenge, hash_buffer, 8); 19 20 }
_NtPasswordHash函数():将password做MD4哈希,然后返回password_hash中
1 void _NtPasswordHash(const HCRYPTPROV hProv, const wchar_t* password, BYTE* password_hash) 2 { 3 HCRYPTHASH hHash = 0; 4 if (!CryptCreateHash(hProv, CALG_MD4, 0, 0, &hHash)) 5 throw "CryptCreateHash failed (MD4)"; 6 if (!CryptHashData(hHash, (const BYTE*)password, lstrlenW(password) << 1, 0)) 7 throw "CryptHashData failed (user password)"; 8 DWORD hash_len = MD4LEN; 9 BYTE hash_buffer[MD4LEN]; 10 if (!CryptGetHashParam(hHash, HP_HASHVAL, hash_buffer, &hash_len, 0)) 11 throw "CryptGetHashParam failed (NT password hash)"; 12 memcpy(password_hash, hash_buffer, 16); 13 }
ChallengeResponse()函数:首先将16位passwordhash后面填充五个0变成21位,接着分成三个长度为7的密钥,分别对challenge做三次DES加密,三次加密结果放到response中
1 void _ChallengeResponse(const HCRYPTPROV hProv, const BYTE* challenge, const BYTE* password_hash, BYTE* response) 2 { 3 BYTE z_password_hash[21]; 4 memset(z_password_hash, 0, 21); 5 memcpy(z_password_hash, password_hash, 16); 6 7 _DesEncrypt(hProv, challenge, z_password_hash, response); 8 _DesEncrypt(hProv, challenge, z_password_hash + 7, response + 8); 9 _DesEncrypt(hProv, challenge, z_password_hash + 14, response + 16); 10 }
接下来我们来看DES加密函数,由于每次给的密钥都是7位长度,所以加密前首先调用EXPAND()函数对密钥进行扩展,扩展到8位,这里的加密模式是ECB(电子密码本)加密,加密的结果放到result中
1 typedef struct { 2 BLOBHEADER key_header; 3 DWORD key_length; 4 BYTE key_data[8]; 5 } DESKey; 6 7 void EXPAND(BYTE* key); 8 9 void _DesEncrypt(const HCRYPTPROV hProv, const BYTE* data, const BYTE* key, BYTE* result) 10 { 11 // Fill CryptoAPI-required key structure 12 DESKey des_key; 13 des_key.key_header.bType = PLAINTEXTKEYBLOB; 14 des_key.key_header.bVersion = CUR_BLOB_VERSION; 15 des_key.key_header.reserved = 0; 16 des_key.key_header.aiKeyAlg = CALG_DES; 17 des_key.key_length = 8; 18 memcpy(des_key.key_data, key, 7); 19 EXPAND(des_key.key_data); 20 21 HCRYPTKEY hKey; 22 // import key BLOB 23 if (!CryptImportKey(hProv, (BYTE*)&des_key, 24 sizeof(des_key), 0, 0, &hKey)) 25 throw "CryptImportKey failed"; 26 // set ECB mode required by RFC 27 DWORD des_mode = CRYPT_MODE_ECB; 28 if (!CryptSetKeyParam(hKey, KP_MODE, (BYTE*)&des_mode, 0)) 29 throw "CryptSetKeyParam failed (ECB mode)"; 30 // set initialization vector 31 BYTE IV[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; 32 if (!CryptSetKeyParam(hKey, KP_IV, &IV[0], 0)) 33 throw "CryptSetKeyParam failed (init vector)"; 34 // encrypt 35 DWORD data_len = 8; 36 memcpy(result, data, 8); // encrypt in-place 37 if (!CryptEncrypt(hKey, 0, FALSE, 0, (BYTE*)&result[0], &data_len, 8)) 38 throw "CryptEncrypt failed (DES)"; 39 } 40
下面来看一下密钥扩展函数,该函数的具体流程如下:
- 创建了一个名为new_key的字节数组,用于存储扩展后的密钥。
- 接下来,使用一个循环来处理每个7位的数据包。循环从0到7迭代,共8次。
- 在每次迭代中,根据当前的索引值i,获取原始密钥中的两个相邻的八位字节。如果i大于0,则取索引为i-1的字节作为左字节;否则,取索引为0的字节作为左字节。右字节的获取方式类似,如果i小于7,则取索引为i的字节作为右字节;否则,取索引为6的字节作为右字节。
- 接着,通过位运算将左字节和右字节中的位进行清除,只保留当前7位的数据。左字节通过与操作符&和右移操作符>>实现,右字节通过与操作符&和左移操作符<<实现。
- 然后,将左字节和右字节进行位移操作,将它们移动到最终的位置。左字节通过左移操作符<<实现,右字节通过右移操作符>>实现。
- 最后,将左字节和右字节进行按位或操作,得到最终的八位字节,并将其存储在new_key数组中对应的位置。
1 void EXPAND(BYTE* key) 2 { 3 BYTE left_octet, right_octet; 4 BYTE new_key[8]; 5 // split original key into eight 7-bit packs: 6 // 00000001 11111122 22222333 3333444 44455555 55666666 67777777 7 for (int i = 0; i < 8; i++) 8 { 9 // fetch two adjacent octets of key containing current 7 bits 10 left_octet = key[i > 0 ? i - 1 : 0]; 11 right_octet = key[i < 7 ? i : 6]; 12 // clear all bits except current 7 ones 13 left_octet &= 0xFF >> (8 - i); 14 right_octet &= 0xFF << (i + 1); 15 // shift bits to their final position 16 left_octet = left_octet << (8 - i); 17 right_octet = right_octet >> i; 18 // combine into resulting octet 19 new_key[i] = left_octet | right_octet; 20 } 21 memcpy(key, new_key, 8); 22 }
至此,我们就算出来了NtResponse
2.计算AunthenticatorResponse
首先查看文档,得知输入有Password、NT-Response、PeerChallenge、AuthenticatorChallenge、UserName
输出AuthenticatorResponse。该函数处理流程如下:
- 该函数里面定义了长度16的PasswordHash和PasswordHashHash,长度为8的Challenge。首先,使用MD4算法对密码进行哈希处理,得到PasswordHash。
- 然后,对PasswordHash进行再次哈希处理,得到PasswordHashHash。
- 接着,使用SHA算法对PasswordHashHash、NT-Response和Magic1进行哈希处理,得到Digest。
- 使用ChallengeHash函数对PeerChallenge、AuthenticatorChallenge和UserName进行哈希处理,得到Challenge。
- 再次使用SHA算法对Digest、Challenge和Magic2进行哈希处理,得到最终的Digest
下面是具体实现代码
1 void GenerateAuthenticatorResponse(const HCRYPTPROV hProv, const wchar_t* password_unicode, const BYTE* NTResponse, const BYTE* PeerChalleng, const BYTE* AuthenticatorChallenge, const char* UserName) 2 { 3 BYTE Magic1[39] = { 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76, 4 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65, 5 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67, 6 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74 }; 7 BYTE Magic2[41] = { 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B, 8 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F, 9 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E, 10 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F, 11 0x6E }; 12 BYTE Passwordhash[16]; 13 _NtPasswordHash(hProv, password_unicode, Passwordhash); 14 BYTE PasswordHashhash[16]; 15 _NtPasswordHashHash(hProv, Passwordhash, PasswordHashhash); 16 SHA_CTX Context; 17 SHA1_Init(&Context); 18 SHA1_Update(&Context, PasswordHashhash, 16); 19 SHA1_Update(&Context, NTResponse, 24); 20 SHA1_Update(&Context, Magic1, 39); 21 unsigned char aa[SHA_DIGEST_LENGTH]; 22 SHA1_Final(aa, &Context); 23 BYTE Challenge[8]; 24 _ChallengeHash(hProv, PeerChalleng, AuthenticatorChallenge, UserName, Challenge); 25 SHA1_Init(&Context); 26 SHA1_Update(&Context, aa, sizeof(aa)); 27 SHA1_Update(&Context, Challenge, 8); 28 SHA1_Update(&Context, Magic2, 41); 29 unsigned char bb[SHA_DIGEST_LENGTH]; 30 SHA1_Final(bb, &Context); 31 cout << endl << "AuthenticatorResponse: s="; 32 for (int i = 0; i < 20; i++) 33 { 34 printf("%02X", (unsigned char)bb[i]); 35 } 36 }
3.运行程序,将结果和wireshark抓包内容进行比对
NT-Response:DA2191E86678231E62B5D628CBA859031B1E6082533B32B5
AuthenticatorResponse: s=2AA71CBBDD95F43ABA628329A0271A8DD1114310
全部代码如下:注意展开
1 #pragma comment(lib, "libssl.lib") 2 #pragma comment(lib, "libcrypto.lib") 3 #include <stdio.h> 4 #include <fstream> 5 #include <tchar.h> 6 #include <windows.h> 7 #include <WinCrypt.h> 8 #include <openssl/sha.h> 9 #include<string> 10 #include<iostream> 11 #include <openssl/des.h> 12 using namespace std; 13 14 void print_array(const BYTE* array, DWORD length); 15 void GenerateNTResponse(const HCRYPTPROV hProv, const BYTE* auth_challenge, const BYTE* peer_challenge, const char* user_name, const wchar_t* password, BYTE* response); 16 void GenerateAuthenticatorResponse(const HCRYPTPROV hProv, const wchar_t* password_unicode, const BYTE* NTResponse, const BYTE* PeerChalleng, const BYTE* AuthenticatorChallenge, const char* UserName); 17 void Challenge1(const HCRYPTPROV hProv, const BYTE* auth_challenge, const BYTE* peer_challenge, const char* user_name, BYTE* response); 18 void _NtPasswordHash(const HCRYPTPROV hProv, const wchar_t* password, BYTE* password_hash); 19 BYTE nt_response[24]; 20 int main() 21 { 22 const char* user_name = "WA_042"; 23 const wchar_t* password = L"123456"; 24 const BYTE auth_challenge[] = { 0x11,0xD8,0x2A,0x82,0x0F, 25 0xBE,0xB0,0x30,0x73,0xB1,0xF7, 26 0x03,0x73,0x22,0x26,0xBF }; 27 const BYTE peer_challenge[] = { 0xC1,0xD4,0x43,0x9D,0xBD,0x65, 28 0xA2,0x74,0xB9,0x7A,0xF0,0x3F, 29 0x98,0x00,0x6D,0x75 }; 30 HCRYPTPROV hProv = 0; 31 32 CryptAcquireContext(&hProv, NULL, MS_DEF_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT); 33 //wcout << password; 34 printf("User name: %s\n", user_name); 35 wprintf(L"User password: %s\n", password); 36 printf("Authenticator challenge: "); 37 print_array(auth_challenge, 16); 38 printf("\n"); 39 printf("Peer challenge: "); print_array(peer_challenge, 16); 40 printf("\n"); 41 GenerateNTResponse(hProv, auth_challenge, peer_challenge, user_name, password, nt_response); 42 printf("NT Response: "); print_array(nt_response, 24); 43 GenerateAuthenticatorResponse(hProv, password, nt_response, peer_challenge, auth_challenge, user_name); 44 cout << endl; 45 Challenge1(hProv, auth_challenge, peer_challenge, user_name, nt_response); 46 cout << endl << "挑战一结束"; 47 return 0; 48 } 49 50 void Challenge1(const HCRYPTPROV hProv, const BYTE* auth_challenge, const BYTE* peer_challenge, const char* user_name, BYTE* response) 51 { 52 std::ifstream file("passlib.txt"); 53 if (!file) { 54 std::cerr << "无法打开文件" << std::endl; 55 } 56 57 string line; 58 while (std::getline(file, line)) { 59 BYTE Nt_response[24]; 60 wchar_t* wc = new wchar_t[line.size()]; 61 swprintf(wc, 100, L"%S", line.c_str()); 62 // wcout << wc << endl; 63 GenerateNTResponse(hProv, auth_challenge, peer_challenge, user_name, wc, Nt_response); 64 //cout << Nt_response << endl; 65 for (int i = 0; i < 24; i++) 66 { 67 68 if (Nt_response[i] != nt_response[i]) 69 { 70 cout << "破解失败" << endl; 71 break; 72 } 73 else 74 { 75 cout << "破解成功!密码为:"; 76 wcout << wc << endl; 77 break; 78 } 79 // 处理读取到的数据 80 } 81 } 82 83 file.close(); 84 85 86 } 87 void _DesDecrypt(unsigned char* NTResponse, unsigned char* key, unsigned char* decrypted_result); 88 89 const CHAR hex_digits[] = "0123456789ABCDEF"; 90 91 void print_array(const BYTE* array, DWORD length) 92 { 93 printf("0x"); 94 for (DWORD i = 0; i < length; i++) 95 printf("%c%c", hex_digits[array[i] >> 4], 96 hex_digits[array[i] & 0xf]); 97 } 98 99 100 void _ChallengeHash(const HCRYPTPROV hProv, const BYTE* peer_challenge, const BYTE* auth_challenge, const char* user_name, BYTE* challenge); 101 void _NtPasswordHash(const HCRYPTPROV hProv, const wchar_t* password, BYTE* password_hash); 102 void _ChallengeResponse(const HCRYPTPROV hProv, const BYTE* challenge, const BYTE* password_hash, BYTE* response); 103 void _NtPasswordHashHash(const HCRYPTPROV hProv, BYTE* password, BYTE* password_hash); 104 void GenerateNTResponse(const HCRYPTPROV hProv, const BYTE* auth_challenge, const BYTE* peer_challenge, const char* user_name, const wchar_t* password, BYTE* response) 105 { 106 BYTE challenge[8]; 107 BYTE password_hash[16]; 108 109 _ChallengeHash(hProv, peer_challenge, auth_challenge, user_name, challenge); 110 _NtPasswordHash(hProv, password, password_hash); 111 _ChallengeResponse(hProv, challenge, password_hash, response); 112 } 113 void GenerateAuthenticatorResponse(const HCRYPTPROV hProv, const wchar_t* password_unicode, const BYTE* NTResponse, const BYTE* PeerChalleng, const BYTE* AuthenticatorChallenge, const char* UserName) 114 { 115 BYTE Magic1[39] = { 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76, 116 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65, 117 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67, 118 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74 }; 119 BYTE Magic2[41] = { 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B, 120 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F, 121 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E, 122 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F, 123 0x6E }; 124 BYTE Passwordhash[16]; 125 _NtPasswordHash(hProv, password_unicode, Passwordhash); 126 BYTE PasswordHashhash[16]; 127 _NtPasswordHashHash(hProv, Passwordhash, PasswordHashhash); 128 SHA_CTX Context; 129 SHA1_Init(&Context); 130 SHA1_Update(&Context, PasswordHashhash, 16); 131 SHA1_Update(&Context, NTResponse, 24); 132 SHA1_Update(&Context, Magic1, 39); 133 unsigned char aa[SHA_DIGEST_LENGTH]; 134 SHA1_Final(aa, &Context); 135 BYTE Challenge[8]; 136 _ChallengeHash(hProv, PeerChalleng, AuthenticatorChallenge, UserName, Challenge); 137 SHA1_Init(&Context); 138 SHA1_Update(&Context, aa, sizeof(aa)); 139 SHA1_Update(&Context, Challenge, 8); 140 SHA1_Update(&Context, Magic2, 41); 141 unsigned char bb[SHA_DIGEST_LENGTH]; 142 SHA1_Final(bb, &Context); 143 cout << endl << "AuthenticatorResponse: s="; 144 for (int i = 0; i < 20; i++) 145 { 146 printf("%02X", (unsigned char)bb[i]); 147 } 148 } 149 #define SHA1LEN 20 150 151 void _ChallengeHash(const HCRYPTPROV hProv, const BYTE* peer_challenge, const BYTE* auth_challenge, const char* user_name, BYTE* challenge) 152 { 153 HCRYPTHASH hHash = 0; 154 if (!CryptCreateHash(hProv, CALG_SHA1, 0, 0, &hHash)) 155 throw "CryptCreateHash failed (SHA1)"; 156 if (!CryptHashData(hHash, peer_challenge, 16, 0)) 157 throw "CryptHashData failed (peer challenge)"; 158 if (!CryptHashData(hHash, auth_challenge, 16, 0)) 159 throw "CryptHashData failed (auth challenge)"; 160 if (!CryptHashData(hHash, (const BYTE*)user_name, 161 (DWORD)strlen(user_name), 0)) 162 throw "CryptHashData failed (user name)"; 163 DWORD hash_len = SHA1LEN; 164 BYTE hash_buffer[SHA1LEN]; 165 if (!CryptGetHashParam(hHash, HP_HASHVAL, hash_buffer, 166 &hash_len, 0)) 167 throw "CryptGetHashParam failed (challenge hash)"; 168 memcpy(challenge, hash_buffer, 8); 169 170 } 171 172 173 #define MD4LEN 16 174 175 void _NtPasswordHash(const HCRYPTPROV hProv, const wchar_t* password, BYTE* password_hash) 176 { 177 HCRYPTHASH hHash = 0; 178 if (!CryptCreateHash(hProv, CALG_MD4, 0, 0, &hHash)) 179 throw "CryptCreateHash failed (MD4)"; 180 if (!CryptHashData(hHash, (const BYTE*)password, lstrlenW(password) << 1, 0)) 181 throw "CryptHashData failed (user password)"; 182 DWORD hash_len = MD4LEN; 183 BYTE hash_buffer[MD4LEN]; 184 if (!CryptGetHashParam(hHash, HP_HASHVAL, hash_buffer, &hash_len, 0)) 185 throw "CryptGetHashParam failed (NT password hash)"; 186 memcpy(password_hash, hash_buffer, 16); 187 } 188 void _NtPasswordHashHash(const HCRYPTPROV hProv, BYTE* passwordhash, BYTE* password_hashhash) 189 { 190 HCRYPTHASH hHashhash = 0; 191 if (!CryptCreateHash(hProv, CALG_MD4, 0, 0, &hHashhash)) 192 throw "CryptCreateHash failed (MD4)"; 193 if (!CryptHashData(hHashhash, passwordhash, 16, 0)) 194 throw "CryptHashData failed (user password)"; 195 DWORD hash_len = MD4LEN; 196 BYTE hash_buffer[MD4LEN]; 197 if (!CryptGetHashParam(hHashhash, HP_HASHVAL, hash_buffer, &hash_len, 0)) 198 throw "CryptGetHashParam failed (NT password hash)"; 199 200 memcpy(password_hashhash, hash_buffer, 16); 201 //cout << endl; print_array(password_hashhash, 16); 202 } 203 204 void _DesEncrypt(const HCRYPTPROV hProv, const BYTE* data, const BYTE* key, BYTE* result); 205 void _ChallengeResponse(const HCRYPTPROV hProv, const BYTE* challenge, const BYTE* password_hash, BYTE* response) 206 { 207 BYTE z_password_hash[21]; 208 memset(z_password_hash, 0, 21); 209 memcpy(z_password_hash, password_hash, 16); 210 211 _DesEncrypt(hProv, challenge, z_password_hash, response); 212 _DesEncrypt(hProv, challenge, z_password_hash + 7, response + 8); 213 _DesEncrypt(hProv, challenge, z_password_hash + 14, response + 16); 214 } 215 typedef struct { 216 BLOBHEADER key_header; 217 DWORD key_length; 218 BYTE key_data[8]; 219 } DESKey; 220 221 void EXPAND(BYTE* key); 222 223 void _DesEncrypt(const HCRYPTPROV hProv, const BYTE* data, const BYTE* key, BYTE* result) 224 { 225 // Fill CryptoAPI-required key structure 226 DESKey des_key; 227 des_key.key_header.bType = PLAINTEXTKEYBLOB; 228 des_key.key_header.bVersion = CUR_BLOB_VERSION; 229 des_key.key_header.reserved = 0; 230 des_key.key_header.aiKeyAlg = CALG_DES; 231 des_key.key_length = 8; 232 memcpy(des_key.key_data, key, 7); 233 EXPAND(des_key.key_data); 234 235 HCRYPTKEY hKey; 236 // import key BLOB 237 if (!CryptImportKey(hProv, (BYTE*)&des_key, 238 sizeof(des_key), 0, 0, &hKey)) 239 throw "CryptImportKey failed"; 240 // set ECB mode required by RFC 241 DWORD des_mode = CRYPT_MODE_ECB; 242 if (!CryptSetKeyParam(hKey, KP_MODE, (BYTE*)&des_mode, 0)) 243 throw "CryptSetKeyParam failed (ECB mode)"; 244 // set initialization vector 245 BYTE IV[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; 246 if (!CryptSetKeyParam(hKey, KP_IV, &IV[0], 0)) 247 throw "CryptSetKeyParam failed (init vector)"; 248 // encrypt 249 DWORD data_len = 8; 250 memcpy(result, data, 8); // encrypt in-place 251 if (!CryptEncrypt(hKey, 0, FALSE, 0, (BYTE*)&result[0], &data_len, 8)) 252 throw "CryptEncrypt failed (DES)"; 253 } 254 255 void EXPAND(BYTE* key) 256 { 257 BYTE left_octet, right_octet; 258 BYTE new_key[8]; 259 // split original key into eight 7-bit packs: 260 // 00000001 11111122 22222333 3333444 44455555 55666666 67777777 261 for (int i = 0; i < 8; i++) 262 { 263 // fetch two adjacent octets of key containing current 7 bits 264 left_octet = key[i > 0 ? i - 1 : 0]; 265 right_octet = key[i < 7 ? i : 6]; 266 // clear all bits except current 7 ones 267 left_octet &= 0xFF >> (8 - i); 268 right_octet &= 0xFF << (i + 1); 269 // shift bits to their final position 270 left_octet = left_octet << (8 - i); 271 right_octet = right_octet >> i; 272 // combine into resulting octet 273 new_key[i] = left_octet | right_octet; 274 } 275 memcpy(key, new_key, 8); 276 }
二、根据前期PPTP实验中捕获的数据包中CHAP协议的挑战响应认证数据,在未知用户口令情况下编程实现CHAP认证口令的破解
思路:编写一个challenge1()函数,创建一个自己的字典,使用字典进行爆破,每次读入字典中的一个密码文本,然后调用GenerateNTResponse()函数计算NT-Response,与正确的进行比对,如果成功,则输出密码
1.编写challenge1()函数
1 void Challenge1(const HCRYPTPROV hProv, const BYTE* auth_challenge, const BYTE* peer_challenge, const char* user_name, BYTE* response) 2 { 3 std::ifstream file("passlib.txt"); 4 if (!file) { 5 std::cerr << "无法打开文件" << std::endl; 6 } 7 8 string line; 9 while (std::getline(file, line)) { 10 BYTE Nt_response[24]; 11 wchar_t* wc = new wchar_t[line.size()]; 12 swprintf(wc, 100, L"%S", line.c_str()); 13 // wcout << wc << endl; 14 GenerateNTResponse(hProv, auth_challenge, peer_challenge, user_name, wc, Nt_response); 15 //cout << Nt_response << endl; 16 for (int i = 0; i < 24; i++) 17 { 18 19 if (Nt_response[i] != nt_response[i]) 20 { 21 cout << "破解失败" << endl; 22 break; 23 } 24 else 25 { 26 cout << "破解成功!密码为:"; 27 wcout << wc << endl; 28 break; 29 } 30 // 处理读取到的数据 31 } 32 } 33 34 file.close(); 35 36 37 }
运行字典爆破
三、在单向数据条件下(仅能截获用户数据)实现CHAP认证口令的破解
思路:单向数据条件下,意味着只能获取用户数据NT-Response、PeerChallenge、UserName。那么根据NtResponse的计算原理,采用逆向破解的思路。首先:由于NtResponse是由passwordhash分成三部分作密钥对challenge加密的密文连接得到,那么先将NtResponse拆分成三部分,对应每部分passwordhash对challenge做DES加密的结果。
因此,破解思路是,每次读入字典里的密钥做hash作为密钥,然后将NtResponse三个部分分别作为DES函数的输入,记录DES函数的输出,比较三个输出是否相同,如果相同,则破解成功。
注意,我能运行成功是因为我的目录里面有passlib.txt字典文件,大家可以创建自己的字典文件
1.编写代码
源码如下,注意,已经折叠,点击展开
1 #define _CRT_SECURE_NO_WARNINGS C4996 2 #include <iostream> 3 #include <stdio.h> 4 #include <openssl/ssl.h> 5 #include <stdlib.h> 6 #include <string.h> 7 #include <openssl/sha.h> 8 #include <openssl/md4.h> 9 #include <openssl/des.h> 10 11 int readfile(char* pass[]) { 12 FILE* file = fopen("./passlib.txt", "r"); 13 if (file == NULL) { 14 perror("Error opening file"); 15 return 0; 16 } 17 18 char buffer[100]; 19 int count = 0; 20 21 while (fgets(buffer, sizeof(buffer), file) != NULL) { 22 buffer[strcspn(buffer, "\n")] = 0; 23 pass[count] = _strdup(buffer); 24 count++; 25 } 26 27 fclose(file); 28 29 30 return count; 31 } 32 33 void des_decrypt(unsigned char* key, unsigned char* NTResponse, unsigned char* decrypted_result) { 34 DES_cblock des_key; 35 memcpy(des_key, key, 8); 36 DES_key_schedule key_schedule; 37 DES_set_key_unchecked(&des_key, &key_schedule); 38 DES_ecb_encrypt((DES_cblock*)NTResponse, (DES_cblock*)decrypted_result, &key_schedule, DES_DECRYPT); 39 } 40 void expand(unsigned char* t, unsigned char* k) 41 { 42 k[0] = t[0] & 0xfe; 43 k[1] = (t[0] << 7 | t[1] >> 1) & 0xfe; 44 k[2] = (t[1] << 6 | t[2] >> 2) & 0xfe; 45 k[3] = (t[2] << 5 | t[3] >> 3) & 0xfe; 46 k[4] = (t[3] << 4 | t[4] >> 4) & 0xfe; 47 k[5] = (t[4] << 3 | t[5] >> 5) & 0xfe; 48 k[6] = (t[5] << 2 | t[6] >> 6) & 0xfe; 49 k[7] = (t[6] << 1) & 0xfe; 50 } 51 int main() 52 { 53 unsigned char peer_challenge[17] = { 0xC1,0xD4,0x43,0x9D,0xBD,0x65,0xA2,0x74,0xB9,0x7A,0xF0,0x3F,0x98,0x00,0x6D,0x75 ,'\0' }; 54 char username[7] = { 0x77,0x61,0x5f,0x30,0x34,0x32,'\0' }; 55 unsigned char challenge[8] = { 0x00 }; 56 unsigned char NTResponse[25] = { 0xDA ,0x21,0x91 ,0xE8 ,0x66 ,0x78 ,0x23 ,0x1E ,0x62 ,0xB5 57 ,0xD6 ,0x28 ,0xCB ,0xA8 ,0x59 ,0x03 ,0x1B ,0x1E ,0x60 ,0x82 ,0x53 ,0x3B ,0x32 ,0xB5,'\0' }; 58 59 const char* pass[100]; 60 int loop = readfile((char**)pass); 61 int has_found = 0; 62 63 unsigned char response1[9] = { 0x00 }; 64 unsigned char response2[9] = { 0x00 }; 65 unsigned char response3[9] = { 0x00 }; 66 67 strncat((char*)response1, (char*)NTResponse, 8); 68 response1[8] = '\0'; 69 strncat((char*)response2, (char*)NTResponse + 8, 8); 70 response2[8] = '\0'; 71 strncat((char*)response3, (char*)NTResponse + 16, 8); 72 response3[8] = '\0'; 73 printf("read the password in pass.txt:\n"); 74 for (int i = 0; i < loop; i++) { 75 printf("%s\n", pass[i]); 76 } 77 78 for (int j = 0; j < loop; j++) 79 { 80 81 unsigned char password_hash[21] = { 0x00 }; 82 unsigned char password[10] = { 0x00 }; 83 strncpy((char*)password, pass[j], strlen(pass[j])); 84 85 password[strlen(pass[j])] = '\0'; 86 87 int pass_len = strlen((char*)password); 88 89 unsigned char* unicode_password; 90 unicode_password = (unsigned char*)malloc(sizeof(unsigned char) * pass_len * 2); 91 for (int i = 0; i < pass_len; i++) { 92 unicode_password[i * 2] = password[i]; 93 unicode_password[i * 2 + 1] = 0; 94 } 95 96 MD4((unsigned char*)unicode_password, pass_len * sizeof(unsigned short), password_hash); 97 98 char zero[5] = { 0x00,0x00,0x00,0x00,0x00 }; 99 100 strncat((char*)password_hash, zero, 5); 101 102 unsigned char ks1[9] = { 0x00 }; 103 unsigned char ks2[9] = { 0x00 }; 104 unsigned char ks3[9] = { 0x00 }; 105 106 unsigned char pass1[8] = { 0x00 }; 107 unsigned char pass2[8] = { 0x00 }; 108 unsigned char pass3[8] = { 0x00 }; 109 110 for (int i = 0; i < 7; i++) 111 { 112 pass1[i] = password_hash[i]; 113 } 114 for (int i = 0; i < 7; i++) 115 { 116 pass2[i] = password_hash[i + 7]; 117 } 118 for (int i = 0; i < 7; i++) 119 { 120 pass3[i] = password_hash[i + 14]; 121 } 122 123 expand(pass1, ks1); 124 125 expand(pass2, ks2); 126 127 expand(pass3, ks3); 128 129 130 unsigned char result1[32] = { 0x00 }; 131 unsigned char result2[32] = { 0x00 }; 132 unsigned char result3[32] = { 0x00 }; 133 134 des_decrypt(ks1, response1, result1); 135 des_decrypt(ks2, response2, result2); 136 des_decrypt(ks3, response3, result3); 137 138 if (strcmp((char*)result1, (char*)result2) == 0) 139 { 140 if (strcmp((char*)result1, (char*)result3) == 0) 141 { 142 printf("\n"); 143 printf("Find! The password is:%s", pass[j]); 144 has_found = 1; 145 break; 146 } 147 } 148 149 } 150 for (int i = 0; i < loop; i++) { 151 free((void*)pass[i]); 152 } 153 154 if (has_found == 0) 155 printf("Cannot Find the password"); 156 }
2.运行代码
至此,实验结束
顺便附上python源码,欢迎批评指正!
1 # coding = utf-8 2 import binascii 3 from builtins import bytes 4 5 from Crypto.Hash import SHA1,SHA,MD4 6 from Crypto.Cipher import DES,ARC4 7 8 def GenerateNTResponse(AuthenticatorChallenge,PeerChalleng,UserName,password_unicode): 9 PasswordHash=Nt_password_hash(password_unicode) 10 #print_hex(PasswordHash) 11 Challenge=ChallengeHash(PeerChalleng,AuthenticatorChallenge,UserName) 12 challenge_resposn=ChallengeResponse(PasswordHash,Challenge) 13 return challenge_resposn 14 15 def ChallengeHash(PeerChalleng,AuthenticatorChallenge,UserName): 16 Challenge=SHA1.new() 17 Challenge.update(PeerChalleng) 18 Challenge.update(AuthenticatorChallenge) 19 Challenge.update(UserName) 20 return Challenge.digest()[:8] 21 def Expand(rawkey): #expand 7Bytes to 8 Bytes 22 tmp_key = [] 23 for i in rawkey[:7]: 24 tmp_key.append(i) 25 key = [] 26 for i in range(8): 27 key.append(b'\x00') 28 # ------------------------- 29 key[0] = tmp_key[0] 30 for i in range(1, 7): 31 key[i] = ((tmp_key[i - 1] << (8 - i)) & 0xff) | (tmp_key[i] >> i) 32 key[7] = (tmp_key[6] << 1) & 0xff 33 global b 34 for i in range(len(key)): 35 b = 1 36 for j in range(1, 8): 37 t = (key[i] >> j) 38 b = (t ^ b) & 0x1 # 39 key[i] = (key[i] & 0xfe) | b 40 ans = b'' 41 for i in range(8): 42 ans += bytes([key[i]]) 43 return ans 44 def ChallengeResponse(PasswordHash,Challenge): 45 zero = b'\x00' 46 while(len(PasswordHash) < 21): # important 47 PasswordHash += zero #zero-padded to 21 octets 48 res = DES.new(Expand(PasswordHash[0:7]), DES.MODE_ECB).encrypt(Challenge) 49 res += DES.new(Expand(PasswordHash[7:14]), DES.MODE_ECB).encrypt(Challenge) 50 res += DES.new(Expand(PasswordHash[14:21]), DES.MODE_ECB).encrypt(Challenge) 51 return res 52 53 def Nt_password_hash(Password): 54 PasswordHash=MD4.new(Password) 55 return PasswordHash.digest() 56 57 def Nt_password_hashhash(Passwordhash): 58 PasswordHashhash=MD4.new(Passwordhash) 59 return PasswordHashhash.digest() 60 61 def GenerateAuthenticatorResponse(password_unicode,NTResponse,PeerChalleng,AuthenticatorChallenge,UserName): 62 Magic1 =b'\x4D\x61\x67\x69\x63\x20\x73\x65\x72\x76\x65\x72\x20\x74\x6F\x20\x63\x6C\x69\x65\x6E\x74\x20\x73\x69\x67\x6E\x69\x6E\x67\x20\x63\x6F\x6E\x73\x74\x61\x6E\x74' 63 Magic2 = bytes([0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,0x6E]) 64 Passwordhash=Nt_password_hash(password_unicode) 65 PasswordHashhash=Nt_password_hashhash(Passwordhash) 66 Context=SHA.new() 67 Context.update(PasswordHashhash) 68 Context.update(NTResponse) 69 Context.update(Magic1) 70 aa=Context.digest() 71 Challenge=ChallengeHash(PeerChalleng,AuthenticatorChallenge,UserName) 72 Context=SHA.new() 73 Context.update(aa) 74 Context.update(Challenge) 75 Context.update(Magic2) 76 aa=Context.digest() 77 return b'S=' + binascii.hexlify(aa)[:40] 78 def print_hex(s): 79 for i in s: 80 print('\%#x' % i, end='') 81 print('') 82 83 84 if __name__ == "__main__": 85 86 UserName = 'WA_042'.encode("utf8") 87 password = '123456'.encode("utf8") 88 AuthenticatorChallenge = b'\x11\xD8\x2A\x82\x0F\xBE\xB0\x30\x73\xB1\xF7\x03\x73\x22\x26\xBF' 89 PeerChalleng = b'\xC1\xD4\x43\x9D\xBD\x65\xA2\x74\xB9\x7A\xF0\x3F\x98\x00\x6D\x75' 90 password_unicode = b'' 91 for ch in password: 92 password_unicode += bytes([ch]) 93 password_unicode +=b'\x00' 94 NTResponse=GenerateNTResponse(AuthenticatorChallenge, PeerChalleng, UserName, password_unicode) 95 PasswordHashhash = Nt_password_hashhash(Nt_password_hash(password_unicode)) 96 AuthenticatorResponse=GenerateAuthenticatorResponse(password_unicode,NTResponse,PeerChalleng,AuthenticatorChallenge,UserName) 97 print("用户名:",UserName) 98 print("密码:",password) 99 print("挑战响应值:") 100 print_hex(NTResponse) 101 print("AuthenticatorResponse: ",AuthenticatorResponse)