mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-11 21:54:56 +03:00
c21af3347e
- 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
209 lines
8.4 KiB
Python
209 lines
8.4 KiB
Python
#!/usr/bin/env python3
|
|
"""Agent for performing Linux privilege escalation enumeration — authorized testing only."""
|
|
|
|
import json
|
|
import argparse
|
|
import subprocess
|
|
import os
|
|
import re
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
|
|
|
|
def enumerate_system_info():
|
|
"""Gather basic system information for privilege escalation assessment."""
|
|
cmds = {
|
|
"hostname": ["hostname"],
|
|
"kernel": ["uname", "-a"],
|
|
"os_release": ["cat", "/etc/os-release"],
|
|
"architecture": ["uname", "-m"],
|
|
"current_user": ["whoami"],
|
|
"id": ["id"],
|
|
"env_path": ["echo", "$PATH"],
|
|
}
|
|
results = {}
|
|
for key, cmd in cmds.items():
|
|
try:
|
|
result = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
|
|
results[key] = result.stdout.strip()
|
|
except Exception as e:
|
|
results[key] = str(e)
|
|
return {"system_info": results, "timestamp": datetime.utcnow().isoformat()}
|
|
|
|
|
|
def check_sudo_permissions():
|
|
"""Check sudo permissions for current user."""
|
|
try:
|
|
result = subprocess.run(["sudo", "-l"], capture_output=True, text=True, timeout=10)
|
|
output = result.stdout + result.stderr
|
|
escalation_vectors = []
|
|
dangerous_binaries = [
|
|
"vim", "vi", "nano", "less", "more", "man", "find", "nmap", "python",
|
|
"python3", "perl", "ruby", "lua", "awk", "bash", "sh", "env", "cp",
|
|
"mv", "tar", "zip", "wget", "curl", "ftp", "nc", "ncat", "docker",
|
|
"lxc", "mount", "strace", "ltrace", "gdb", "journalctl", "systemctl",
|
|
]
|
|
for binary in dangerous_binaries:
|
|
if binary in output and "NOPASSWD" in output:
|
|
escalation_vectors.append({"binary": binary, "type": "SUDO_NOPASSWD", "severity": "CRITICAL"})
|
|
elif binary in output:
|
|
escalation_vectors.append({"binary": binary, "type": "SUDO_WITH_PASSWORD", "severity": "HIGH"})
|
|
if "ALL) ALL" in output or "(ALL : ALL) ALL" in output:
|
|
escalation_vectors.append({"type": "FULL_SUDO", "severity": "CRITICAL"})
|
|
return {
|
|
"sudo_output": output[:1000],
|
|
"escalation_vectors": escalation_vectors,
|
|
"has_sudo": result.returncode == 0,
|
|
}
|
|
except Exception as e:
|
|
return {"error": str(e)}
|
|
|
|
|
|
def find_suid_binaries():
|
|
"""Find SUID/SGID binaries that may allow privilege escalation."""
|
|
cmd = ["find", "/", "-perm", "-4000", "-type", "f", "-exec", "ls", "-la", "{}", ";"]
|
|
try:
|
|
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
|
|
binaries = []
|
|
gtfobins_suid = [
|
|
"nmap", "vim", "find", "bash", "more", "less", "nano", "cp", "mv",
|
|
"python", "python3", "perl", "ruby", "awk", "env", "tar", "zip",
|
|
"docker", "strace", "ltrace", "gdb", "pkexec", "mount",
|
|
]
|
|
for line in result.stdout.strip().splitlines():
|
|
parts = line.split()
|
|
if len(parts) >= 9:
|
|
path = parts[-1]
|
|
name = Path(path).name
|
|
exploitable = name in gtfobins_suid
|
|
binaries.append({
|
|
"path": path, "permissions": parts[0],
|
|
"owner": parts[2], "group": parts[3],
|
|
"exploitable": exploitable,
|
|
})
|
|
exploitable = [b for b in binaries if b["exploitable"]]
|
|
return {
|
|
"total_suid": len(binaries),
|
|
"exploitable": len(exploitable),
|
|
"suid_binaries": binaries[:30],
|
|
"exploitable_binaries": exploitable,
|
|
}
|
|
except Exception as e:
|
|
return {"error": str(e)}
|
|
|
|
|
|
def check_writable_files():
|
|
"""Find world-writable files and directories of interest."""
|
|
interesting_paths = ["/etc/passwd", "/etc/shadow", "/etc/sudoers", "/etc/crontab",
|
|
"/etc/cron.d", "/etc/systemd/system", "/root"]
|
|
findings = []
|
|
for path in interesting_paths:
|
|
p = Path(path)
|
|
if p.exists():
|
|
writable = os.access(str(p), os.W_OK)
|
|
if writable:
|
|
findings.append({"path": path, "writable": True, "severity": "CRITICAL"})
|
|
cmd = ["find", "/", "-writable", "-type", "f", "-not", "-path", "'/proc/*'",
|
|
"-not", "-path", "'/sys/*'", "-not", "-path", "'/dev/*'"]
|
|
try:
|
|
result = subprocess.run(cmd, capture_output=True, text=True, timeout=15)
|
|
writable_files = result.stdout.strip().splitlines()[:50]
|
|
sensitive = [f for f in writable_files if any(p in f for p in ["/etc/", "/root/", "cron", ".service", "/bin/", "/sbin/"])]
|
|
findings.extend([{"path": f, "writable": True, "severity": "HIGH"} for f in sensitive[:10]])
|
|
except Exception:
|
|
pass
|
|
return {"writable_findings": findings, "total_findings": len(findings)}
|
|
|
|
|
|
def check_cron_jobs():
|
|
"""Enumerate cron jobs for privilege escalation opportunities."""
|
|
findings = []
|
|
cron_paths = ["/etc/crontab", "/etc/cron.d", "/var/spool/cron/crontabs"]
|
|
for path in cron_paths:
|
|
p = Path(path)
|
|
if p.is_file():
|
|
content = p.read_text(encoding="utf-8", errors="replace")
|
|
for line in content.splitlines():
|
|
if line.strip() and not line.startswith("#"):
|
|
writable_script = re.search(r"(/\S+\.sh|/\S+\.py|/\S+\.pl)", line)
|
|
if writable_script:
|
|
script = writable_script.group(1)
|
|
if Path(script).exists() and os.access(script, os.W_OK):
|
|
findings.append({"cron_file": str(p), "script": script, "writable": True, "severity": "CRITICAL"})
|
|
elif p.is_dir():
|
|
for f in p.iterdir():
|
|
if f.is_file():
|
|
findings.append({"cron_file": str(f), "type": "cron.d entry"})
|
|
try:
|
|
result = subprocess.run(["crontab", "-l"], capture_output=True, text=True, timeout=10)
|
|
if result.returncode == 0:
|
|
findings.append({"type": "user_crontab", "content": result.stdout[:500]})
|
|
except Exception:
|
|
pass
|
|
return {"cron_findings": findings}
|
|
|
|
|
|
def check_capabilities():
|
|
"""Find binaries with Linux capabilities set."""
|
|
try:
|
|
result = subprocess.run(["getcap", "-r", "/"], capture_output=True, text=True, timeout=15)
|
|
caps = []
|
|
for line in result.stdout.strip().splitlines():
|
|
parts = line.split(" = ")
|
|
if len(parts) == 2:
|
|
binary = parts[0].strip()
|
|
cap = parts[1].strip()
|
|
dangerous = any(c in cap for c in ["cap_setuid", "cap_setgid", "cap_sys_admin", "cap_dac_override", "cap_net_raw"])
|
|
caps.append({"binary": binary, "capabilities": cap, "dangerous": dangerous})
|
|
return {"capabilities": caps, "dangerous_caps": [c for c in caps if c["dangerous"]]}
|
|
except Exception as e:
|
|
return {"error": str(e)}
|
|
|
|
|
|
def full_enumeration():
|
|
"""Run complete privilege escalation enumeration."""
|
|
return {
|
|
"timestamp": datetime.utcnow().isoformat(),
|
|
"system": enumerate_system_info(),
|
|
"sudo": check_sudo_permissions(),
|
|
"suid": find_suid_binaries(),
|
|
"writable": check_writable_files(),
|
|
"cron": check_cron_jobs(),
|
|
"capabilities": check_capabilities(),
|
|
}
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Linux Privilege Escalation Enumeration Agent (Authorized Only)")
|
|
sub = parser.add_subparsers(dest="command")
|
|
sub.add_parser("sysinfo", help="System information")
|
|
sub.add_parser("sudo", help="Check sudo permissions")
|
|
sub.add_parser("suid", help="Find SUID/SGID binaries")
|
|
sub.add_parser("writable", help="Find writable sensitive files")
|
|
sub.add_parser("cron", help="Enumerate cron jobs")
|
|
sub.add_parser("caps", help="Check Linux capabilities")
|
|
sub.add_parser("full", help="Full enumeration")
|
|
args = parser.parse_args()
|
|
if args.command == "sysinfo":
|
|
result = enumerate_system_info()
|
|
elif args.command == "sudo":
|
|
result = check_sudo_permissions()
|
|
elif args.command == "suid":
|
|
result = find_suid_binaries()
|
|
elif args.command == "writable":
|
|
result = check_writable_files()
|
|
elif args.command == "cron":
|
|
result = check_cron_jobs()
|
|
elif args.command == "caps":
|
|
result = check_capabilities()
|
|
elif args.command == "full":
|
|
result = full_enumeration()
|
|
else:
|
|
parser.print_help()
|
|
return
|
|
print(json.dumps(result, indent=2, default=str))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|