mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-07-06 07:48:57 +03:00
174 lines
6.2 KiB
Python
174 lines
6.2 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Cartography Cloud Asset Inventory Security Analysis Script
|
|
|
|
Connects to Neo4j after Cartography sync and runs security-focused
|
|
queries to identify misconfigurations, attack paths, and overprivileged access.
|
|
"""
|
|
|
|
import json
|
|
import sys
|
|
from datetime import datetime
|
|
|
|
try:
|
|
from neo4j import GraphDatabase
|
|
except ImportError:
|
|
print("Install neo4j driver: pip install neo4j")
|
|
sys.exit(1)
|
|
|
|
|
|
SECURITY_QUERIES = {
|
|
"public_s3_buckets": {
|
|
"description": "S3 buckets with public access",
|
|
"severity": "HIGH",
|
|
"query": """
|
|
MATCH (b:S3Bucket)
|
|
WHERE b.anonymous_access = true
|
|
RETURN b.name AS bucket, b.region AS region, b.arn AS arn
|
|
ORDER BY b.name
|
|
"""
|
|
},
|
|
"admin_iam_users": {
|
|
"description": "IAM users with administrator access",
|
|
"severity": "HIGH",
|
|
"query": """
|
|
MATCH (user:AWSUser)-[:POLICY]->(policy:AWSPolicy)
|
|
WHERE policy.name = 'AdministratorAccess'
|
|
RETURN user.name AS username, user.arn AS arn, policy.name AS policy
|
|
"""
|
|
},
|
|
"public_ec2_ssh": {
|
|
"description": "EC2 instances with SSH exposed to internet",
|
|
"severity": "CRITICAL",
|
|
"query": """
|
|
MATCH (i:EC2Instance)-[:MEMBER_OF_EC2_SECURITY_GROUP]->(sg:EC2SecurityGroup)
|
|
-[:MEMBER_OF_EC2_SECURITY_GROUP_RULE]->(rule:IpRule)
|
|
WHERE rule.fromport <= 22 AND rule.toport >= 22
|
|
AND '0.0.0.0/0' IN rule.ipranges
|
|
RETURN i.instanceid AS instance, i.publicipaddress AS public_ip,
|
|
sg.name AS security_group
|
|
"""
|
|
},
|
|
"cross_account_trusts": {
|
|
"description": "Cross-account IAM trust relationships",
|
|
"severity": "MEDIUM",
|
|
"query": """
|
|
MATCH (role:AWSRole)-[:TRUSTS_AWS_PRINCIPAL]->(p:AWSPrincipal)
|
|
WHERE p.arn CONTAINS ':root'
|
|
AND NOT p.arn CONTAINS role.accountid
|
|
RETURN role.name AS role, role.arn AS role_arn,
|
|
p.arn AS trusted_principal
|
|
"""
|
|
},
|
|
"unencrypted_rds": {
|
|
"description": "RDS instances without encryption",
|
|
"severity": "HIGH",
|
|
"query": """
|
|
MATCH (rds:RDSInstance)
|
|
WHERE rds.storage_encrypted = false
|
|
RETURN rds.db_instance_identifier AS database,
|
|
rds.engine AS engine, rds.region AS region
|
|
"""
|
|
},
|
|
"unused_iam_roles": {
|
|
"description": "IAM roles unused for 90+ days",
|
|
"severity": "LOW",
|
|
"query": """
|
|
MATCH (role:AWSRole)
|
|
WHERE role.last_used IS NULL
|
|
RETURN role.name AS role, role.arn AS arn
|
|
LIMIT 50
|
|
"""
|
|
},
|
|
"lambda_admin_roles": {
|
|
"description": "Lambda functions with admin permissions",
|
|
"severity": "HIGH",
|
|
"query": """
|
|
MATCH (f:AWSLambda)-[:STS_ASSUME_ROLE_ALLOWS]->(r:AWSRole)-[:POLICY]->(p:AWSPolicy)
|
|
WHERE p.name = 'AdministratorAccess'
|
|
RETURN f.name AS function_name, r.name AS role, p.name AS policy
|
|
"""
|
|
}
|
|
}
|
|
|
|
|
|
def run_security_audit(uri, user, password):
|
|
"""Run all security queries against Neo4j."""
|
|
driver = GraphDatabase.driver(uri, auth=(user, password))
|
|
results = {}
|
|
|
|
with driver.session() as session:
|
|
for check_name, check_config in SECURITY_QUERIES.items():
|
|
print(f"\n[*] Running: {check_config['description']} [{check_config['severity']}]")
|
|
try:
|
|
result = session.run(check_config["query"])
|
|
records = [dict(record) for record in result]
|
|
results[check_name] = {
|
|
"description": check_config["description"],
|
|
"severity": check_config["severity"],
|
|
"findings": records,
|
|
"count": len(records)
|
|
}
|
|
if records:
|
|
print(f" [!] Found {len(records)} issues")
|
|
for r in records[:5]:
|
|
print(f" - {json.dumps(r, default=str)}")
|
|
if len(records) > 5:
|
|
print(f" ... and {len(records) - 5} more")
|
|
else:
|
|
print(f" [OK] No issues found")
|
|
except Exception as e:
|
|
print(f" [ERROR] {e}")
|
|
results[check_name] = {"error": str(e)}
|
|
|
|
driver.close()
|
|
return results
|
|
|
|
|
|
def generate_report(results, output_file=None):
|
|
"""Generate security audit report from query results."""
|
|
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
lines = [
|
|
"=" * 60,
|
|
"Cartography Cloud Asset Security Audit",
|
|
f"Generated: {timestamp}",
|
|
"=" * 60
|
|
]
|
|
|
|
total_findings = sum(r.get("count", 0) for r in results.values() if "error" not in r)
|
|
critical = sum(r.get("count", 0) for r in results.values() if r.get("severity") == "CRITICAL")
|
|
high = sum(r.get("count", 0) for r in results.values() if r.get("severity") == "HIGH")
|
|
|
|
lines.append(f"\nTotal Findings: {total_findings}")
|
|
lines.append(f"Critical: {critical} | High: {high}")
|
|
|
|
for name, data in results.items():
|
|
if "error" in data:
|
|
continue
|
|
lines.append(f"\n## {data['description']} [{data['severity']}]")
|
|
lines.append(f"Findings: {data['count']}")
|
|
for f in data.get("findings", [])[:10]:
|
|
lines.append(f" - {json.dumps(f, default=str)}")
|
|
|
|
report = "\n".join(lines)
|
|
if output_file:
|
|
with open(output_file, "w") as f:
|
|
f.write(report)
|
|
print(f"\n[+] Report saved to {output_file}")
|
|
else:
|
|
print(report)
|
|
return report
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import argparse
|
|
parser = argparse.ArgumentParser(description="Cartography Security Audit")
|
|
parser.add_argument("--neo4j-uri", default="bolt://localhost:7687")
|
|
parser.add_argument("--neo4j-user", default="neo4j")
|
|
parser.add_argument("--neo4j-password", required=True)
|
|
parser.add_argument("--output", type=str, help="Output report file")
|
|
args = parser.parse_args()
|
|
|
|
results = run_security_audit(args.neo4j_uri, args.neo4j_user, args.neo4j_password)
|
|
generate_report(results, args.output)
|