Skip to content

Commit

Permalink
add server 2016 support
Browse files Browse the repository at this point in the history
add server 2016 support
  • Loading branch information
zcgonvh committed Jan 17, 2018
1 parent d4b7c86 commit 2eddd0a
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 29 deletions.
2 changes: 1 addition & 1 deletion NTDSDumpEx/NTDSDumpEx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ void usage()
}
int main(int argc, char *argv[]) {
setlocale(LC_ALL, "");
printf("ntds.dit hashes off-line dumper v0.2.\n"
printf("ntds.dit hashes off-line dumper v0.3.\n"
"Part of GMH's fuck Tools,Code by zcgonvh.\n\n"
);
char *sysfile = 0;
Expand Down
2 changes: 1 addition & 1 deletion NTDSDumpEx/NTDSDumpEx.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>false</GenerateDebugInformation>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
Expand Down
112 changes: 95 additions & 17 deletions NTDSDumpEx/ntds.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ NTDS::NTDS() {
instance = NULL;
sesId = NULL;
dbId = NULL;

pekList = NULL;
// set the page size for NTDS.dit
err = JetSetSystemParameter(&instance, JET_sesidNil,
JET_paramDatabasePageSize, NTDS_PAGE_SIZE, NULL);
Expand All @@ -51,7 +51,7 @@ NTDS::NTDS() {
JET_paramRecovery, NULL, (JET_PCSTR)"Off");
if (err == JET_errSuccess) {
// create an instance
err = JetCreateInstance(&instance, (JET_PCSTR)"ntdsdump_0_2");
err = JetCreateInstance(&instance, (JET_PCSTR)"ntdsdump_0_3");
if (err == JET_errSuccess) {
// initialize
err = JetInit(&instance);
Expand Down Expand Up @@ -292,6 +292,33 @@ BOOL NTDS::EncryptDecryptWithKey(PBYTE pbKey, DWORD dwKeyLen,
}
return bResult;
}
BOOL NTDS::DecryptAes(PBYTE pbKey, DWORD dwKeyLen,
PBYTE pbSalt, DWORD dwSaltLen,
PBYTE pbData, DWORD dwDataLen) {
HCRYPTPROV hProv=0;
HCRYPTKEY hKey=0;
AES128_KEY_BLOB blob = { 0 };
BOOL bResult = FALSE;
DWORD d = dwDataLen;
if (CryptAcquireContext(&hProv, NULL, MS_ENH_RSA_AES_PROV, PROV_RSA_AES,
CRYPT_NEWKEYSET | CRYPT_VERIFYCONTEXT)) {
blob.hdr.bType = PLAINTEXTKEYBLOB;
blob.hdr.bVersion = CUR_BLOB_VERSION;
blob.hdr.reserved = 0;
blob.hdr.aiKeyAlg = CALG_AES_128;
blob.dwKeySize = 16;
memcpy(blob.bKey, pbKey, 16);
if (CryptImportKey(hProv, (PBYTE)&blob, sizeof(AES128_KEY_BLOB), 0, 0, &hKey) && CryptSetKeyParam(hKey, KP_IV, pbSalt, 0))
{
bResult=CryptDecrypt(hKey, 0, 0, 0, pbData, &d);
}
CryptDestroyKey(hKey);
CryptReleaseContext(hProv, 0);
}
return bResult;
}



static BYTE PekListAuthenticator[PEK_AUTH_LEN] =
{ 0x56, 0xD9, 0x81, 0x48, 0xEC, 0x91, 0xD1, 0x11,
Expand Down Expand Up @@ -319,31 +346,68 @@ BOOL NTDS::GetPEKey(PBYTE pbSysKey, PBYTE pbPEKey) {
JET_bitTableReadOnly | JET_bitTableSequential, &tableId);

if (err != JET_errSuccess) {
printf("ret 1\n");
return FALSE;
}

// go to first
err = JetMove(sesId, tableId, JET_MoveFirst, JET_bitNil);

// while good read
while (err == JET_errSuccess) {

DWORD dwPekListSize = 0;
err = JetRetrieveColumn(sesId, tableId, pekId,
(void*)&pekList, sizeof(pekList), &dwPekListSize, JET_bitNil, NULL);

0, 0, &dwPekListSize, JET_bitNil, NULL);
if (err == JET_wrnBufferTruncated)
{
pekList = (PPEK_LIST)malloc(dwPekListSize);
err = JetRetrieveColumn(sesId, tableId, pekId,
pekList, dwPekListSize, &dwPekListSize, JET_bitNil, NULL);
}


// ensure it's good read and size exceeds size of PEK_LIST structure
if (err == JET_errSuccess && dwPekListSize >= sizeof(PEK_LIST)) {

// decrypt the data returned
// depending on major/minor values in data read
// a salt may or may not be required
EncryptDecryptWithKey(pbSysKey, SYSTEM_KEY_LEN,
pekList.Hdr.bSalt, PEK_SALT_LEN, PEK_SALT_ROUNDS,
(PBYTE)&pekList.Data, dwPekListSize - sizeof(PEK_HDR));

if (pekList->Hdr.dwFlag)
{
if (pekList->Hdr.dwVersion == 0)
{
printf("[x]unsupported version :0\n");
}
else if (pekList->Hdr.dwVersion == 1)
{
printf("[x]unsupported version: 1\n");
}
else if (pekList->Hdr.dwVersion == 2)
{
printf("[+]PEK version: 2k3\n");
EncryptDecryptWithKey(pbSysKey, SYSTEM_KEY_LEN,
pekList->Hdr.bSalt, PEK_SALT_LEN, PEK_SALT_ROUNDS,
(PBYTE)&pekList->Data, dwPekListSize - sizeof(PEK_HDR));
}
else if (pekList->Hdr.dwVersion == 3)
{
printf("[+]PEK version: 2016\n");
DecryptAes(pbSysKey, SYSTEM_KEY_LEN,
pekList->Hdr.bSalt, PEK_SALT_LEN,
(PBYTE)&pekList->Data, dwPekListSize - sizeof(PEK_HDR));
}
else
{
printf("[x]unknown version :%d\n", pekList->Hdr.dwVersion);
}
}
// verify our decryption was successful.
bResult = (memcmp(pekList.Data.bAuth, PekListAuthenticator, PEK_AUTH_LEN) == 0);
bResult = (memcmp(pekList->Data.bAuth, PekListAuthenticator, PEK_AUTH_LEN) == 0);
if (bResult) {
// if good, copy back to buffer
memcpy(pbPEKey, pekList.Data.bKey, PEK_VALUE_LEN);
//if good, copy back to buffer
memcpy(pbPEKey, pekList->Data.entries[0].bKey, PEK_VALUE_LEN);
bResult = 1;
break;
}
}
Expand Down Expand Up @@ -527,7 +591,15 @@ DWORD NTDS::GetColumnData(ULONG columnId, PVOID pbBuffer, DWORD cbBufSize) {
VOID NTDS::PEKDecryptSecretDataBlock(LPBYTE pbData, DWORD dwSize)
{
PSECRET_DATA pSecret = (PSECRET_DATA)pbData;
EncryptDecryptWithKey(pekList.Data.bKey, PEK_VALUE_LEN, pSecret->bSalt, PEK_SALT_LEN, 1, &pSecret->pbData, dwSize-24);
if (pSecret->wType == SECRET_CRYPT_TYPE_AES)
{
DecryptAes(pekList->Data.entries[0].bKey, PEK_VALUE_LEN, pSecret->bSalt, PEK_SALT_LEN, (PBYTE)((DWORD)(&pSecret->pbData)+0x4), dwSize - 24-4);
}
else
{
EncryptDecryptWithKey(pekList->Data.entries[0].bKey, PEK_VALUE_LEN, pSecret->bSalt, PEK_SALT_LEN, 1, &pSecret->pbData, 0x10);
}

}
VOID NTDS::DisplayDecrypted(DWORD rid, PBYTE pbHash, FILE* fp,char fmt) {
BYTE hash[16];
Expand All @@ -538,10 +610,16 @@ VOID NTDS::DisplayDecrypted(DWORD rid, PBYTE pbHash, FILE* fp,char fmt) {
fprintf(fp,c,hash[i]);
}
}
VOID NTDS::DumpHash(DWORD rid, PBYTE pbHash, FILE* fp,char fmt) {
VOID NTDS::DumpHash(DWORD rid, PBYTE pbHash,DWORD dwLength, FILE* fp,char fmt) {

PEKDecryptSecretDataBlock(pbHash, 40);
DisplayDecrypted(rid, pbHash + 24, fp, fmt);
PSECRET_DATA pSecret = (PSECRET_DATA)pbHash;
DWORD offset = 24;
if (pSecret->wType == SECRET_CRYPT_TYPE_AES)
{
offset += 4;
}
PEKDecryptSecretDataBlock(pbHash, dwLength);
DisplayDecrypted(rid, pbHash + offset, fp, fmt);
}

/**********************************************************
Expand Down Expand Up @@ -634,7 +712,7 @@ BOOL NTDS::GetHashes(char fmt, BOOL bHistory, BOOL bInactive, BOOL bMachines, BO
DWORD ret = GetColumnData(lmId, (PVOID)lmHash, sizeof(lmHash));

if (err == JET_errSuccess && ret) {
DumpHash(rid, lmHash, out, fmt);
DumpHash(rid, lmHash,ret, out, fmt);
}
else {
fprintf(out, "aad3b435b51404eeaad3b435b51404ee");
Expand All @@ -644,7 +722,7 @@ BOOL NTDS::GetHashes(char fmt, BOOL bHistory, BOOL bInactive, BOOL bMachines, BO
ret = GetColumnData(ntId, (PVOID)ntHash, sizeof(ntHash));

if (err == JET_errSuccess && ret) {
DumpHash(rid, ntHash, out, fmt);
DumpHash(rid, ntHash,ret, out, fmt);
}
else {
fprintf(out, "31d6cfe0d16ae931b73c59d7e0c089c0");
Expand Down
35 changes: 25 additions & 10 deletions NTDSDumpEx/ntds.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,20 +93,24 @@ typedef enum {
#define PEK_SALT_ROUNDS 1000

typedef struct _PEK_HDR {
DWORD dwMajor; // either 1 or 2
DWORD dwMinor; // possibly inaccurate description
DWORD dwVersion; // 1:unk, 2:2k3, 3:2016
DWORD dwFlag; // possibly inaccurate description
BYTE bSalt[PEK_SALT_LEN]; // added with version 2
} PEK_HDR, *PPEK_HDR;

// unknown1 and unknown2 are used but I haven't investigated
// since the key probably doesn't get changed much

typedef struct _PEK_DATA_ENTRY
{
DWORD dwIndex; //index
BYTE bKey[PEK_VALUE_LEN]; //data
}PEK_DATA_ENTRY, *PPEK_DATA_ENTRY;

typedef struct _PEK_DATA {
BYTE bAuth[PEK_AUTH_LEN]; // verifies if decryption successful
FILETIME ftModified; // when list was last changed
DWORD dwUnknown1; //
DWORD dwCurrentIndex; // current key index
DWORD dwTotalKeys; // total keys in list
DWORD dwUnknown2; //
BYTE bKey[PEK_VALUE_LEN]; // list can support multiple keys but it's not supported here at the moment.
_PEK_DATA_ENTRY entries[1];//entries
} PEK_DATA, *PPEK_DATA;

typedef struct _PEK_LIST {
Expand All @@ -120,14 +124,24 @@ typedef struct _PEK_LIST {
//
// Seems to work fine with those . . .
//
#define SECRET_CRYPT_TYPE_RC4 0x11
#define SECRET_CRYPT_TYPE_AES 0x13
typedef struct _SECRET_DATA {
WORD wVersion; //
WORD wType; //encrypt type aes:0x13 RC4:0x11
WORD wUnknown; // seems reserved
DWORD dwPEKIndex; // key index for PEK_LIST
BYTE bSalt[PEK_SALT_LEN]; // RtlGenRandom();
BYTE pbData;
} SECRET_DATA, *PSECRET_DATA;


typedef struct _AES128_KEY_BLOB
{
BLOBHEADER hdr;
DWORD dwKeySize;
BYTE bKey[16];
}AES128_KEY_BLOB, *PAES128_KEY_BLOB;

// ===============================================

#ifdef DEBUG
Expand All @@ -154,17 +168,18 @@ class NTDS {
std::wstring dbName;
BOOL bPrintSize;

PEK_LIST pekList; // size might exceed structure
PPEK_LIST pekList; // size might exceed structure
// i estimate very rarely
BOOL EnumColumns(VOID);
ULONG GetColumnId(DWORD);
BOOL IsAccountInactive(DWORD);
BOOL IsAccountMachine(DWORD);
DWORD GetColumnData(ULONG, PVOID, DWORD);
VOID DumpHash(DWORD, PBYTE, FILE*,char);
VOID DumpHash(DWORD, PBYTE,DWORD, FILE*,char);
VOID DisplayDecrypted(DWORD, PBYTE, FILE*, char);
VOID PEKDecryptSecretDataBlock(PBYTE, DWORD);
BOOL EncryptDecryptWithKey(PBYTE, DWORD, PBYTE, DWORD, DWORD, PBYTE, DWORD);
BOOL DecryptAes(PBYTE, DWORD, PBYTE, DWORD, PBYTE, DWORD);
std::vector<COLUMN_INFO> columns; // only attributes
public:
NTDS();
Expand Down
Binary file added Windows Server 2016.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 2eddd0a

Please sign in to comment.