├── Makefile ├── .gitignore ├── LICENSE ├── README.md ├── beacon.h ├── DelegationBOF.cna └── DelegationBOF.c /Makefile: -------------------------------------------------------------------------------- 1 | DelegationBOF: 2 | x86_64-w64-mingw32-gcc -c DelegationBOF.c -o delegationx64.o 3 | x86_64-w64-mingw32-strip -N DelegationBOF.c delegationx64.o 4 | i686-w64-mingw32-gcc -o delegationx86.o -c DelegationBOF.c 5 | i686-w64-mingw32-strip -N Delegation.c delegationx86.o 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Icebreaker Security 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DelegationBOF 2 | 3 | This tool uses LDAP to check a domain for known abusable Kerberos delegation settings. Currently, it supports RBCD, Constrained, Constrained w/Protocol Transition, and Unconstrained Delegation checks. 4 | 5 | Despite the name, I decided to add in a couple more features since the bulk of the code was already there. So now there is a get-spns command as well which can look for ASREP accounts or Kerberoastable SPNs. 6 | 7 | ## Instructions 8 | 9 | Clone, run make, add the .cna to your CS client. 10 | 11 | ### Delegation Accounts 12 | run help get-delegation 13 | 14 | Syntax: get-delegation [Type] [optional: FQDN] 15 | 16 | Type options : RBCD, Constrained, ConstrainedProto, Unconstrained, All 17 | 18 | If no domain is provided, the local domain is used. 19 | 20 | ### Kerberoastable Accounts 21 | run help get-spns 22 | 23 | Syntax: get-spns [Type] [optional: FQDN] 24 | 25 | Type options : spns, ASREP, All 26 | 27 | If no domain is provided, the local domain is used. 28 | 29 | ## Potential issues 30 | In order to make the output not terrible I'm using Cobalt Strike's built in BeaconFormatAlloc fuction. This requires a preset buffer, which I set to 2048. If you are testing in a large domain I would suggest increasing this before running. 31 | 32 | 33 | -------------------------------------------------------------------------------- /beacon.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Beacon Object Files (BOF) 3 | * ------------------------- 4 | * A Beacon Object File is a light-weight post exploitation tool that runs 5 | * with Beacon's inline-execute command. 6 | * 7 | * Cobalt Strike 4.1. 8 | */ 9 | 10 | /* data API */ 11 | typedef struct { 12 | char * original; /* the original buffer [so we can free it] */ 13 | char * buffer; /* current pointer into our buffer */ 14 | int length; /* remaining length of data */ 15 | int size; /* total size of this buffer */ 16 | } datap; 17 | 18 | DECLSPEC_IMPORT void BeaconDataParse(datap * parser, char * buffer, int size); 19 | DECLSPEC_IMPORT int BeaconDataInt(datap * parser); 20 | DECLSPEC_IMPORT short BeaconDataShort(datap * parser); 21 | DECLSPEC_IMPORT int BeaconDataLength(datap * parser); 22 | DECLSPEC_IMPORT char * BeaconDataExtract(datap * parser, int * size); 23 | 24 | /* format API */ 25 | typedef struct { 26 | char * original; /* the original buffer [so we can free it] */ 27 | char * buffer; /* current pointer into our buffer */ 28 | int length; /* remaining length of data */ 29 | int size; /* total size of this buffer */ 30 | } formatp; 31 | 32 | DECLSPEC_IMPORT void BeaconFormatAlloc(formatp * format, int maxsz); 33 | DECLSPEC_IMPORT void BeaconFormatReset(formatp * format); 34 | DECLSPEC_IMPORT void BeaconFormatFree(formatp * format); 35 | DECLSPEC_IMPORT void BeaconFormatAppend(formatp * format, char * text, int len); 36 | DECLSPEC_IMPORT void BeaconFormatPrintf(formatp * format, char * fmt, ...); 37 | DECLSPEC_IMPORT char * BeaconFormatToString(formatp * format, int * size); 38 | DECLSPEC_IMPORT void BeaconFormatInt(formatp * format, int value); 39 | 40 | /* Output Functions */ 41 | #define CALLBACK_OUTPUT 0x0 42 | #define CALLBACK_OUTPUT_OEM 0x1e 43 | #define CALLBACK_ERROR 0x0d 44 | #define CALLBACK_OUTPUT_UTF8 0x20 45 | 46 | DECLSPEC_IMPORT void BeaconPrintf(int type, char * fmt, ...); 47 | DECLSPEC_IMPORT void BeaconOutput(int type, char * data, int len); 48 | 49 | /* Token Functions */ 50 | DECLSPEC_IMPORT BOOL BeaconUseToken(HANDLE token); 51 | DECLSPEC_IMPORT void BeaconRevertToken(); 52 | DECLSPEC_IMPORT BOOL BeaconIsAdmin(); 53 | 54 | /* Spawn+Inject Functions */ 55 | DECLSPEC_IMPORT void BeaconGetSpawnTo(BOOL x86, char * buffer, int length); 56 | DECLSPEC_IMPORT void BeaconInjectProcess(HANDLE hProc, int pid, char * payload, int p_len, int p_offset, char * arg, int a_len); 57 | DECLSPEC_IMPORT void BeaconInjectTemporaryProcess(PROCESS_INFORMATION * pInfo, char * payload, int p_len, int p_offset, char * arg, int a_len); 58 | DECLSPEC_IMPORT void BeaconCleanupProcess(PROCESS_INFORMATION * pInfo); 59 | 60 | /* Utility Functions */ 61 | DECLSPEC_IMPORT BOOL toWideChar(char * src, wchar_t * dst, int max); 62 | -------------------------------------------------------------------------------- /DelegationBOF.cna: -------------------------------------------------------------------------------- 1 | beacon_command_register( 2 | "get-delegation", 3 | "Enumerate a given domain for different types of abusable Kerberos Delegation settings.", 4 | "Synopsis: get-delegation [Single option or All] [FQDN]. Current options: Constrained, Unconstrained, ConstrainedProto, RBCD. Domain is optional, if not used then the current domain will be checked"); 5 | 6 | beacon_command_register( 7 | "get-spns", 8 | "Enumerate a given domain for user accounts with SPNs and ASREP.", 9 | "Synopsis: get-spns [Single option or All] [FQDN]. Current options: spn, ASREP. Domain is optional, if not used then the current domain will be checked"); 10 | 11 | 12 | 13 | alias get-delegation { 14 | 15 | $data = substr($0, 15); 16 | @args = split(' ', $data); 17 | $type = 0; 18 | $domain = "local"; 19 | 20 | if(@args[0] iswm "Constrained") 21 | { 22 | $type = 1; 23 | } 24 | else if(@args[0] iswm "ConstrainedProto") 25 | { 26 | $type = 2; 27 | } 28 | else if(@args[0] iswm "Unconstrained") 29 | { 30 | $type = 3; 31 | } 32 | else if(@args[0] iswm "RBCD") 33 | { 34 | $type = 4; 35 | } 36 | else if(@args[0] iswm "All") 37 | { 38 | $type = 5; 39 | } 40 | if($type == 0) 41 | { 42 | berror($1,"Error: Not a valid option, use help get-delegation to see current options"); 43 | return; 44 | } 45 | 46 | if(size(@args) == 2) 47 | { 48 | $domain = @args[1]; 49 | } 50 | 51 | # figure out the arch of this session 52 | $barch = barch($1); 53 | 54 | # read in the right BOF file 55 | $handle = openf(script_resource("delegation $+ $barch $+ .o")); 56 | $bof = readb($handle, -1); 57 | closef($handle); 58 | if(strlen($bof) < 1) 59 | { 60 | berror($1,"Error: BOF bin could not be found. Please ensure the compiled BOF (.o file) exists in the same folder as this aggressor script"); 61 | return; 62 | } 63 | 64 | $args = bof_pack($1, "iZ", $type, $domain); 65 | 66 | 67 | btask($1, "Running get-delegation"); 68 | 69 | # execute it. 70 | beacon_inline_execute($1, $bof, "go", $args); 71 | 72 | 73 | 74 | } 75 | 76 | alias get-spns { 77 | 78 | $data = substr($0, 9); 79 | @args = split(' ', $data); 80 | $type = 0; 81 | $domain = "local"; 82 | 83 | if(@args[0] iswm "spn") 84 | { 85 | $type = 6; 86 | } 87 | else if(@args[0] iswm "ASREP") 88 | { 89 | $type = 7; 90 | } 91 | else if(@args[0] iswm "All") 92 | { 93 | $type = 8; 94 | } 95 | if($type == 0) 96 | { 97 | berror($1,"Error: Not a valid option, use help get-spns to see current options"); 98 | return; 99 | } 100 | 101 | if(size(@args) == 2) 102 | { 103 | $domain = @args[1]; 104 | } 105 | 106 | # figure out the arch of this session 107 | $barch = barch($1); 108 | 109 | # read in the right BOF file 110 | $handle = openf(script_resource("delegation $+ $barch $+ .o")); 111 | $bof = readb($handle, -1); 112 | closef($handle); 113 | if(strlen($bof) < 1) 114 | { 115 | berror($1,"Error: BOF bin could not be found. Please ensure the compiled BOF (.o file) exists in the same folder as this aggressor script"); 116 | return; 117 | } 118 | 119 | $args = bof_pack($1, "iZ", $type, $domain); 120 | 121 | 122 | btask($1, "Running get-spns"); 123 | 124 | # execute it. 125 | beacon_inline_execute($1, $bof, "go", $args); 126 | } 127 | -------------------------------------------------------------------------------- /DelegationBOF.c: -------------------------------------------------------------------------------- 1 | #define _CRT_SECURE_NO_WARNINGS 2 | #include 3 | #include "beacon.h" 4 | #include "Iads.h" 5 | 6 | #define S_ADS_NOMORE_ROWS _HRESULT_TYPEDEF_(0x00005012L) 7 | #define S_ADS_NOMORE_COLUMNS _HRESULT_TYPEDEF_(0x00005013L) 8 | 9 | WINBASEAPI BOOL WINAPI KERNEL32$GetComputerNameExW(COMPUTER_NAME_FORMAT,LPWSTR, LPDWORD); 10 | WINBASEAPI HMODULE WINAPI KERNEL32$LoadLibraryW(LPCWSTR lpLibFileName); 11 | WINBASEAPI BOOL WINAPI KERNEL32$FileTimeToLocalFileTime(const FILETIME*, LPFILETIME); 12 | WINBASEAPI BOOL WINAPI KERNEL32$FileTimeToSystemTime(const FILETIME*, LPFILETIME); 13 | WINBASEAPI DWORD WINAPI KERNEL32$GetLastError(); 14 | WINBASEAPI void* WINAPI KERNEL32$HeapAlloc(HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes); 15 | WINBASEAPI HANDLE WINAPI KERNEL32$GetProcessHeap(); 16 | WINBASEAPI BOOL WINAPI KERNEL32$HeapFree(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem); 17 | 18 | WINBASEAPI HRESULT WINAPI OLE32$CoInitialize(LPVOID pvReserved); 19 | WINBASEAPI void WINAPI OLE32$CoUninitialize(); 20 | 21 | WINBASEAPI INT WINAPI OLEAUT32$SystemTimeToVariantTime(LPSYSTEMTIME, DOUBLE*); 22 | WINBASEAPI HRESULT WINAPI OLEAUT32$VariantChangeType(VARIANTARG* ,const VARIANTARG* ,USHORT ,VARTYPE ); 23 | WINBASEAPI HRESULT WINAPI OLEAUT32$VariantClear(VARIANT*); 24 | WINBASEAPI void WINAPI OLEAUT32$VariantInit(VARIANT*); 25 | 26 | 27 | WINBASEAPI wchar_t* __cdecl MSVCRT$wcscat(wchar_t* dest, const wchar_t* src); 28 | WINBASEAPI wchar_t* __cdecl MSVCRT$wcscpy(wchar_t* dest, const wchar_t* src); 29 | WINBASEAPI int __cdecl MSVCRT$wcscmp(const wchar_t* wcs1, const wchar_t* wcs2); 30 | 31 | WINBASEAPI BOOL WINAPI ADVAPI32$LookupAccountSidW(LPCWSTR lpSystemName, PSID Sid, LPWSTR Name, LPDWORD cchName, LPWSTR ReferencedDomainName, LPDWORD cchReferencedDomainName, PSID_NAME_USE peUse); 32 | WINBASEAPI BOOL WINAPI ADVAPI32$GetSecurityDescriptorDacl(PSECURITY_DESCRIPTOR pSecurityDescriptor,LPBOOL lpbDaclPresent, PACL* pDacl,LPBOOL lpbDaclDefaulted); 33 | WINBASEAPI BOOL WINAPI ADVAPI32$GetAce(PACL pAcl,DWORD dwAceIndex, LPVOID* pAce); 34 | 35 | typedef HRESULT(WINAPI* _ADsOpenObject)(LPCWSTR lpszPathName, LPCWSTR lpszUserName,LPCWSTR lpszPassword, DWORD dwReserved,REFIID riid,void** ppObject); 36 | typedef BOOL(WINAPI* _FreeADsMem)(LPVOID); 37 | 38 | BOOL GetCurrentDomain(wchar_t* pPath) 39 | { 40 | wchar_t domain[50] = L""; 41 | DWORD dwDomainSize = sizeof(domain); 42 | //ComputerNameDnsDomain = 2 43 | BOOL success = KERNEL32$GetComputerNameExW(2, domain, &dwDomainSize); 44 | if (!success) 45 | { 46 | BeaconPrintf(CALLBACK_ERROR, "Error GetComputerNameExW : %d\n", KERNEL32$GetLastError()); 47 | return FALSE; 48 | } 49 | 50 | MSVCRT$wcscat(pPath, domain); 51 | 52 | return TRUE; 53 | } 54 | //REF: https://docs.microsoft.com/en-us/windows/win32/secauthz/finding-the-owner-of-a-file-object-in-c-- 55 | void MapSidToAcct(PSID pSid, formatp* obj) 56 | { 57 | BOOL bRtnBool = TRUE; 58 | LPTSTR AcctName = NULL; 59 | LPTSTR DomainName = NULL; 60 | DWORD dwAcctName = 1, dwDomainName = 1; 61 | SID_NAME_USE eUse = 8; //SidTypeUnknown 62 | HANDLE hFile; 63 | PSECURITY_DESCRIPTOR pSD = NULL; 64 | 65 | // First call to LookupAccountSid to get the buffer sizes. 66 | bRtnBool = ADVAPI32$LookupAccountSidW(NULL, pSid, AcctName, (LPDWORD)&dwAcctName, DomainName, (LPDWORD)&dwDomainName, &eUse); 67 | 68 | // Reallocate memory for the buffers. 69 | AcctName = (LPTSTR)KERNEL32$HeapAlloc(KERNEL32$GetProcessHeap(), HEAP_ZERO_MEMORY, dwAcctName); 70 | DomainName = (LPTSTR)KERNEL32$HeapAlloc(KERNEL32$GetProcessHeap(), HEAP_ZERO_MEMORY, dwDomainName); 71 | 72 | // Second call to LookupAccountSid to get the account name. 73 | bRtnBool = ADVAPI32$LookupAccountSidW(NULL, pSid, AcctName, (LPDWORD)&dwAcctName, DomainName, (LPDWORD)&dwDomainName, &eUse); 74 | // Check GetLastError for LookupAccountSid error condition. 75 | if (bRtnBool == FALSE) { 76 | DWORD dwErrorCode = 0; 77 | 78 | dwErrorCode = KERNEL32$GetLastError(); 79 | 80 | if (dwErrorCode == ERROR_NONE_MAPPED) 81 | BeaconPrintf(CALLBACK_ERROR,"Account owner not found for specified SID.\n"); 82 | else 83 | BeaconPrintf(CALLBACK_OUTPUT,"Error in LookupAccountSid.\n"); 84 | return; 85 | 86 | } 87 | 88 | BeaconFormatPrintf(obj,"\t%ls\\%ls\n", DomainName, AcctName); 89 | 90 | KERNEL32$HeapFree(KERNEL32$GetProcessHeap(), 0, DomainName); 91 | KERNEL32$HeapFree(KERNEL32$GetProcessHeap(), 0, AcctName); 92 | 93 | } 94 | 95 | VOID LdapSearch(wchar_t* myFilter, wchar_t* lpszPathName) 96 | { 97 | 98 | 99 | HRESULT hr = S_OK; 100 | ADS_SEARCH_COLUMN col; 101 | // Can add support for these later 102 | LPWSTR szUsername = NULL; 103 | LPWSTR szPassword = NULL; 104 | 105 | IDirectorySearch* pDSSearch = NULL; 106 | 107 | ADS_SEARCH_HANDLE hSearch; 108 | DWORD dwCount = 0; 109 | unsigned int i = 0; 110 | 111 | LPWSTR pColumn; 112 | FILETIME filetime; 113 | LARGE_INTEGER liValue; 114 | SYSTEMTIME systemtime; 115 | DATE date; 116 | VARIANT varDate; 117 | OLEAUT32$VariantInit(&varDate); 118 | 119 | //Initialize beaconformat 120 | formatp obj; 121 | int len = 2048; 122 | BeaconFormatAlloc(&obj, len); 123 | 124 | // Initialize COM. 125 | OLE32$CoInitialize(0); 126 | 127 | HMODULE hActiveds = KERNEL32$LoadLibraryW(L"Activeds.dll"); 128 | _ADsOpenObject pADsOpenObject = (_ADsOpenObject)GetProcAddress(hActiveds, "ADsOpenObject"); 129 | _FreeADsMem pFreeADsMem = (_FreeADsMem)GetProcAddress(hActiveds, "FreeADsMem"); 130 | 131 | // Open a connection with server. 132 | static GUID xIID_IDirectorySearch = { 0x109ba8ec, 0x92f0, 0x11d0, {0xa7, 0x90, 0x00, 0xc0, 0x4f, 0xd8, 0xd5, 0xa8} }; 133 | hr = pADsOpenObject((LPCWSTR)lpszPathName, szUsername, szPassword, ADS_SECURE_AUTHENTICATION, &xIID_IDirectorySearch, (void**)&pDSSearch); 134 | 135 | if (!SUCCEEDED(hr)) 136 | { 137 | BeaconPrintf(CALLBACK_ERROR,"ADsOpenObject failed\n"); 138 | goto DONE; 139 | } 140 | 141 | //Search specific attributes 142 | pDSSearch->lpVtbl->ExecuteSearch(pDSSearch, (LPWSTR)myFilter, NULL, (DWORD)-1, &hSearch); 143 | 144 | if (hSearch == NULL) 145 | { 146 | BeaconPrintf(CALLBACK_ERROR,"Failed execute search\n"); 147 | goto DONE; 148 | } 149 | 150 | while (pDSSearch->lpVtbl->GetNextRow(pDSSearch, hSearch) != S_ADS_NOMORE_ROWS) 151 | { 152 | while (pDSSearch->lpVtbl->GetNextColumnName(pDSSearch, hSearch, &pColumn) != S_ADS_NOMORE_COLUMNS) 153 | { 154 | hr = pDSSearch->lpVtbl->GetColumn(pDSSearch, hSearch, pColumn, &col); 155 | if (SUCCEEDED(hr)) 156 | { 157 | 158 | if (col.dwADsType == ADSTYPE_PATH) 159 | { 160 | for (i = 0; i < col.dwNumValues; i++) 161 | { 162 | BeaconFormatPrintf(&obj,"%ls : %ls\n", pColumn, col.pADsValues->CaseIgnoreString); 163 | } 164 | } 165 | if (col.dwADsType == ADSTYPE_NT_SECURITY_DESCRIPTOR) 166 | { 167 | for (i = 0; i < col.dwNumValues; i++) 168 | { 169 | ADS_NT_SECURITY_DESCRIPTOR sec = col.pADsValues[i].SecurityDescriptor; 170 | 171 | BOOL bDaclPresent = SE_DACL_PRESENT; 172 | BOOL bDaclDefaulted = SE_DACL_DEFAULTED; 173 | PACL pDacl = NULL; 174 | LPVOID pAce; 175 | ACCESS_ALLOWED_ACE* pAceBuffer; 176 | PSID pSid = NULL; 177 | wchar_t* accountName = NULL; 178 | ADVAPI32$GetSecurityDescriptorDacl((PSECURITY_DESCRIPTOR)sec.lpValue, &bDaclPresent, &pDacl, &bDaclDefaulted); 179 | if (bDaclPresent) 180 | { 181 | BeaconFormatPrintf(&obj,"[*]%ls: \n", pColumn); 182 | for (i = 0; i < pDacl->AceCount; i++) 183 | { 184 | ADVAPI32$GetAce(pDacl, i, &pAce); 185 | 186 | pAceBuffer = (ACCESS_ALLOWED_ACE*)pAce; 187 | pSid = (PSID)&pAceBuffer->SidStart; 188 | 189 | MapSidToAcct(pSid, &obj); 190 | 191 | 192 | 193 | } 194 | } 195 | 196 | } 197 | } 198 | 199 | if (col.dwADsType == ADSTYPE_BOOLEAN) 200 | { 201 | BOOL dwBool; 202 | const wchar_t* pBool = NULL; 203 | for (i = 0; i < col.dwNumValues; i++) 204 | { 205 | dwBool = col.pADsValues[i].Boolean; 206 | pBool = dwBool ? L"TRUE" : L"FALSE"; 207 | BeaconFormatPrintf(&obj,"[*]%ls : %ls\n", pColumn, pBool); 208 | 209 | } 210 | 211 | } 212 | 213 | if (col.dwADsType == ADSTYPE_DN_STRING) 214 | { 215 | for (i = 0; i < col.dwNumValues; i++) 216 | { 217 | BeaconFormatPrintf(&obj,"[*]%ls : %ls\n", pColumn, col.pADsValues[i].DNString); 218 | 219 | } 220 | 221 | } 222 | if (col.dwADsType == ADSTYPE_CASE_IGNORE_STRING) 223 | { 224 | for (i = 0; i < col.dwNumValues; i++) 225 | { 226 | BeaconFormatPrintf(&obj,"[*]%ls : %ls\n", pColumn, col.pADsValues[i].CaseIgnoreString); 227 | } 228 | } 229 | 230 | if (col.dwADsType == ADSTYPE_LARGE_INTEGER) 231 | { 232 | for (unsigned int x = 0; x < col.dwNumValues; x++) 233 | { 234 | liValue = col.pADsValues[x].LargeInteger; 235 | filetime.dwLowDateTime = liValue.LowPart; 236 | filetime.dwHighDateTime = liValue.HighPart; 237 | if ((filetime.dwHighDateTime == 0) && (filetime.dwLowDateTime == 0)) 238 | { 239 | continue; 240 | } 241 | else 242 | { 243 | 244 | if (filetime.dwLowDateTime == -1) 245 | { 246 | BeaconFormatPrintf(&obj,"[*]%ls : Never Expires.\n", pColumn); 247 | } 248 | else 249 | { 250 | if (KERNEL32$FileTimeToLocalFileTime(&filetime, &filetime) != 0) 251 | { 252 | if (KERNEL32$FileTimeToSystemTime(&filetime, &systemtime) != 0) 253 | { 254 | if (OLEAUT32$SystemTimeToVariantTime(&systemtime, &date) != 0) 255 | { 256 | varDate.vt = VT_DATE; 257 | varDate.date = date; 258 | OLEAUT32$VariantChangeType(&varDate, &varDate, VARIANT_NOVALUEPROP, VT_BSTR); 259 | BeaconFormatPrintf(&obj,"[*]%ls : %ls\n", pColumn, (wchar_t*)varDate.bstrVal); 260 | 261 | OLEAUT32$VariantClear(&varDate); 262 | } 263 | } 264 | 265 | } 266 | } 267 | } 268 | 269 | } 270 | 271 | } 272 | pDSSearch->lpVtbl->FreeColumn(pDSSearch, &col); 273 | 274 | } 275 | 276 | 277 | pFreeADsMem(pColumn); 278 | } 279 | BeaconFormatPrintf(&obj,"\n\n"); 280 | } 281 | pDSSearch->lpVtbl->CloseSearchHandle(pDSSearch, hSearch); 282 | int outSize = 0; 283 | char* dataOut = BeaconFormatToString(&obj, &outSize); 284 | BeaconOutput(CALLBACK_OUTPUT, dataOut, outSize); 285 | 286 | DONE: 287 | if (pDSSearch) 288 | pDSSearch->lpVtbl->Release(pDSSearch); 289 | OLE32$CoUninitialize(); 290 | BeaconFormatFree(&obj); 291 | 292 | return; 293 | } 294 | 295 | VOID FindDelegation(wchar_t* domain, int type) 296 | { 297 | 298 | wchar_t path[50] = L""; 299 | MSVCRT$wcscpy(path, L"LDAP://"); 300 | wchar_t* myFilter = L"(&(userAccountControl:1.2.840.113556.1.4.803:=524288)(!(UserAccountControl:1.2.840.113556.1.4.803:=2)))"; 301 | wchar_t* myFilter2 = L"(&(userAccountControl:1.2.840.113556.1.4.803:=16777216)(!(UserAccountControl:1.2.840.113556.1.4.803:=2)))"; 302 | wchar_t* myFilter3 = L"(&(msDS-AllowedToDelegateTo=*)(!(UserAccountControl:1.2.840.113556.1.4.803:=2)))"; 303 | wchar_t* myFilter4 = L"(&(msDS-AllowedToActOnBehalfOfOtherIdentity=*)(!(UserAccountControl:1.2.840.113556.1.4.803:=2)))"; 304 | wchar_t* myFilter5 = L"(&(samAccountType=805306368)(!samAccountName=krbtgt)(serviceprincipalname=*)(!(UserAccountControl:1.2.840.113556.1.4.803:=2)))"; 305 | wchar_t* myFilter6 = L"(&(userAccountControl:1.2.840.113556.1.4.803 : = 4194304)(!(UserAccountControl:1.2.840.113556.1.4.803 : = 2)))"; 306 | 307 | if (MSVCRT$wcscmp(domain, L"local") == 0) 308 | { 309 | BOOL success = GetCurrentDomain(&path); 310 | if (!success) 311 | return; 312 | } 313 | else 314 | { 315 | MSVCRT$wcscat(path, domain); 316 | } 317 | //It didn't like switch 318 | if (type == 1){ 319 | BeaconPrintf(CALLBACK_OUTPUT, "\n[+]Find Contrained Delegation...\n\n"); 320 | LdapSearch(myFilter3, path); 321 | } 322 | else if (type == 2){ 323 | BeaconPrintf(CALLBACK_OUTPUT, "\n[+]Find Contrained Delegation w/ Protocol Transition...\n\n"); 324 | LdapSearch(myFilter2, path); 325 | } 326 | else if (type == 3){ 327 | BeaconPrintf(CALLBACK_OUTPUT, "\n[+]Finding Unconstrained Delegation...\n\n"); 328 | LdapSearch(myFilter, path); 329 | } 330 | else if (type == 4){ 331 | BeaconPrintf(CALLBACK_OUTPUT, "\n[+]Find RBCD...\n\n"); 332 | LdapSearch(myFilter4, path); 333 | } 334 | else if (type == 5){ 335 | BeaconPrintf(CALLBACK_OUTPUT, "\n[+]Finding Unconstrained Delegation...\n\n"); 336 | LdapSearch(myFilter, path); 337 | 338 | BeaconPrintf(CALLBACK_OUTPUT, "\n[+]Find Contrained Delegation w/ Protocol Transition...\n\n"); 339 | LdapSearch(myFilter2, path); 340 | 341 | BeaconPrintf(CALLBACK_OUTPUT, "\n[+]Find Contrained Delegation...\n\n"); 342 | LdapSearch(myFilter3, path); 343 | 344 | BeaconPrintf(CALLBACK_OUTPUT, "\n[+]Find RBCD...\n\n"); 345 | LdapSearch(myFilter4, path); 346 | } 347 | else if (type == 6){ 348 | BeaconPrintf(CALLBACK_OUTPUT, "\n[+]Find User SPNs...\n\n"); 349 | LdapSearch(myFilter5, path); 350 | } 351 | else if (type == 7){ 352 | BeaconPrintf(CALLBACK_OUTPUT, "\n[+]Find ASREP Accounts...\n\n"); 353 | LdapSearch(myFilter6, path); 354 | } 355 | else if (type == 8){ 356 | BeaconPrintf(CALLBACK_OUTPUT, "\n[+]Find User SPNs...\n\n"); 357 | LdapSearch(myFilter5, path); 358 | BeaconPrintf(CALLBACK_OUTPUT, "\n[+]Find ASREP Accounts...\n\n"); 359 | LdapSearch(myFilter6, path); 360 | } 361 | else 362 | { 363 | BeaconPrintf(CALLBACK_ERROR, "\nNot a Valid Option!\n\n"); 364 | } 365 | 366 | 367 | } 368 | 369 | void go(char* args, int length) { 370 | 371 | datap parser; 372 | int type; 373 | wchar_t* domain = NULL; 374 | BeaconDataParse(&parser, args, length); 375 | 376 | type = BeaconDataInt(&parser); 377 | domain = BeaconDataExtract(&parser, NULL); 378 | 379 | FindDelegation(domain, type); 380 | BeaconPrintf(CALLBACK_OUTPUT, "[*] Complete!\n"); 381 | } 382 | --------------------------------------------------------------------------------