mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-07-18 05:29:40 +03:00
c47eed6a64
- Fix 25 shell=True subprocess calls with list-based commands - Fix 49 verify=False in defensive skills (env-var override) - Add timeout to 231 HTTP/subprocess/socket calls - Fix 6 SQL injection patterns with whitelist validation - Replace 8 __import__() with standard imports - Remove 701 unused imports across 442 files - Add authorized-testing disclaimers to all offensive skills - Complete 11 incomplete skill directories - Expand 10 stub SKILL.md files with full content - Fix 2 YAML parse errors in frontmatter - Fix 5 pre-existing syntax errors - Convert 22 hardcoded paths/ports to environment variables - Back up 21 redundant skill pairs to .bak - Fix 2 global declaration errors - 724/724 skills with full folder anatomy (SKILL.md + agent.py + api-reference.md + LICENSE) - 0 compile errors across all 724 agent.py files
138 lines
5.3 KiB
Python
138 lines
5.3 KiB
Python
#!/usr/bin/env python3
|
|
# For authorized penetration testing and educational environments only.
|
|
# Usage against targets without prior mutual consent is illegal.
|
|
# It is the end user's responsibility to obey all applicable local, state and federal laws.
|
|
"""BloodHound CE reconnaissance agent using bloodhound Python ingestor and Neo4j."""
|
|
|
|
import json
|
|
import sys
|
|
import argparse
|
|
import subprocess
|
|
from datetime import datetime
|
|
|
|
try:
|
|
from neo4j import GraphDatabase
|
|
except ImportError:
|
|
print("Install: pip install neo4j")
|
|
sys.exit(1)
|
|
|
|
|
|
def collect_bloodhound_data(domain, username, password, dc_ip, method="all"):
|
|
"""Run BloodHound Python ingestor to collect AD data."""
|
|
cmd = [
|
|
"bloodhound-python", "-d", domain, "-u", username, "-p", password,
|
|
"-ns", dc_ip, "-c", method, "--zip",
|
|
]
|
|
try:
|
|
result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
|
|
return {"status": "completed", "output": result.stdout[:1000]}
|
|
except FileNotFoundError:
|
|
return {"status": "error", "message": "Install: pip install bloodhound"}
|
|
except subprocess.TimeoutExpired:
|
|
return {"status": "timeout"}
|
|
|
|
|
|
def query_shortest_path_to_da(driver):
|
|
"""Find shortest path to Domain Admins."""
|
|
with driver.session() as session:
|
|
result = session.run(
|
|
"MATCH p=shortestPath((u:User)-[*1..]->(g:Group {name: $group})) "
|
|
"WHERE u.enabled = true RETURN u.name AS user, length(p) AS hops "
|
|
"ORDER BY hops LIMIT 10",
|
|
group="DOMAIN ADMINS@DOMAIN.LOCAL",
|
|
)
|
|
return [{"user": r["user"], "hops": r["hops"]} for r in result]
|
|
|
|
|
|
def query_kerberoastable_users(driver):
|
|
"""Find kerberoastable user accounts."""
|
|
with driver.session() as session:
|
|
result = session.run(
|
|
"MATCH (u:User) WHERE u.hasspn = true AND u.enabled = true "
|
|
"RETURN u.name AS user, u.serviceprincipalnames AS spns, "
|
|
"u.admincount AS admin_count ORDER BY u.admincount DESC"
|
|
)
|
|
return [{"user": r["user"], "spns": r["spns"],
|
|
"admin": r["admin_count"]} for r in result]
|
|
|
|
|
|
def query_unconstrained_delegation(driver):
|
|
"""Find computers with unconstrained delegation."""
|
|
with driver.session() as session:
|
|
result = session.run(
|
|
"MATCH (c:Computer) WHERE c.unconstraineddelegation = true "
|
|
"RETURN c.name AS computer, c.operatingsystem AS os"
|
|
)
|
|
return [{"computer": r["computer"], "os": r["os"]} for r in result]
|
|
|
|
|
|
def query_as_rep_roastable(driver):
|
|
"""Find AS-REP roastable accounts (no pre-auth required)."""
|
|
with driver.session() as session:
|
|
result = session.run(
|
|
"MATCH (u:User) WHERE u.dontreqpreauth = true AND u.enabled = true "
|
|
"RETURN u.name AS user, u.admincount AS admin_count"
|
|
)
|
|
return [{"user": r["user"], "admin": r["admin_count"]} for r in result]
|
|
|
|
|
|
def run_recon(neo4j_uri, neo4j_user, neo4j_password):
|
|
"""Run BloodHound reconnaissance queries."""
|
|
driver = GraphDatabase.driver(neo4j_uri, auth=(neo4j_user, neo4j_password))
|
|
print(f"\n{'='*60}")
|
|
print(f" BLOODHOUND CE RECONNAISSANCE")
|
|
print(f" Generated: {datetime.utcnow().isoformat()} UTC")
|
|
print(f"{'='*60}\n")
|
|
|
|
paths = query_shortest_path_to_da(driver)
|
|
print(f"--- SHORTEST PATHS TO DOMAIN ADMIN ({len(paths)}) ---")
|
|
for p in paths:
|
|
print(f" {p['user']}: {p['hops']} hops")
|
|
|
|
kerb = query_kerberoastable_users(driver)
|
|
print(f"\n--- KERBEROASTABLE USERS ({len(kerb)}) ---")
|
|
for k in kerb[:10]:
|
|
print(f" {k['user']} (admin={k['admin']})")
|
|
|
|
deleg = query_unconstrained_delegation(driver)
|
|
print(f"\n--- UNCONSTRAINED DELEGATION ({len(deleg)}) ---")
|
|
for d in deleg:
|
|
print(f" {d['computer']}: {d['os']}")
|
|
|
|
asrep = query_as_rep_roastable(driver)
|
|
print(f"\n--- AS-REP ROASTABLE ({len(asrep)}) ---")
|
|
for a in asrep:
|
|
print(f" {a['user']} (admin={a['admin']})")
|
|
|
|
driver.close()
|
|
return {"paths_to_da": paths, "kerberoastable": kerb,
|
|
"unconstrained_delegation": deleg, "asrep_roastable": asrep}
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="BloodHound CE Recon Agent")
|
|
parser.add_argument("--neo4j-uri", default="bolt://localhost:7687", help="Neo4j URI")
|
|
parser.add_argument("--neo4j-user", default="neo4j", help="Neo4j username")
|
|
parser.add_argument("--neo4j-password", required=True, help="Neo4j password")
|
|
parser.add_argument("--collect", action="store_true", help="Run data collection first")
|
|
parser.add_argument("--domain", help="AD domain for collection")
|
|
parser.add_argument("--ad-user", help="AD username for collection")
|
|
parser.add_argument("--ad-pass", help="AD password for collection")
|
|
parser.add_argument("--dc-ip", help="Domain controller IP")
|
|
parser.add_argument("--output", help="Save report to JSON file")
|
|
args = parser.parse_args()
|
|
|
|
if args.collect and args.domain:
|
|
result = collect_bloodhound_data(args.domain, args.ad_user, args.ad_pass, args.dc_ip)
|
|
print(json.dumps(result, indent=2))
|
|
|
|
report = run_recon(args.neo4j_uri, args.neo4j_user, args.neo4j_password)
|
|
if args.output:
|
|
with open(args.output, "w") as f:
|
|
json.dump(report, f, indent=2, default=str)
|
|
print(f"\n[+] Report saved to {args.output}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|