Files
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

198 lines
6.7 KiB
Python

#!/usr/bin/env python3
"""Agent for detecting T1548 Abuse Elevation Control Mechanism (UAC bypass, sudo abuse)."""
import argparse
import json
import os
import re
import subprocess
import sys
from datetime import datetime, timezone
UAC_BYPASS_BINARIES = [
"fodhelper.exe", "computerdefaults.exe", "eventvwr.exe",
"sdclt.exe", "slui.exe", "cmstp.exe", "mmc.exe",
"wsreset.exe", "changepk.exe", "dism.exe",
]
UAC_BYPASS_REGISTRY_KEYS = [
r"HKCU\Software\Classes\ms-settings\Shell\Open\command",
r"HKCU\Software\Classes\mscfile\Shell\Open\command",
r"HKCU\Software\Classes\exefile\Shell\Open\command",
r"HKCU\Software\Microsoft\Windows\CurrentVersion\App Paths",
]
SUDO_ABUSE_PATTERNS = [
r"sudo\s+-u\s+\#-1",
r"sudo\s+.*NOPASSWD",
r"sudo\s+.*env_reset.*env_keep",
r"sudo\s+\S+\s+/bin/bash",
r"sudo\s+find\s+.*-exec",
r"sudo\s+vim\s+-c\s+.*!",
r"sudo\s+python\s+-c",
]
def check_uac_bypass_registry():
"""Check for UAC bypass registry modifications."""
findings = []
if sys.platform != "win32":
return findings
for key in UAC_BYPASS_REGISTRY_KEYS:
try:
result = subprocess.check_output(
["reg", "query", key], text=True, errors="replace", timeout=5
)
if result.strip() and "ERROR" not in result:
findings.append({
"type": "uac_registry",
"key": key,
"value": result.strip()[:200],
"severity": "HIGH",
})
except subprocess.SubprocessError:
pass
return findings
def check_uac_bypass_processes():
"""Check Sysmon logs for auto-elevate binary abuse."""
findings = []
if sys.platform != "win32":
return findings
ps_cmd = (
"Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-Sysmon/Operational';"
"Id=1} -MaxEvents 500 "
"| Where-Object {$_.Properties[4].Value -match '"
+ "|".join(UAC_BYPASS_BINARIES)
+ "'} | Select-Object TimeCreated,"
"@{N='Image';E={$_.Properties[4].Value}},"
"@{N='CommandLine';E={$_.Properties[10].Value}},"
"@{N='ParentImage';E={$_.Properties[20].Value}} "
"| ConvertTo-Json"
)
try:
result = subprocess.check_output(
["powershell", "-NoProfile", "-Command", ps_cmd],
text=True, errors="replace", timeout=30
)
data = json.loads(result) if result.strip() else []
if not isinstance(data, list):
data = [data]
for evt in data:
parent = (evt.get("ParentImage", "") or "").lower()
if "explorer.exe" not in parent and "svchost.exe" not in parent:
findings.append({
"type": "uac_auto_elevate",
"time": evt.get("TimeCreated", ""),
"image": evt.get("Image", ""),
"parent": evt.get("ParentImage", ""),
"commandline": evt.get("CommandLine", "")[:200],
})
except (subprocess.SubprocessError, json.JSONDecodeError):
pass
return findings
def check_linux_sudo_abuse():
"""Check auth logs for sudo abuse patterns."""
findings = []
if sys.platform == "win32":
return findings
log_paths = ["/var/log/auth.log", "/var/log/secure"]
for log_path in log_paths:
if not os.path.isfile(log_path):
continue
try:
with open(log_path, "r", errors="replace") as f:
for line in f:
if "sudo" not in line.lower():
continue
for pat in SUDO_ABUSE_PATTERNS:
if re.search(pat, line, re.IGNORECASE):
findings.append({
"type": "sudo_abuse",
"log": log_path,
"line": line.strip()[:200],
"pattern": pat,
})
except PermissionError:
pass
try:
result = subprocess.check_output(
["sudo", "-l", "-n"], text=True, errors="replace", timeout=5
)
if "NOPASSWD" in result:
for line in result.splitlines():
if "NOPASSWD" in line:
findings.append({
"type": "nopasswd_sudo",
"rule": line.strip(),
"severity": "MEDIUM",
})
except subprocess.SubprocessError:
pass
return findings
def check_setuid_binaries():
"""Find SUID/SGID binaries that could be abused for elevation."""
findings = []
if sys.platform == "win32":
return findings
try:
result = subprocess.check_output(
["find", "/", "-perm", "-4000", "-type", "f"],
text=True, errors="replace", timeout=30, stderr=subprocess.DEVNULL
)
known_suid = {"/usr/bin/sudo", "/usr/bin/passwd", "/usr/bin/su",
"/usr/bin/mount", "/usr/bin/umount", "/usr/bin/ping"}
for line in result.strip().splitlines():
path = line.strip()
if path and path not in known_suid:
findings.append({"type": "unusual_suid", "path": path})
except subprocess.SubprocessError:
pass
return findings
def main():
parser = argparse.ArgumentParser(
description="Detect T1548 Abuse Elevation Control Mechanism"
)
parser.add_argument("--output", "-o", help="Output JSON report")
parser.add_argument("--verbose", "-v", action="store_true")
args = parser.parse_args()
print("[*] T1548 Elevation Abuse Detection Agent")
report = {"timestamp": datetime.now(timezone.utc).isoformat(), "findings": {}}
if sys.platform == "win32":
reg = check_uac_bypass_registry()
procs = check_uac_bypass_processes()
report["findings"]["uac_registry"] = reg
report["findings"]["uac_processes"] = procs
print(f"[*] UAC registry: {len(reg)}, UAC process: {len(procs)}")
else:
sudo = check_linux_sudo_abuse()
suid = check_setuid_binaries()
report["findings"]["sudo_abuse"] = sudo
report["findings"]["suid_binaries"] = suid
print(f"[*] Sudo abuse: {len(sudo)}, SUID binaries: {len(suid)}")
total = sum(len(v) if isinstance(v, list) else 0 for v in report["findings"].values())
report["risk_level"] = "CRITICAL" if total >= 5 else "HIGH" if total >= 2 else "MEDIUM" 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()