Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions AD-BOF/LDAP-BOF/LDAP.axs
Original file line number Diff line number Diff line change
Expand Up @@ -310,22 +310,24 @@ _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);
let ou_path = parsed_json["ou_path"] || "";
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}...`);
});
Expand Down Expand Up @@ -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"], []);
ax.register_commands_group(group_ldap, ["beacon", "gopher", "kharon"], ["windows"], []);
3 changes: 2 additions & 1 deletion AD-BOF/LDAP-BOF/_include/acl_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
#endif // ACL_COMMON_H
59 changes: 58 additions & 1 deletion AD-BOF/LDAP-BOF/src/common/acl_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -1769,4 +1826,4 @@ PSECURITY_DESCRIPTOR ConvertBervalToSecurityDescriptor(BERVAL* sdBerval) {
// For BOF simplicity, we'll accept this small memory cost

return pAbsoluteSD;
}
}
9 changes: 6 additions & 3 deletions AD-BOF/LDAP-BOF/src/get/get-acl.c
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -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);
Expand All @@ -116,4 +119,4 @@ void go(char *args, int alen) {
if (dcHostname) MSVCRT$free(dcHostname);
if (targetDN) MSVCRT$free(targetDN);
CleanupLDAP(ld);
}
}