全網首一份!你最需要的PPTP MS-CHAP V2 挑戰響應程式設計模擬計算教程!程式碼基於RFC2759,附全部原始碼!

定州刺猬發表於2024-05-18

本文基於網路密碼課上的實驗

本來想水一水就過去,程式碼就網上找找,不行就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

全部程式碼如下:注意展開

全網首一份!你最需要的PPTP MS-CHAP V2 挑戰響應程式設計模擬計算教程!程式碼基於RFC2759,附全部原始碼!
  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 }
View Code

二、根據前期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.編寫程式碼

原始碼如下,注意,已經摺疊,點選展開

全網首一份!你最需要的PPTP MS-CHAP V2 挑戰響應程式設計模擬計算教程!程式碼基於RFC2759,附全部原始碼!
  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原始碼,歡迎批評指正!

全網首一份!你最需要的PPTP MS-CHAP V2 挑戰響應程式設計模擬計算教程!程式碼基於RFC2759,附全部原始碼!
  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)
View Code

相關文章