Junior Member
Join Date: Apr 2025
Posts: 1
|
HiveV5 file decryptor PoC - Educational
#include <Windows.h>
#include <shlwapi.h>
#include <iostream>
#include <stdio.h>
#include <fstream>
#include <string>
#include <sstream>
#include <vector>
#include <iterator>
#include <iomanip>
#include <thread>
#include <set>
#include <string_view>
#include <functional>
#include <algorithm>
#include <string>
#include <filesystem>
#pragma comment(lib,"shlwapi.lib")
std::vector<BYTE> base64_decode(const std::string & in);
void openFile(std::string file_name, unsigned char* buffer, int dim);
void file_decrypt();
void offset_bruteforce();
//the working path will be updated when selecting files
std::string currentWorkingPath = "";
const char base64_url_alphabet[] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'
};
//array for storing decrypted keystream
unsigned char* decrypted_keystream = new unsigned char[0xCFFF00]();
//size of NOT Cyphered Block
unsigned int ncb_size;
//this field changes, on each HIVE sample!
//unsigned int specialOffset = 0x2ABD4E;
//unsigned int specialOffset = 0x98072A;
unsigned int specialOffset = 0;
//flag NCB swicth /non crypted block)
boolean ncb_override = true;
unsigned char* knownheader;
int main()
{
std::string j;
std::cout << "Hive ransomware V5 - file decryptor PoC" << std::endl;
std::cout << "--------------------------------------------\n" << std::endl;
std::cout << "1. Decrypt a file using decrypted keystream" << std::endl;
std::cout << "2. Offset bruteforce" << std::endl;
std::cout << "your move: " << std::endl;
j = std::cin.get();
if (j == "1")
{
file_decrypt();
}
else
if (j == "2")
{
offset_bruteforce();
}
}
void file_decrypt()
{
//open the decrypted keystream file
std::cout << "Please enter the decrypted keystream file: \n";
std::string path_decrypted_keystream;
std::cin >> path_decrypted_keystream;
path_decrypted_keystream.erase(remove(path_decrypt ed_keystream.begin(), path_decrypted_keystream.end(), '"'), path_decrypted_keystream.end());
std::ifstream ifs(path_decrypted_keystream.c_str());
if (!ifs)
{
std::cout << "error opening keystream file!";
}
else
{
//ask for offset
std::cout << "Please enter the offset value in hex format (0x12345678): \n";
std::cin >> std::hex >> specialOffset;
while (true)
{
//open the file to be decrypted
std::cout << "Please enter the encrypted file: \n";
std::string path_encrypted_file;
std::cin >> path_encrypted_file;
path_encrypted_file.erase(remove(path_encrypted_fi le.begin(), path_encrypted_file.end(), '"'), path_encrypted_file.end());
std::ifstream ifs(path_encrypted_file.c_str());
//updating working path based on encrypted file position in file system
currentWorkingPath = path_encrypted_file.substr(0, path_encrypted_file.find_last_of("/\"));
if (!ifs)
{
std::cout << "error opening encrypted file!\n";
}
else
{
//get encrypted file extension
size_t i = path_encrypted_file.rfind('.', path_encrypted_file.length());
if (i != std::string::npos)
{
std::string extension = (path_encrypted_file.substr(i + 1, path_encrypted_file.length() - i));
std::vector<BYTE> decoded_extension = base64_decode(extension);
//byte dimension of decoded byte
int dim_decoded_extension = decoded_extension.size();
//transform to array
unsigned char* decoded_extension_array = new unsigned char[dim_decoded_extension]();
std::copy(decoded_extension.begin(), decoded_extension.end(), decoded_extension_array);
//read the decrypted keystream
openFile(path_decrypted_keystream, decrypted_keystream, 0xCFFF00);
//extract xor key from decrypted keystream
unsigned char* first_offset_xor = new unsigned char[4]();
memcpy(first_offset_xor, decrypted_keystream + specialOffset - 0x4, 4);
//get first offset
unsigned char* first_offset = new unsigned char[4]();
memcpy(first_offset, decoded_extension_array + dim_decoded_extension - 8, 4);
//read decryption mode byte
unsigned char decryption_mode = decoded_extension_array[6];
//case decryption mode is 0xFB
if (decryption_mode == '0xFB')
{
//no crypted block mode
ncb_override = true;
}
else
{
//case decryption mode is 0xFF
//crypted block mode on!
ncb_override = false;
}
//first offset xored
unsigned char* first_offset_xored = new unsigned char[4]();
//xor first offset with xor key extracted from decrypted keystream
for (int i = 0; i < 4; i++)
{
first_offset_xored[i] = first_offset[i] ^ first_offset_xor[i];
}
//first offset xored to littleEndian
unsigned char* first_offset_xored_le = new unsigned char[4]();
first_offset_xored_le[0] = first_offset_xored[3];
first_offset_xored_le[1] = first_offset_xored[2];
first_offset_xored_le[2] = first_offset_xored[1];
first_offset_xored_le[3] = first_offset_xored[0];
//TO int
unsigned int first_offset_xored_int = (first_offset_xored_le[0] << 24 |
first_offset_xored_le[1] << 16 |
first_offset_xored_le[2] << 8 |
first_offset_xored_le[3]);
//mul offset with fixed value 0x3333347B
unsigned long long mul1 = (unsigned long long) first_offset_xored_int * 0x3333347B;
//shifting to the right the upper part of the mul1 long long by 15
unsigned int shift1 = (unsigned int)(mul1 >> 32) >> 0x15;
//mul shift with fixed value 0x9FFFFC
unsigned int mul2 = shift1 * 0x9FFFFC;
//difference between offset1 and mul2 (acts like second offset xor key)
unsigned int diff1 = first_offset_xored_int - mul2;
//extract second xor key from decrypted keystream
unsigned char* second_offset_xor = new unsigned char[4]();
memcpy(second_offset_xor, decrypted_keystream + diff1, 4);
//get second offset
unsigned char* second_offset = new unsigned char[4]();
memcpy(second_offset, decoded_extension_array + dim_decoded_extension - 4, 4);
//second offset xored
unsigned char* second_offset_xored = new unsigned char[4]();
//xor second offset with second xor key extracted from decrypted keystream
for (int i = 0; i < 4; i++)
{
second_offset_xored[i] = second_offset[i] ^ second_offset_xor[i];
}
//second offset xored to littleEndian
unsigned char* second_offset_xored_le = new unsigned char[4]();
second_offset_xored_le[0] = second_offset_xored[3];
second_offset_xored_le[1] = second_offset_xored[2];
second_offset_xored_le[2] = second_offset_xored[1];
second_offset_xored_le[3] = second_offset_xored[0];
//TO int
unsigned int second_offset_xored_int = (second_offset_xored_le[0] << 24 |
second_offset_xored_le[1] << 16 |
second_offset_xored_le[2] << 8 |
second_offset_xored_le[3]);
//mul first_offset_xored_le with 0xCCCCCCCD
unsigned long long mul3 = (unsigned long long) first_offset_xored_int * 0xCCCCCCCD;
//mul second_offset_xored_le with 0xCCCCCCCD
unsigned long long mul4 = (unsigned long long) second_offset_xored_int * 0xCCCCCCCD;
std::vector<BYTE> decoded_extension = base64_decode(extension);
//byte dimension of decoded byte
int dim_decoded_extension = decoded_extension.size();
//transform to array
unsigned char* decoded_extension_array = new unsigned char[dim_decoded_extension]();
std::copy(decoded_extension.begin(), decoded_extension.end(), decoded_extension_array);
//read the decrypted keystream
openFile(path_decrypted_keystream, decrypted_keystream, 0xCFFF00);
//extract xor key from decrypted keystream
unsigned char* first_offset_xor = new unsigned char[4]();
boolean isOffsetNotFound = true;
//special offset from zero until I find the right offset
specialOffset = 0;
std::cout << "Starting offset bruteforce from " << specialOffset << " \n";
//crea vettore per file cifrato
unsigned char* file_encrypted = new unsigned char[4]();
//leggi i primi [lunghezzaHeader] byte del file cifrato
openFile(path_encrypted_file, file_encrypted, headerLen);
while (isOffsetNotFound)
{
memcpy(first_offset_xor, decrypted_keystream + specialOffset - 0x4, 4);
//get first offset
unsigned char* first_offset = new unsigned char[4]();
memcpy(first_offset, decoded_extension_array + dim_decoded_extension - 8, 4);
//first offset xored
unsigned char* first_offset_xored = new unsigned char[4]();
//xor first offset with xor key extracted from decrypted keystream
for (int i = 0; i < 4; i++)
{
first_offset_xored[i] = first_offset[i] ^ first_offset_xor[i];
}
//first offset xored to littleEndian
unsigned char* first_offset_xored_le = new unsigned char[4]();
first_offset_xored_le[0] = first_offset_xored[3];
first_offset_xored_le[1] = first_offset_xored[2];
first_offset_xored_le[2] = first_offset_xored[1];
first_offset_xored_le[3] = first_offset_xored[0];
|