本文基於網路密碼課上的實驗
本來想水一水就過去,程式碼就網上找找,不行就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)