├── HiveRansomwareV5-file_decryptor ├── HiveRansomwareV5-file_decryptor.vcxproj.user ├── HiveRansomwareV5-file_decryptor.vcxproj.filters ├── HiveRansomwareV5-file_decryptor.vcxproj └── HiveRansomwareV5-file_decryptor.cpp └── README.md /HiveRansomwareV5-file_decryptor/HiveRansomwareV5-file_decryptor.vcxproj.user: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /HiveRansomwareV5-file_decryptor/HiveRansomwareV5-file_decryptor.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | File di origine 20 | 21 | 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HiveV5 file decryptor PoC 2 | 3 | ## Introduction 4 | 5 | The work done in the last few months has been necessary to reveal the malicious file encryption mechanism of Hive v5-5.2. 6 | The work was divided into two parts 7 | 1. [Keystream decryption](https://github.com/reecdeep/HiveV5_keystream_decryptor) 8 | 2. File decryption using the decrypted keystream 9 | 10 | I would like to thank the great [@rivitna](https://twitter.com/rivitna2) for the support, dialogue and advices of these months of work! 11 | Please take note of [rivitna's github](https://github.com/rivitna) full of useful informations about Hive ransomware and more. 12 | 13 | In this readme you will find some information about the file decryption algorithm, referring you to the PoC for a more complete picture of how it works. 14 | A keystream is an encrypted cleartext. A cleartext is a set of 0xA00000 bytes to which the first 0x2FFF00 bytes have been appended, for a total of 0xCFFF00 bytes. These bytes were created with the weak algorithm already discussed in the first part released in July 2022. 15 | Here below is a example of cleartext: 16 | 17 | ![cleartext](https://user-images.githubusercontent.com/72123074/204250635-f96b579f-c19b-4c14-be15-157300e1633d.png) 18 | 19 | 20 | The Hive sample analyzed and referred to in this document was chosen from [this list](https://github.com/rivitna/Malware/blob/main/Hive/Hive_samples.txt) created by [@rivitna](https://twitter.com/rivitna2) to which my warmest thanks go. 21 | To get an idea of the complexity of ransomware, please take a look at [this analysis](https://www.microsoft.com/security/blog/2022/07/05/hive-ransomware-gets-upgrades-in-rust/) published by Microsoft Threat Intelligence Center (MSTIC). 22 | 23 | 24 | ## File encryption algorithm 25 | 26 | The cleartext (a decrypted keystream) is used by Hive ransomware when encrypting each file. When encrypting a file, Hive ransomware calculates two integers referring to precise positions in the cleartext (offsets) to be used to encrypt the file according to the following formula: 27 | 28 | ![formula](https://user-images.githubusercontent.com/72123074/204250645-788c1269-cbf8-4006-b042-b16ee7756cdb.png) 29 | 30 | where c = i % 0x2FFF00 e d = i % 0x2FFD00 , with i as a byte counter. 31 | 32 | 33 | 34 | ## The encrypted file extension 35 | The preliminary operations before writing a file are: 36 | - Renaming the file using MoveFileExW and changing its extension; 37 | - Writing the renamed file with the result of the xor operation shown above. 38 | 39 | ![file_extension](https://user-images.githubusercontent.com/72123074/204250641-2567b7f9-cc42-4516-b378-b357eed4d018.png) 40 | 41 | Also in this case the cleartext plays a fundamental role. In fact it is used for: 42 | 1. Determine the keystream ID (first 6 bytes) using a hash function 43 | 2. Encrypt the positions (offsets) used to extract bytes from the cleartext 44 | However, the first offset is encrypted using a fixed position of the cleartext and is different for each Hive 5/5.1/5.2 sample. 45 | A kind of magical value. In many Hive 5/5.1 artifacts this magic value is shown explicitly inside a memory reference, like in this case 0x98072A : 46 | 47 | ![off1](https://user-images.githubusercontent.com/72123074/204250648-da2d1514-2e56-4849-8090-79ad25625298.png) 48 | 49 | Or this case 0x7539D: 50 | 51 | ![off2](https://user-images.githubusercontent.com/72123074/204250651-82ea5c5d-840f-486a-807a-1a7a62f30811.png) 52 | 53 | But in the next evidence the for loop is slightly different and has been written in such a way as not to explicit the magic value that we need to identify. This concerns an artifact belonging to Hive 5.2: 54 | 55 | ![off3](https://user-images.githubusercontent.com/72123074/204250653-a747b1bb-e1d7-4061-a21c-d3c72612c93f.png) 56 | 57 | In this case it is possible to use the offset bruteforce function present in the released tool, using a file with a known extension and the relative decrypted keystream. Using the header of the encrypted file and the header of the unencrypted file it is possible to understand what is the offset from which the decryptor must start to decrypt the file. 58 | 59 | The file encryption mode can have two values: 0xFB or 0xFF 60 | - 0xFB means that the ransomware encrypted the entire file without leaving any portion of the file unencrypted. 61 | - 0xFF means that the ransomware calculated a NCB (not encrypted block) for each file and encrypting blocks of 0x100000 bytes. 62 | For further information regarding the calculation of the size of the unencrypted blocks and the cleartext offset, please refer to the PoC code. 63 | 64 | 65 | ## Usage 66 | The program offers two options: 67 | 68 | ![usage](https://user-images.githubusercontent.com/72123074/204250655-5e8c46e1-f9aa-4718-bcfe-ca60ff34b5b1.png) 69 | 70 | 1. Decryption of files using the decrypted keystream. You need to enter the special offset present in the sample that encrypted the files. 71 | 2. Given a file with a known header (PDF, JPG, PNG, Office files) brute the possible value of the special offset by decrypting the first bytes and looking for a match with the known signature 72 | 73 | 74 | ## References 75 | 76 | 77 | -------------------------------------------------------------------------------- /HiveRansomwareV5-file_decryptor/HiveRansomwareV5-file_decryptor.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 15.0 23 | {BB3DC7CE-8196-4748-B406-A3E18D37834C} 24 | Win32Proj 25 | hivefile 26 | 10.0.17763.0 27 | hive_file_decryption 28 | 29 | 30 | 31 | Application 32 | true 33 | v141 34 | Unicode 35 | 36 | 37 | Application 38 | false 39 | v141 40 | true 41 | Unicode 42 | 43 | 44 | Application 45 | true 46 | v141 47 | Unicode 48 | 49 | 50 | Application 51 | false 52 | v141 53 | true 54 | Unicode 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | true 76 | 77 | 78 | true 79 | 80 | 81 | false 82 | 83 | 84 | false 85 | 86 | 87 | 88 | 89 | 90 | Level3 91 | Disabled 92 | true 93 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 94 | true 95 | 96 | 97 | Console 98 | true 99 | 100 | 101 | 102 | 103 | 104 | 105 | Level3 106 | Disabled 107 | true 108 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 109 | true 110 | 111 | 112 | Console 113 | true 114 | 115 | 116 | 117 | 118 | 119 | 120 | Level3 121 | MaxSpeed 122 | true 123 | true 124 | true 125 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 126 | true 127 | 128 | 129 | Console 130 | true 131 | true 132 | true 133 | 134 | 135 | 136 | 137 | 138 | 139 | Level3 140 | MaxSpeed 141 | true 142 | true 143 | true 144 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 145 | true 146 | 147 | 148 | Console 149 | true 150 | true 151 | true 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | -------------------------------------------------------------------------------- /HiveRansomwareV5-file_decryptor/HiveRansomwareV5-file_decryptor.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #pragma comment(lib,"shlwapi.lib") 21 | 22 | std::vector base64_decode(const std::string & in); 23 | void openFile(std::string file_name, unsigned char* buffer, int dim); 24 | void file_decrypt(); 25 | void offset_bruteforce(); 26 | 27 | 28 | //the working path will be updated when selecting files 29 | std::string currentWorkingPath = ""; 30 | 31 | const char base64_url_alphabet[] = { 32 | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 33 | 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 34 | 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 35 | 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 36 | '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_' 37 | }; 38 | 39 | //array for storing decrypted keystream 40 | unsigned char* decrypted_keystream = new unsigned char[0xCFFF00](); 41 | //size of NOT Cyphered Block 42 | unsigned int ncb_size; 43 | 44 | //this field changes, on each HIVE sample! 45 | //unsigned int specialOffset = 0x2ABD4E; 46 | //unsigned int specialOffset = 0x98072A; 47 | unsigned int specialOffset = 0; 48 | 49 | //flag NCB swicth /non crypted block) 50 | boolean ncb_override = true; 51 | unsigned char* knownheader; 52 | 53 | 54 | 55 | 56 | 57 | int main() 58 | { 59 | 60 | std::string j; 61 | 62 | std::cout << "Hive ransomware V5 - file decryptor PoC" << std::endl; 63 | std::cout << "--------------------------------------------\n" << std::endl; 64 | 65 | std::cout << "1. Decrypt a file using decrypted keystream" << std::endl; 66 | std::cout << "2. Offset bruteforce" << std::endl; 67 | std::cout << "your move: " << std::endl; 68 | 69 | j = std::cin.get(); 70 | 71 | 72 | if (j == "1") 73 | { 74 | file_decrypt(); 75 | } 76 | else 77 | if (j == "2") 78 | { 79 | offset_bruteforce(); 80 | } 81 | 82 | 83 | } 84 | 85 | 86 | 87 | 88 | 89 | void file_decrypt() 90 | { 91 | //open the decrypted keystream file 92 | std::cout << "Please enter the decrypted keystream file: \n"; 93 | std::string path_decrypted_keystream; 94 | std::cin >> path_decrypted_keystream; 95 | path_decrypted_keystream.erase(remove(path_decrypted_keystream.begin(), path_decrypted_keystream.end(), '\"'), path_decrypted_keystream.end()); 96 | std::ifstream ifs(path_decrypted_keystream.c_str()); 97 | 98 | if (!ifs) 99 | { 100 | std::cout << "error opening keystream file!"; 101 | } 102 | else 103 | { 104 | //ask for offset 105 | std::cout << "Please enter the offset value in hex format (0x12345678): \n"; 106 | std::cin >> std::hex >> specialOffset; 107 | 108 | 109 | 110 | 111 | while (true) 112 | { 113 | //open the file to be decrypted 114 | std::cout << "Please enter the encrypted file: \n"; 115 | std::string path_encrypted_file; 116 | std::cin >> path_encrypted_file; 117 | path_encrypted_file.erase(remove(path_encrypted_file.begin(), path_encrypted_file.end(), '\"'), path_encrypted_file.end()); 118 | std::ifstream ifs(path_encrypted_file.c_str()); 119 | 120 | //updating working path based on encrypted file position in file system 121 | currentWorkingPath = path_encrypted_file.substr(0, path_encrypted_file.find_last_of("/\\")); 122 | 123 | if (!ifs) 124 | { 125 | std::cout << "error opening encrypted file!\n"; 126 | } 127 | else 128 | { 129 | //get encrypted file extension 130 | size_t i = path_encrypted_file.rfind('.', path_encrypted_file.length()); 131 | if (i != std::string::npos) 132 | { 133 | std::string extension = (path_encrypted_file.substr(i + 1, path_encrypted_file.length() - i)); 134 | std::vector decoded_extension = base64_decode(extension); 135 | //byte dimension of decoded byte 136 | int dim_decoded_extension = decoded_extension.size(); 137 | 138 | //transform to array 139 | unsigned char* decoded_extension_array = new unsigned char[dim_decoded_extension](); 140 | std::copy(decoded_extension.begin(), decoded_extension.end(), decoded_extension_array); 141 | 142 | //read the decrypted keystream 143 | openFile(path_decrypted_keystream, decrypted_keystream, 0xCFFF00); 144 | 145 | //extract xor key from decrypted keystream 146 | unsigned char* first_offset_xor = new unsigned char[4](); 147 | memcpy(first_offset_xor, decrypted_keystream + specialOffset - 0x4, 4); 148 | 149 | //get first offset 150 | unsigned char* first_offset = new unsigned char[4](); 151 | memcpy(first_offset, decoded_extension_array + dim_decoded_extension - 8, 4); 152 | 153 | //read decryption mode byte 154 | unsigned char decryption_mode = decoded_extension_array[6]; 155 | 156 | //case decryption mode is 0xFB 157 | if (decryption_mode == '0xFB') 158 | { 159 | //no crypted block mode 160 | ncb_override = true; 161 | } 162 | else 163 | { 164 | //case decryption mode is 0xFF 165 | //crypted block mode on! 166 | ncb_override = false; 167 | } 168 | 169 | 170 | 171 | //first offset xored 172 | unsigned char* first_offset_xored = new unsigned char[4](); 173 | 174 | //xor first offset with xor key extracted from decrypted keystream 175 | for (int i = 0; i < 4; i++) 176 | { 177 | first_offset_xored[i] = first_offset[i] ^ first_offset_xor[i]; 178 | } 179 | 180 | //first offset xored to littleEndian 181 | unsigned char* first_offset_xored_le = new unsigned char[4](); 182 | first_offset_xored_le[0] = first_offset_xored[3]; 183 | first_offset_xored_le[1] = first_offset_xored[2]; 184 | first_offset_xored_le[2] = first_offset_xored[1]; 185 | first_offset_xored_le[3] = first_offset_xored[0]; 186 | 187 | //TO int 188 | unsigned int first_offset_xored_int = (first_offset_xored_le[0] << 24 | 189 | first_offset_xored_le[1] << 16 | 190 | first_offset_xored_le[2] << 8 | 191 | first_offset_xored_le[3]); 192 | 193 | //mul offset with fixed value 0x3333347B 194 | unsigned long long mul1 = (unsigned long long) first_offset_xored_int * 0x3333347B; 195 | 196 | //shifting to the right the upper part of the mul1 long long by 15 197 | unsigned int shift1 = (unsigned int)(mul1 >> 32) >> 0x15; 198 | 199 | //mul shift with fixed value 0x9FFFFC 200 | unsigned int mul2 = shift1 * 0x9FFFFC; 201 | 202 | //difference between offset1 and mul2 (acts like second offset xor key) 203 | unsigned int diff1 = first_offset_xored_int - mul2; 204 | 205 | //extract second xor key from decrypted keystream 206 | unsigned char* second_offset_xor = new unsigned char[4](); 207 | memcpy(second_offset_xor, decrypted_keystream + diff1, 4); 208 | 209 | //get second offset 210 | unsigned char* second_offset = new unsigned char[4](); 211 | memcpy(second_offset, decoded_extension_array + dim_decoded_extension - 4, 4); 212 | 213 | 214 | //second offset xored 215 | unsigned char* second_offset_xored = new unsigned char[4](); 216 | 217 | //xor second offset with second xor key extracted from decrypted keystream 218 | for (int i = 0; i < 4; i++) 219 | { 220 | second_offset_xored[i] = second_offset[i] ^ second_offset_xor[i]; 221 | } 222 | 223 | //second offset xored to littleEndian 224 | unsigned char* second_offset_xored_le = new unsigned char[4](); 225 | second_offset_xored_le[0] = second_offset_xored[3]; 226 | second_offset_xored_le[1] = second_offset_xored[2]; 227 | second_offset_xored_le[2] = second_offset_xored[1]; 228 | second_offset_xored_le[3] = second_offset_xored[0]; 229 | 230 | //TO int 231 | unsigned int second_offset_xored_int = (second_offset_xored_le[0] << 24 | 232 | second_offset_xored_le[1] << 16 | 233 | second_offset_xored_le[2] << 8 | 234 | second_offset_xored_le[3]); 235 | 236 | 237 | //mul first_offset_xored_le with 0xCCCCCCCD 238 | unsigned long long mul3 = (unsigned long long) first_offset_xored_int * 0xCCCCCCCD; 239 | //mul second_offset_xored_le with 0xCCCCCCCD 240 | unsigned long long mul4 = (unsigned long long) second_offset_xored_int * 0xCCCCCCCD; 241 | 242 | //shifting to the right the upper part of the mul3 long long by 2 243 | unsigned int shift2 = (unsigned int)(mul3 >> 32) >> 0x2; 244 | 245 | //logic-AND between shift2 and 0x3FE00000 246 | unsigned int and1 = shift2 & 0x3FE00000; 247 | 248 | //mul and1 by 5 times 249 | unsigned int mul5 = and1 * 5; 250 | 251 | //REAL FIRST OFFSET FOR XOR KEY 252 | unsigned int real_first_XORKEY_offset = first_offset_xored_int - mul5; 253 | 254 | //shifting to the right the upper part of the mul4 long long by 2 255 | unsigned int shift3 = (unsigned int)(mul4 >> 32) >> 0x2; 256 | 257 | //logic-AND between shift3 and 0x3FE00000 258 | unsigned int and2 = shift3 & 0x3FE00000; 259 | 260 | //mul and1 by 5 times 261 | unsigned int mul6 = and2 * 5; 262 | 263 | //REAL SECOND OFFSET FOR XOR KEY 264 | unsigned int real_second_XORKEY_offset = second_offset_xored_int - mul6; 265 | 266 | //get encrypted file size 267 | std::ifstream in_file(path_encrypted_file.c_str(), std::ios::binary); 268 | in_file.seekg(0, std::ios::end); 269 | int file_size = in_file.tellg(); 270 | 271 | //unsigned char* file_clear = new unsigned char[file_size](); 272 | //unsigned char* file_encrypted = new unsigned char[file_size](); 273 | 274 | unsigned char* file_clear; 275 | file_clear = (unsigned char*)malloc(file_size); 276 | 277 | unsigned char* file_encrypted; 278 | file_encrypted = (unsigned char*)malloc(file_size); 279 | 280 | //read the encrypted file 281 | openFile(path_encrypted_file, file_encrypted, file_size); 282 | 283 | 284 | //NCB-Not-cyphered-block size computation 285 | if (file_size > 0x100000) 286 | { 287 | 288 | //unsigned int file_size = 0x4c4b40; 289 | //unsigned int file_size = 0x100001; 290 | unsigned long long shl = (unsigned long long)file_size << 0xB; 291 | 292 | //get the high part of shl operation 293 | unsigned int shl_hp = shl >> 0x20; 294 | 295 | //off 296 | unsigned int edx = 0; 297 | 298 | if (0xC9FFFFF >= file_size) 299 | { 300 | edx = shl_hp; 301 | 302 | } 303 | else 304 | { 305 | edx = 0x64; 306 | } 307 | 308 | unsigned int shl3 = edx << 0x14; 309 | 310 | unsigned int sub1 = file_size - shl3; 311 | unsigned int sub2 = edx - 1; 312 | if (sub2 > 0) 313 | { 314 | ncb_size = sub1 / sub2; 315 | } 316 | else 317 | { 318 | ncb_size = sub1; 319 | } 320 | 321 | } 322 | 323 | 324 | 325 | 326 | 327 | //storing count of byte being decrypted 328 | //this variable is used to access in incremental way the decrypted keystream array 329 | //this variable is never reset 330 | unsigned int total_decrypted_count = 0; 331 | 332 | unsigned int total_decrypted_blocks = 0; 333 | 334 | //if the ncb_size is larger than 0, it means the file is not completely encrypted 335 | //so there are not cyphered blocks 336 | if (ncb_size > 0 && (ncb_override == false)) 337 | { 338 | //counter of cyphering block, for resetting the cryptedBlock flag 339 | unsigned int decypher_count = 0; 340 | 341 | //counter of first offset 342 | unsigned int not_cyphered_count = 0; 343 | 344 | //flag to distinguish from encrypted block or not encrypted block 345 | boolean cryptedBlock = true; 346 | 347 | unsigned int mod_first_offset = 0; 348 | unsigned int mod_second_offset = 0; 349 | unsigned int total_decrypted_count = 0; 350 | 351 | for (int i = 0; i < file_size; i++) 352 | { 353 | mod_second_offset = total_decrypted_count % 0x2FFF00; 354 | mod_first_offset = total_decrypted_count % 0x2FFD00; 355 | 356 | //if I'm on a crypted block 357 | if (cryptedBlock) 358 | { 359 | file_clear[i] = decrypted_keystream[real_first_XORKEY_offset + mod_second_offset] ^ 360 | decrypted_keystream[real_second_XORKEY_offset + mod_first_offset] ^ 361 | file_encrypted[i]; 362 | 363 | decypher_count++; 364 | total_decrypted_count++; 365 | } 366 | else 367 | { 368 | file_clear[i] = file_encrypted[i]; 369 | 370 | not_cyphered_count++; 371 | } 372 | 373 | 374 | //if cypher_count reaches the end of the encrypted block 375 | if (decypher_count == 0x100000) 376 | { 377 | //update the decryption block count 378 | total_decrypted_blocks++; 379 | 380 | cryptedBlock = false; 381 | 382 | //reset the counter 383 | decypher_count = 0; 384 | } 385 | 386 | //if reach the count of ncb_size, then start deciphering again and set not cyphered_count to zero 387 | if (not_cyphered_count == ncb_size) 388 | { 389 | cryptedBlock = true; 390 | 391 | //reset the counter 392 | not_cyphered_count = 0; 393 | } 394 | 395 | 396 | } 397 | } 398 | else 399 | { 400 | unsigned int mod_first_offset = 0; 401 | unsigned int mod_second_offset = 0; 402 | 403 | for (int i = 0; i < file_size; i++) 404 | { 405 | mod_second_offset = i % 0x2FFF00; 406 | mod_first_offset = i % 0x2FFD00; 407 | 408 | file_clear[i] = decrypted_keystream[real_first_XORKEY_offset + mod_second_offset] ^ 409 | decrypted_keystream[real_second_XORKEY_offset + mod_first_offset] ^ 410 | file_encrypted[i]; 411 | 412 | } 413 | 414 | 415 | 416 | 417 | } 418 | 419 | 420 | 421 | // Character to be found 422 | char ch = '.'; 423 | 424 | // To store the index of last 425 | // character found 426 | size_t found; 427 | 428 | // Function to find the last 429 | // character ch in str 430 | found = path_encrypted_file.find_last_of(ch); 431 | 432 | std::string outputFile = path_encrypted_file.substr(0, found); 433 | 434 | std::ofstream outfile_keystream(outputFile, std::ofstream::binary); 435 | outfile_keystream.write((const char *)file_clear, file_size); 436 | outfile_keystream.close(); 437 | 438 | 439 | 440 | 441 | } 442 | else 443 | { 444 | 445 | } 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | } 455 | 456 | } 457 | 458 | 459 | 460 | 461 | } 462 | } 463 | 464 | 465 | std::vector base64_decode(const std::string & in) 466 | { 467 | std::vector out; 468 | std::vector T(256, -1); 469 | unsigned int i; 470 | for (i = 0; i < 64; i++) T[base64_url_alphabet[i]] = i; 471 | 472 | int val = 0, valb = -8; 473 | for (i = 0; i < in.length(); i++) { 474 | unsigned char c = in[i]; 475 | if (T[c] == -1) break; 476 | val = (val << 6) + T[c]; 477 | valb += 6; 478 | if (valb >= 0) { 479 | out.push_back(BYTE((val >> valb) & 0xFF)); 480 | valb -= 8; 481 | } 482 | } 483 | return out; 484 | } 485 | 486 | void openFile(std::string file_name, unsigned char* buffer, int dim) 487 | { 488 | 489 | // open the file: 490 | std::streampos fileSize; 491 | std::ifstream file(file_name, std::ios::binary); 492 | 493 | 494 | // read the data: 495 | //buffer = new unsigned char[dim](); 496 | file.read((char*)&buffer[0], dim); 497 | 498 | 499 | } 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | void offset_bruteforce() 513 | { 514 | //open the decrypted keystream file 515 | std::cout << "Please enter the decrypted keystream file: \n"; 516 | std::string path_decrypted_keystream; 517 | std::cin >> path_decrypted_keystream; 518 | path_decrypted_keystream.erase(remove(path_decrypted_keystream.begin(), path_decrypted_keystream.end(), '\"'), path_decrypted_keystream.end()); 519 | std::ifstream ifs(path_decrypted_keystream.c_str()); 520 | 521 | if (!ifs) 522 | { 523 | std::cout << "error opening keystream file!"; 524 | } 525 | else 526 | { 527 | 528 | int headerLen = 0; 529 | 530 | std::cout << "Please enter the int referring to the extension (0:pdf, 1:doc|xls|ppt, 2:jpg, 3:png, 4:docx|xlsx|pptx ): \n"; 531 | int choosen_extension; 532 | std::cin >> choosen_extension; 533 | 534 | switch (choosen_extension) 535 | { 536 | case 0: 537 | printf("you choosed pdf\n"); 538 | headerLen = 5; 539 | knownheader = new unsigned char[headerLen](); 540 | knownheader[0] = 0x25; 541 | knownheader[1] = 0x50; 542 | knownheader[2] = 0x44; 543 | knownheader[3] = 0x46; 544 | knownheader[4] = 0x2D; 545 | 546 | break; 547 | case 1: 548 | 549 | printf("you choosed doc|xls|ppt\n"); 550 | headerLen = 8; 551 | knownheader = new unsigned char[headerLen](); 552 | knownheader[0] = 0xD0; 553 | knownheader[1] = 0xCF; 554 | knownheader[2] = 0x11; 555 | knownheader[3] = 0xE0; 556 | knownheader[4] = 0xA1; 557 | knownheader[5] = 0xB1; 558 | knownheader[6] = 0x1A; 559 | knownheader[7] = 0xE1; 560 | 561 | break; 562 | case 2: 563 | printf("you choosed jpg\n"); 564 | headerLen = 3; 565 | knownheader = new unsigned char[headerLen](); 566 | knownheader[0] = 0xFF; 567 | knownheader[1] = 0xD8; 568 | knownheader[2] = 0xFF; 569 | break; 570 | case 3: 571 | printf("you choosed png\n"); 572 | headerLen = 8; 573 | knownheader = new unsigned char[headerLen](); 574 | knownheader[0] = 0x89; 575 | knownheader[1] = 0x50; 576 | knownheader[2] = 0x4E; 577 | knownheader[3] = 0x47; 578 | knownheader[4] = 0x0D; 579 | knownheader[5] = 0x0A; 580 | knownheader[6] = 0x1A; 581 | knownheader[7] = 0x0A; 582 | break; 583 | case 4: 584 | printf("you choosed docx|xlsx|pptx\n"); 585 | headerLen = 4; 586 | knownheader = new unsigned char[headerLen](); 587 | knownheader[0] = 0x50; 588 | knownheader[1] = 0x4B; 589 | knownheader[2] = 0x03; 590 | knownheader[3] = 0x04; 591 | break; 592 | 593 | 594 | default: 595 | printf("Wrong choose!\n"); 596 | break; 597 | } 598 | 599 | 600 | //open the file to be decrypted 601 | std::cout << "Please enter the encrypted file with the same extension: \n"; 602 | std::string path_encrypted_file; 603 | std::cin >> path_encrypted_file; 604 | path_encrypted_file.erase(remove(path_encrypted_file.begin(), path_encrypted_file.end(), '\"'), path_encrypted_file.end()); 605 | std::ifstream ifs(path_encrypted_file.c_str()); 606 | 607 | //updating working path based on encrypted file position in file system 608 | currentWorkingPath = path_encrypted_file.substr(0, path_encrypted_file.find_last_of("/\\")); 609 | 610 | if (!ifs) 611 | { 612 | std::cout << "error opening encrypted file!"; 613 | } 614 | else 615 | { 616 | //get encrypted file extension 617 | size_t i = path_encrypted_file.rfind('.', path_encrypted_file.length()); 618 | if (i != std::string::npos) 619 | { 620 | std::string extension = (path_encrypted_file.substr(i + 1, path_encrypted_file.length() - i)); 621 | std::vector decoded_extension = base64_decode(extension); 622 | //byte dimension of decoded byte 623 | int dim_decoded_extension = decoded_extension.size(); 624 | 625 | //transform to array 626 | unsigned char* decoded_extension_array = new unsigned char[dim_decoded_extension](); 627 | std::copy(decoded_extension.begin(), decoded_extension.end(), decoded_extension_array); 628 | 629 | //read the decrypted keystream 630 | openFile(path_decrypted_keystream, decrypted_keystream, 0xCFFF00); 631 | 632 | //extract xor key from decrypted keystream 633 | unsigned char* first_offset_xor = new unsigned char[4](); 634 | 635 | boolean isOffsetNotFound = true; 636 | //special offset from zero until I find the right offset 637 | specialOffset = 0; 638 | 639 | std::cout << "Starting offset bruteforce from " << specialOffset << " \n"; 640 | 641 | //crea vettore per file cifrato 642 | unsigned char* file_encrypted = new unsigned char[4](); 643 | //leggi i primi [lunghezzaHeader] byte del file cifrato 644 | openFile(path_encrypted_file, file_encrypted, headerLen); 645 | 646 | while (isOffsetNotFound) 647 | { 648 | memcpy(first_offset_xor, decrypted_keystream + specialOffset - 0x4, 4); 649 | //get first offset 650 | unsigned char* first_offset = new unsigned char[4](); 651 | memcpy(first_offset, decoded_extension_array + dim_decoded_extension - 8, 4); 652 | 653 | //first offset xored 654 | unsigned char* first_offset_xored = new unsigned char[4](); 655 | 656 | //xor first offset with xor key extracted from decrypted keystream 657 | for (int i = 0; i < 4; i++) 658 | { 659 | first_offset_xored[i] = first_offset[i] ^ first_offset_xor[i]; 660 | } 661 | 662 | //first offset xored to littleEndian 663 | unsigned char* first_offset_xored_le = new unsigned char[4](); 664 | first_offset_xored_le[0] = first_offset_xored[3]; 665 | first_offset_xored_le[1] = first_offset_xored[2]; 666 | first_offset_xored_le[2] = first_offset_xored[1]; 667 | first_offset_xored_le[3] = first_offset_xored[0]; 668 | 669 | //TO int 670 | unsigned int first_offset_xored_int = (first_offset_xored_le[0] << 24 | 671 | first_offset_xored_le[1] << 16 | 672 | first_offset_xored_le[2] << 8 | 673 | first_offset_xored_le[3]); 674 | 675 | //mul offset with fixed value 0x3333347B 676 | unsigned long long mul1 = (unsigned long long) first_offset_xored_int * 0x3333347B; 677 | 678 | //shifting to the right the upper part of the mul1 long long by 15 679 | unsigned int shift1 = (unsigned int)(mul1 >> 32) >> 0x15; 680 | 681 | //mul shift with fixed value 0x9FFFFC 682 | unsigned int mul2 = shift1 * 0x9FFFFC; 683 | 684 | //difference between offset1 and mul2 (acts like second offset xor key) 685 | unsigned int diff1 = first_offset_xored_int - mul2; 686 | 687 | //extract second xor key from decrypted keystream 688 | unsigned char* second_offset_xor = new unsigned char[4](); 689 | memcpy(second_offset_xor, decrypted_keystream + diff1, 4); 690 | 691 | //get second offset 692 | unsigned char* second_offset = new unsigned char[4](); 693 | memcpy(second_offset, decoded_extension_array + dim_decoded_extension - 4, 4); 694 | 695 | 696 | //second offset xored 697 | unsigned char* second_offset_xored = new unsigned char[4](); 698 | 699 | //xor second offset with second xor key extracted from decrypted keystream 700 | for (int i = 0; i < 4; i++) 701 | { 702 | second_offset_xored[i] = second_offset[i] ^ second_offset_xor[i]; 703 | } 704 | 705 | //second offset xored to littleEndian 706 | unsigned char* second_offset_xored_le = new unsigned char[4](); 707 | second_offset_xored_le[0] = second_offset_xored[3]; 708 | second_offset_xored_le[1] = second_offset_xored[2]; 709 | second_offset_xored_le[2] = second_offset_xored[1]; 710 | second_offset_xored_le[3] = second_offset_xored[0]; 711 | 712 | //TO int 713 | unsigned int second_offset_xored_int = (second_offset_xored_le[0] << 24 | 714 | second_offset_xored_le[1] << 16 | 715 | second_offset_xored_le[2] << 8 | 716 | second_offset_xored_le[3]); 717 | 718 | 719 | //mul first_offset_xored_le with 0xCCCCCCCD 720 | unsigned long long mul3 = (unsigned long long) first_offset_xored_int * 0xCCCCCCCD; 721 | //mul second_offset_xored_le with 0xCCCCCCCD 722 | unsigned long long mul4 = (unsigned long long) second_offset_xored_int * 0xCCCCCCCD; 723 | 724 | //shifting to the right the upper part of the mul3 long long by 2 725 | unsigned int shift2 = (unsigned int)(mul3 >> 32) >> 0x2; 726 | 727 | //logic-AND between shift2 and 0x3FE00000 728 | unsigned int and1 = shift2 & 0x3FE00000; 729 | 730 | //mul and1 by 5 times 731 | unsigned int mul5 = and1 * 5; 732 | 733 | //REAL FIRST OFFSET FOR XOR KEY 734 | unsigned int real_first_XORKEY_offset = first_offset_xored_int - mul5; 735 | 736 | //shifting to the right the upper part of the mul4 long long by 2 737 | unsigned int shift3 = (unsigned int)(mul4 >> 32) >> 0x2; 738 | 739 | //logic-AND between shift3 and 0x3FE00000 740 | unsigned int and2 = shift3 & 0x3FE00000; 741 | 742 | //mul and1 by 5 times 743 | unsigned int mul6 = and2 * 5; 744 | 745 | //REAL SECOND OFFSET FOR XOR KEY 746 | unsigned int real_second_XORKEY_offset = second_offset_xored_int - mul6; 747 | 748 | //creating an output array of headerLen sie 749 | unsigned char* fileclear = new unsigned char[headerLen](); 750 | 751 | unsigned int mod_first_offset = 0; 752 | unsigned int mod_second_offset = 0; 753 | 754 | //decrypting the first headerLen bytes 755 | for (int i = 0; i < headerLen; i++) 756 | { 757 | mod_second_offset = i % 0x2FFF00; 758 | mod_first_offset = i % 0x2FFD00; 759 | 760 | fileclear[i] = decrypted_keystream[real_first_XORKEY_offset + mod_second_offset] ^ 761 | decrypted_keystream[real_second_XORKEY_offset + mod_first_offset] ^ 762 | file_encrypted[i]; 763 | 764 | } 765 | 766 | 767 | //headerLen is the number of matching bytes 768 | if (strncmp(reinterpret_cast(fileclear + 0), reinterpret_cast(knownheader + 0), headerLen) == 0) 769 | { 770 | isOffsetNotFound = false; 771 | 772 | std::cout << "========= Offset FOUND: " << specialOffset << " (0x" << std::hex << specialOffset << ") ======== \n\n"; 773 | } 774 | 775 | if (specialOffset > 0xFFFFFFFF) 776 | { 777 | std::cout << "========= Offset not found! ======== \n\n"; 778 | isOffsetNotFound = false; 779 | 780 | } 781 | else 782 | { 783 | // 784 | specialOffset = specialOffset + 1; 785 | 786 | } 787 | 788 | 789 | 790 | 791 | } 792 | 793 | 794 | 795 | 796 | 797 | 798 | 799 | 800 | } 801 | 802 | 803 | 804 | 805 | 806 | 807 | 808 | 809 | 810 | 811 | 812 | 813 | } 814 | 815 | } 816 | } 817 | 818 | 819 | --------------------------------------------------------------------------------