mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-12 14:14:56 +03:00
325 lines
12 KiB
Python
325 lines
12 KiB
Python
#!/usr/bin/env python3
|
|
"""PCI DSS compliance control audit agent.
|
|
|
|
Audits systems and configurations against PCI DSS v4.0 requirements
|
|
including network segmentation, encryption, access controls, logging,
|
|
vulnerability management, and secure configuration checks.
|
|
"""
|
|
import argparse
|
|
import json
|
|
import os
|
|
import re
|
|
import socket
|
|
import ssl
|
|
import subprocess
|
|
import sys
|
|
from datetime import datetime, timezone
|
|
|
|
|
|
def check_tls_configuration(host, port=443):
|
|
"""PCI DSS Req 4.2.1 - Strong cryptography for transmission."""
|
|
findings = []
|
|
print(f"[*] Req 4.2.1: Checking TLS on {host}:{port}")
|
|
try:
|
|
context = ssl.create_default_context()
|
|
with socket.create_connection((host, port), timeout=10) as sock:
|
|
with context.wrap_socket(sock, server_hostname=host) as ssock:
|
|
protocol = ssock.version()
|
|
cipher = ssock.cipher()
|
|
if protocol in ("TLSv1.0", "TLSv1.1", "SSLv3", "SSLv2"):
|
|
findings.append({
|
|
"requirement": "4.2.1", "check": "TLS Protocol Version",
|
|
"status": "FAIL", "severity": "CRITICAL",
|
|
"detail": f"Deprecated protocol: {protocol}",
|
|
})
|
|
else:
|
|
findings.append({
|
|
"requirement": "4.2.1", "check": "TLS Protocol Version",
|
|
"status": "PASS", "severity": "INFO",
|
|
"detail": f"Protocol: {protocol}",
|
|
})
|
|
if cipher:
|
|
weak_ciphers = ["RC4", "DES", "3DES", "NULL", "EXPORT", "MD5"]
|
|
if any(w in cipher[0] for w in weak_ciphers):
|
|
findings.append({
|
|
"requirement": "4.2.1", "check": "Cipher Strength",
|
|
"status": "FAIL", "severity": "HIGH",
|
|
"detail": f"Weak cipher: {cipher[0]}",
|
|
})
|
|
else:
|
|
findings.append({
|
|
"requirement": "4.2.1", "check": "Cipher Strength",
|
|
"status": "PASS", "severity": "INFO",
|
|
"detail": f"Cipher: {cipher[0]} ({cipher[2]} bits)",
|
|
})
|
|
except Exception as e:
|
|
findings.append({
|
|
"requirement": "4.2.1", "check": "TLS Connection",
|
|
"status": "ERROR", "severity": "HIGH", "detail": str(e)[:100],
|
|
})
|
|
return findings
|
|
|
|
|
|
def check_password_policy():
|
|
"""PCI DSS Req 8.3.6 - Password complexity requirements."""
|
|
findings = []
|
|
print("[*] Req 8.3.6: Checking password policy")
|
|
|
|
if sys.platform != "win32":
|
|
# Check PAM password quality
|
|
pam_files = ["/etc/pam.d/common-password", "/etc/pam.d/system-auth",
|
|
"/etc/security/pwquality.conf"]
|
|
for pam_file in pam_files:
|
|
if os.path.isfile(pam_file):
|
|
with open(pam_file, "r") as f:
|
|
content = f.read()
|
|
if "minlen" in content:
|
|
match = re.search(r'minlen\s*=\s*(\d+)', content)
|
|
if match and int(match.group(1)) >= 12:
|
|
findings.append({
|
|
"requirement": "8.3.6", "check": "Min password length",
|
|
"status": "PASS", "severity": "INFO",
|
|
"detail": f"minlen={match.group(1)} in {pam_file}",
|
|
})
|
|
else:
|
|
findings.append({
|
|
"requirement": "8.3.6", "check": "Min password length",
|
|
"status": "FAIL", "severity": "HIGH",
|
|
"detail": f"Password minlen < 12 in {pam_file}",
|
|
})
|
|
break
|
|
else:
|
|
findings.append({
|
|
"requirement": "8.3.6", "check": "Password policy config",
|
|
"status": "WARN", "severity": "MEDIUM",
|
|
"detail": "Could not find PAM password config",
|
|
})
|
|
return findings
|
|
|
|
|
|
def check_audit_logging():
|
|
"""PCI DSS Req 10.2 - Audit logging configuration."""
|
|
findings = []
|
|
print("[*] Req 10.2: Checking audit logging")
|
|
|
|
if sys.platform != "win32":
|
|
# Check auditd
|
|
result = subprocess.run(
|
|
["systemctl", "is-active", "auditd"],
|
|
capture_output=True, text=True, timeout=10,
|
|
)
|
|
if result.stdout.strip() == "active":
|
|
findings.append({
|
|
"requirement": "10.2", "check": "Audit daemon running",
|
|
"status": "PASS", "severity": "INFO",
|
|
})
|
|
else:
|
|
findings.append({
|
|
"requirement": "10.2", "check": "Audit daemon running",
|
|
"status": "FAIL", "severity": "CRITICAL",
|
|
"detail": "auditd is not running",
|
|
})
|
|
|
|
# Check syslog
|
|
for syslog in ["rsyslog", "syslog-ng"]:
|
|
result = subprocess.run(
|
|
["systemctl", "is-active", syslog],
|
|
capture_output=True, text=True, timeout=10,
|
|
)
|
|
if result.stdout.strip() == "active":
|
|
findings.append({
|
|
"requirement": "10.2", "check": f"{syslog} running",
|
|
"status": "PASS", "severity": "INFO",
|
|
})
|
|
break
|
|
return findings
|
|
|
|
|
|
def check_file_integrity():
|
|
"""PCI DSS Req 11.5.2 - File integrity monitoring."""
|
|
findings = []
|
|
print("[*] Req 11.5.2: Checking file integrity monitoring")
|
|
|
|
fim_tools = {
|
|
"aide": ["/usr/bin/aide", "/usr/sbin/aide"],
|
|
"ossec": ["/var/ossec/bin/ossec-syscheckd"],
|
|
"tripwire": ["/usr/sbin/tripwire"],
|
|
"samhain": ["/usr/local/sbin/samhain"],
|
|
}
|
|
|
|
found_fim = False
|
|
for tool_name, paths in fim_tools.items():
|
|
for path in paths:
|
|
if os.path.isfile(path):
|
|
findings.append({
|
|
"requirement": "11.5.2", "check": f"FIM tool: {tool_name}",
|
|
"status": "PASS", "severity": "INFO",
|
|
"detail": f"Found at {path}",
|
|
})
|
|
found_fim = True
|
|
break
|
|
|
|
if not found_fim:
|
|
findings.append({
|
|
"requirement": "11.5.2", "check": "File integrity monitoring",
|
|
"status": "FAIL", "severity": "HIGH",
|
|
"detail": "No FIM tool detected (AIDE, OSSEC, Tripwire, Samhain)",
|
|
})
|
|
|
|
return findings
|
|
|
|
|
|
def check_default_credentials():
|
|
"""PCI DSS Req 2.2.2 - Change vendor defaults."""
|
|
findings = []
|
|
print("[*] Req 2.2.2: Checking for default credentials")
|
|
|
|
# Check for common default accounts
|
|
if os.path.isfile("/etc/passwd"):
|
|
with open("/etc/passwd", "r") as f:
|
|
for line in f:
|
|
parts = line.strip().split(":")
|
|
if len(parts) >= 7:
|
|
username = parts[0]
|
|
shell = parts[6]
|
|
if username in ("guest", "test", "demo", "admin") and shell not in ("/usr/sbin/nologin", "/bin/false"):
|
|
findings.append({
|
|
"requirement": "2.2.2", "check": f"Default account: {username}",
|
|
"status": "FAIL", "severity": "HIGH",
|
|
"detail": f"Account '{username}' has login shell: {shell}",
|
|
})
|
|
|
|
return findings
|
|
|
|
|
|
def check_network_segmentation(target_ip, ports=None):
|
|
"""PCI DSS Req 1.3 - Network segmentation check."""
|
|
findings = []
|
|
if not ports:
|
|
ports = [22, 80, 443, 3306, 5432, 1433, 6379, 9200, 27017]
|
|
print(f"[*] Req 1.3: Checking network segmentation to {target_ip}")
|
|
|
|
for port in ports:
|
|
try:
|
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
sock.settimeout(3)
|
|
result = sock.connect_ex((target_ip, port))
|
|
sock.close()
|
|
if result == 0:
|
|
findings.append({
|
|
"requirement": "1.3", "check": f"Port {port} reachable",
|
|
"status": "WARN", "severity": "MEDIUM",
|
|
"detail": f"{target_ip}:{port} is open from this network segment",
|
|
})
|
|
except Exception:
|
|
pass
|
|
|
|
if not findings:
|
|
findings.append({
|
|
"requirement": "1.3", "check": "Network segmentation",
|
|
"status": "PASS", "severity": "INFO",
|
|
"detail": f"No tested ports reachable on {target_ip}",
|
|
})
|
|
|
|
return findings
|
|
|
|
|
|
def format_summary(all_findings):
|
|
"""Print PCI DSS audit summary."""
|
|
print(f"\n{'='*60}")
|
|
print(f" PCI DSS v4.0 Compliance Audit Report")
|
|
print(f"{'='*60}")
|
|
|
|
pass_count = sum(1 for f in all_findings if f["status"] == "PASS")
|
|
fail_count = sum(1 for f in all_findings if f["status"] == "FAIL")
|
|
warn_count = sum(1 for f in all_findings if f["status"] == "WARN")
|
|
|
|
print(f" Total Checks : {len(all_findings)}")
|
|
print(f" Passed : {pass_count}")
|
|
print(f" Failed : {fail_count}")
|
|
print(f" Warnings : {warn_count}")
|
|
|
|
by_req = {}
|
|
for f in all_findings:
|
|
req = f.get("requirement", "unknown")
|
|
by_req.setdefault(req, []).append(f)
|
|
|
|
print(f"\n Results by Requirement:")
|
|
for req in sorted(by_req.keys()):
|
|
items = by_req[req]
|
|
failed = sum(1 for i in items if i["status"] == "FAIL")
|
|
passed = sum(1 for i in items if i["status"] == "PASS")
|
|
status = "FAIL" if failed > 0 else "PASS"
|
|
print(f" Req {req:8s}: [{status:4s}] {passed} passed, {failed} failed")
|
|
|
|
if fail_count > 0:
|
|
print(f"\n Failed Checks:")
|
|
for f in all_findings:
|
|
if f["status"] == "FAIL":
|
|
print(f" [{f['severity']:8s}] Req {f['requirement']}: {f['check']} - {f.get('detail', '')}")
|
|
|
|
severity_counts = {}
|
|
for f in all_findings:
|
|
if f["status"] == "FAIL":
|
|
sev = f.get("severity", "MEDIUM")
|
|
severity_counts[sev] = severity_counts.get(sev, 0) + 1
|
|
return severity_counts
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="PCI DSS compliance control audit agent")
|
|
parser.add_argument("--tls-host", help="Host to check TLS configuration")
|
|
parser.add_argument("--tls-port", type=int, default=443)
|
|
parser.add_argument("--segment-target", help="IP to check network segmentation")
|
|
parser.add_argument("--skip-password", action="store_true")
|
|
parser.add_argument("--skip-logging", action="store_true")
|
|
parser.add_argument("--skip-fim", action="store_true")
|
|
parser.add_argument("--output", "-o", help="Output JSON report")
|
|
parser.add_argument("--verbose", "-v", action="store_true")
|
|
args = parser.parse_args()
|
|
|
|
all_findings = []
|
|
|
|
if args.tls_host:
|
|
all_findings.extend(check_tls_configuration(args.tls_host, args.tls_port))
|
|
if not args.skip_password:
|
|
all_findings.extend(check_password_policy())
|
|
if not args.skip_logging:
|
|
all_findings.extend(check_audit_logging())
|
|
if not args.skip_fim:
|
|
all_findings.extend(check_file_integrity())
|
|
all_findings.extend(check_default_credentials())
|
|
if args.segment_target:
|
|
all_findings.extend(check_network_segmentation(args.segment_target))
|
|
|
|
if not all_findings:
|
|
print("[!] No checks performed. Use --tls-host or other options.", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
severity_counts = format_summary(all_findings)
|
|
|
|
report = {
|
|
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
"tool": "PCI DSS Audit",
|
|
"standard": "PCI DSS v4.0",
|
|
"findings": all_findings,
|
|
"severity_counts": severity_counts,
|
|
"risk_level": (
|
|
"CRITICAL" if severity_counts.get("CRITICAL", 0) > 0
|
|
else "HIGH" if severity_counts.get("HIGH", 0) > 0
|
|
else "MEDIUM" if severity_counts.get("MEDIUM", 0) > 0
|
|
else "LOW"
|
|
),
|
|
}
|
|
|
|
if args.output:
|
|
with open(args.output, "w") as f:
|
|
json.dump(report, f, indent=2)
|
|
print(f"\n[+] Report saved to {args.output}")
|
|
elif args.verbose:
|
|
print(json.dumps(report, indent=2))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|