From b353be1099b0324437af0d0c37f81403613fbb90 Mon Sep 17 00:00:00 2001 From: jacopoaugelli Date: Tue, 15 Sep 2026 01:07:57 -0800 Subject: [PATCH] Add optional trustee filter --- AD-BOF/LDAP-BOF/LDAP.axs | 8 ++-- AD-BOF/LDAP-BOF/_include/acl_common.h | 3 +- AD-BOF/LDAP-BOF/src/common/acl_common.c | 59 ++++++++++++++++++++++++- AD-BOF/LDAP-BOF/src/get/get-acl.c | 9 ++-- 4 files changed, 71 insertions(+), 8 deletions(-) diff --git a/AD-BOF/LDAP-BOF/LDAP.axs b/AD-BOF/LDAP-BOF/LDAP.axs index c4f706c..52b8ada 100644 --- a/AD-BOF/LDAP-BOF/LDAP.axs +++ b/AD-BOF/LDAP-BOF/LDAP.axs @@ -310,13 +310,14 @@ _cmd_getspn.setPreHook(function (id, cmdline, parsed_json, ...parsed_lines){ var _cmd_getacl = ax.create_command( "get-acl", "Get ACL/security descriptor for an object", - "ldap get-acl jane.doe -ou \"OU=Users,DC=domain,DC=local\" -dc dc01.domain.local --resolve" + "ldap get-acl jane.doe -trustee S-1-5-21-1472672667-4013031263-495691846-1113 -ou \"OU=Users,DC=domain,DC=local\" -dc dc01.domain.local --resolve" ); _cmd_getacl.addArgString("target", true, "Object name or DN"); _cmd_getacl.addArgFlagString("-ou", "ou_path", false, "OU path to search"); _cmd_getacl.addArgFlagString("-dc", "dc_fqdn", false, "Domain Controller FQDN"); _cmd_getacl.addArgBool("--ldaps", "Use LDAPS (port 636)"); _cmd_getacl.addArgBool("--resolve", "Resolve SID names"); +_cmd_getacl.addArgFlagString("-trustee", "trustee", false, "Filter ACLs by trustee SID"); _cmd_getacl.setPreHook(function (id, cmdline, parsed_json, ...parsed_lines){ let target = parsed_json["target"]; let is_dn = identifyInputType(target); @@ -324,8 +325,9 @@ _cmd_getacl.setPreHook(function (id, cmdline, parsed_json, ...parsed_lines){ let dc_fqdn = parsed_json["dc_fqdn"] || ""; let use_ldaps = parsed_json["--ldaps"] ? 1 : 0; let resolve = parsed_json["--resolve"] ? 1 : 0; + let trustee = parsed_json["trustee"] || ""; - let bof_params = ax.bof_pack("cstr,int,cstr,cstr,int,int", [target, is_dn, ou_path, dc_fqdn, use_ldaps, resolve]); + let bof_params = ax.bof_pack("cstr,int,cstr,cstr,int,int,cstr", [target, is_dn, ou_path, dc_fqdn, use_ldaps, resolve, trustee]); let bof_path = ax.script_dir() + "_bin/LDAP/get-acl." + ax.arch(id) + ".o"; ax.execute_alias(id, cmdline, `execute bof "${bof_path}" ${bof_params}`, `Querying ACL for ${target}...`); }); @@ -1398,4 +1400,4 @@ cmd_ldap.addSubCommands([_cmd_removeace, _cmd_removeattribute, _cmd_removedelega var group_ldap = ax.create_commands_group("LDAP-BOF", [cmd_ldap]); -ax.register_commands_group(group_ldap, ["beacon", "gopher", "kharon"], ["windows"], []); \ No newline at end of file +ax.register_commands_group(group_ldap, ["beacon", "gopher", "kharon"], ["windows"], []); diff --git a/AD-BOF/LDAP-BOF/_include/acl_common.h b/AD-BOF/LDAP-BOF/_include/acl_common.h index b5860d4..9c68a7f 100644 --- a/AD-BOF/LDAP-BOF/_include/acl_common.h +++ b/AD-BOF/LDAP-BOF/_include/acl_common.h @@ -440,6 +440,7 @@ char* GetGuidFriendlyName(GUID* guid); // Display utilities void PrintSecurityDescriptorInfo(PSD_INFO sdInfo, const char* objectDN, const char* objectSid); +void PrintFilteredSecurityDescriptorInfo(PSD_INFO sdInfo, const char* objectDN, const char* objectSid, const char* trustee); void PrintAceInfo(PPARSED_ACE_INFO aceInfo, int index, const char* objectDN, const char* objectSid); // ACL modification utilities @@ -449,4 +450,4 @@ PACL CreateNewDaclWithoutAces(PACL oldDacl, DWORD* aceIndicesToRemove, DWORD rem BERVAL* ConvertSecurityDescriptorToBerval(PSECURITY_DESCRIPTOR pSD); PSECURITY_DESCRIPTOR ConvertBervalToSecurityDescriptor(BERVAL* sdBerval); -#endif // ACL_COMMON_H \ No newline at end of file +#endif // ACL_COMMON_H diff --git a/AD-BOF/LDAP-BOF/src/common/acl_common.c b/AD-BOF/LDAP-BOF/src/common/acl_common.c index 72db8e6..816d5cd 100644 --- a/AD-BOF/LDAP-BOF/src/common/acl_common.c +++ b/AD-BOF/LDAP-BOF/src/common/acl_common.c @@ -1192,6 +1192,63 @@ void PrintSecurityDescriptorInfo(PSD_INFO sdInfo, const char* objectDN, const ch BeaconPrintf(CALLBACK_OUTPUT, "=====================================\n"); } +void PrintFilteredSecurityDescriptorInfo(PSD_INFO sdInfo, const char* objectDN, const char* objectSid, const char* trustee) { + if (!sdInfo) return; + + BeaconPrintf(CALLBACK_OUTPUT, "\n[+] Security Descriptor Information:\n====================================="); + + // Owner + if (sdInfo->OwnerSid) { + BeaconPrintf(CALLBACK_OUTPUT, "[*] Owner: %s", sdInfo->OwnerSid); + if (sdInfo->OwnerName) { + BeaconPrintf(CALLBACK_OUTPUT, " Name: %s", sdInfo->OwnerName); + } + } + + // Group + if (sdInfo->GroupSid) { + BeaconPrintf(CALLBACK_OUTPUT, "[*] Group: %s", sdInfo->GroupSid); + if (sdInfo->GroupName) { + BeaconPrintf(CALLBACK_OUTPUT, " Name: %s", sdInfo->GroupName); + } + } + + // Control flags + BeaconPrintf(CALLBACK_OUTPUT, "[*] Control Flags: 0x%04x", sdInfo->ControlFlags); + if (sdInfo->ControlFlags & SE_DACL_PROTECTED) { + BeaconPrintf(CALLBACK_OUTPUT, " - DACL is protected (inheritance blocked)"); + } + if (sdInfo->ControlFlags & SE_SACL_PROTECTED) { + BeaconPrintf(CALLBACK_OUTPUT, " - SACL is protected"); + } + + // DACL + BeaconPrintf(CALLBACK_OUTPUT, "\n[*] DACL (Discretionary Access Control List):"); + if (!sdInfo->HasDacl) { + BeaconPrintf(CALLBACK_OUTPUT, " No DACL present (NULL DACL - everyone has full access!)"); + } else if (sdInfo->DaclAceCount == 0) { + BeaconPrintf(CALLBACK_OUTPUT, " Empty DACL (no one has access)"); + } else { + BeaconPrintf(CALLBACK_OUTPUT, " %d ACE(s):", sdInfo->DaclAceCount); + for (DWORD i = 0; i < sdInfo->DaclAceCount; i++) { + if (!MSVCRT$strcmp(sdInfo->DaclAces[i].TrusteeSid, trustee)) { + PrintAceInfo(&sdInfo->DaclAces[i], i, objectDN, objectSid); + } + } + } + + // SACL (if present) + if (sdInfo->HasSacl && sdInfo->SaclAceCount > 0) { + BeaconPrintf(CALLBACK_OUTPUT, "\n[*] SACL (System Access Control List):"); + BeaconPrintf(CALLBACK_OUTPUT, " %d ACE(s):", sdInfo->SaclAceCount); + for (DWORD i = 0; i < sdInfo->SaclAceCount; i++) { + PrintAceInfo(&sdInfo->SaclAces[i], i, objectDN, objectSid); + } + } + + BeaconPrintf(CALLBACK_OUTPUT, "=====================================\n"); +} + // Print individual ACE information (PowerView format) void PrintAceInfo(PPARSED_ACE_INFO aceInfo, int index, const char* objectDN, const char* objectSid) { if (!aceInfo) return; @@ -1769,4 +1826,4 @@ PSECURITY_DESCRIPTOR ConvertBervalToSecurityDescriptor(BERVAL* sdBerval) { // For BOF simplicity, we'll accept this small memory cost return pAbsoluteSD; -} \ No newline at end of file +} diff --git a/AD-BOF/LDAP-BOF/src/get/get-acl.c b/AD-BOF/LDAP-BOF/src/get/get-acl.c index 538e98c..3ffec1a 100644 --- a/AD-BOF/LDAP-BOF/src/get/get-acl.c +++ b/AD-BOF/LDAP-BOF/src/get/get-acl.c @@ -14,6 +14,7 @@ void go(char *args, int alen) { char* dcAddress = ValidateInput(BeaconDataExtract(&parser, NULL)); int useLdaps = BeaconDataInt(&parser); int resolveNames = BeaconDataInt(&parser); + char* trustee = ValidateInput(BeaconDataExtract(&parser, NULL)); if (!objectIdentifier || MSVCRT$strlen(objectIdentifier) == 0) { BeaconPrintf(CALLBACK_ERROR, "[-] Object identifier is required"); @@ -102,8 +103,10 @@ void go(char *args, int alen) { } // Display security descriptor - PrintSecurityDescriptorInfo(sdInfo, targetDN, objectSidStr); - + if (trustee) { + BeaconPrintf(CALLBACK_OUTPUT, "[+] Filtering Output for %s", trustee); + PrintFilteredSecurityDescriptorInfo(sdInfo, targetDN, objectSidStr, trustee); + } else PrintSecurityDescriptorInfo(sdInfo, targetDN, objectSidStr); // Cleanup FreeSecurityDescriptorInfo(sdInfo); if (objectSidStr) MSVCRT$free(objectSidStr); @@ -116,4 +119,4 @@ void go(char *args, int alen) { if (dcHostname) MSVCRT$free(dcHostname); if (targetDN) MSVCRT$free(targetDN); CleanupLDAP(ld); -} \ No newline at end of file +}