mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-12 22:24:56 +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
158 lines
6.2 KiB
Python
158 lines
6.2 KiB
Python
#!/usr/bin/env python3
|
|
"""Agent for auditing Linux endpoints against CIS Benchmark hardening controls."""
|
|
|
|
import argparse
|
|
import json
|
|
import os
|
|
import re
|
|
import shlex
|
|
import subprocess
|
|
from datetime import datetime, timezone
|
|
|
|
|
|
def run_cmd(cmd, timeout=10):
|
|
"""Run a command and return stdout."""
|
|
try:
|
|
# Strip shell stderr redirects since we use subprocess.DEVNULL
|
|
clean_cmd = cmd.replace("2>/dev/null", "").strip()
|
|
# Use shell only for commands with shell operators
|
|
needs_shell = any(op in clean_cmd for op in ("|", ";", "&&", "||"))
|
|
return subprocess.check_output(
|
|
clean_cmd if needs_shell else shlex.split(clean_cmd),
|
|
shell=needs_shell, text=True, errors="replace",
|
|
timeout=timeout, stderr=subprocess.DEVNULL
|
|
).strip()
|
|
except subprocess.SubprocessError:
|
|
return ""
|
|
|
|
|
|
def check_filesystem_config():
|
|
"""CIS Section 1 — Filesystem Configuration."""
|
|
findings = []
|
|
cramfs = run_cmd("modprobe -n -v cramfs 2>/dev/null")
|
|
if "install /bin/true" not in cramfs and "install /bin/false" not in cramfs:
|
|
findings.append({"check": "1.1.1.1", "issue": "cramfs module not disabled", "severity": "LOW"})
|
|
tmp_mount = run_cmd("findmnt /tmp")
|
|
if not tmp_mount:
|
|
findings.append({"check": "1.1.2", "issue": "/tmp not a separate partition", "severity": "MEDIUM"})
|
|
elif "nodev" not in tmp_mount or "nosuid" not in tmp_mount:
|
|
findings.append({"check": "1.1.3", "issue": "/tmp missing nodev/nosuid options", "severity": "MEDIUM"})
|
|
return findings
|
|
|
|
|
|
def check_services():
|
|
"""CIS Section 2 — Services."""
|
|
findings = []
|
|
unnecessary = ["avahi-daemon", "cups", "dhcpd", "slapd", "nfs-server",
|
|
"rpcbind", "named", "vsftpd", "httpd", "dovecot", "smb", "squid"]
|
|
for svc in unnecessary:
|
|
status = run_cmd(f"systemctl is-enabled {svc} 2>/dev/null")
|
|
if status == "enabled":
|
|
findings.append({"check": "2.x", "issue": f"Unnecessary service enabled: {svc}", "severity": "MEDIUM"})
|
|
return findings
|
|
|
|
|
|
def check_network_parameters():
|
|
"""CIS Section 3 — Network Parameters."""
|
|
findings = []
|
|
params = {
|
|
"net.ipv4.ip_forward": ("0", "3.1.1", "IP forwarding enabled"),
|
|
"net.ipv4.conf.all.send_redirects": ("0", "3.1.2", "ICMP redirects enabled"),
|
|
"net.ipv4.conf.all.accept_source_route": ("0", "3.2.1", "Source routing accepted"),
|
|
"net.ipv4.conf.all.accept_redirects": ("0", "3.2.2", "ICMP redirects accepted"),
|
|
"net.ipv4.conf.all.log_martians": ("1", "3.2.4", "Martian logging disabled"),
|
|
"net.ipv4.tcp_syncookies": ("1", "3.2.8", "SYN cookies disabled"),
|
|
}
|
|
for param, (expected, cis_id, desc) in params.items():
|
|
value = run_cmd(f"sysctl -n {param} 2>/dev/null")
|
|
if value != expected:
|
|
findings.append({"check": cis_id, "issue": desc, "current": value, "expected": expected, "severity": "MEDIUM"})
|
|
return findings
|
|
|
|
|
|
def check_access_auth():
|
|
"""CIS Section 5 — Access, Authentication, Authorization."""
|
|
findings = []
|
|
sshd_config = run_cmd("cat /etc/ssh/sshd_config 2>/dev/null")
|
|
if "PermitRootLogin yes" in sshd_config:
|
|
findings.append({"check": "5.2.10", "issue": "SSH root login permitted", "severity": "HIGH"})
|
|
if "PasswordAuthentication yes" in sshd_config:
|
|
findings.append({"check": "5.2.12", "issue": "SSH password authentication enabled", "severity": "MEDIUM"})
|
|
if "Protocol 1" in sshd_config:
|
|
findings.append({"check": "5.2.4", "issue": "SSH Protocol 1 enabled", "severity": "HIGH"})
|
|
|
|
passwd_maxdays = run_cmd("grep PASS_MAX_DAYS /etc/login.defs 2>/dev/null")
|
|
if passwd_maxdays:
|
|
match = re.search(r'PASS_MAX_DAYS\s+(\d+)', passwd_maxdays)
|
|
if match and int(match.group(1)) > 365:
|
|
findings.append({"check": "5.4.1.1", "issue": f"Password max age: {match.group(1)} days", "severity": "MEDIUM"})
|
|
return findings
|
|
|
|
|
|
def check_audit_logging():
|
|
"""CIS Section 4 — Logging and Auditing."""
|
|
findings = []
|
|
auditd = run_cmd("systemctl is-active auditd 2>/dev/null")
|
|
if auditd != "active":
|
|
findings.append({"check": "4.1.1", "issue": "auditd not active", "severity": "HIGH"})
|
|
rsyslog = run_cmd("systemctl is-active rsyslog 2>/dev/null")
|
|
if rsyslog != "active":
|
|
findings.append({"check": "4.2.1", "issue": "rsyslog not active", "severity": "MEDIUM"})
|
|
return findings
|
|
|
|
|
|
def check_file_permissions():
|
|
"""CIS Section 6 — System File Permissions."""
|
|
findings = []
|
|
critical_files = {
|
|
"/etc/passwd": "644",
|
|
"/etc/shadow": "000",
|
|
"/etc/group": "644",
|
|
"/etc/gshadow": "000",
|
|
}
|
|
for fpath, expected in critical_files.items():
|
|
if os.path.isfile(fpath):
|
|
mode = oct(os.stat(fpath).st_mode)[-3:]
|
|
if mode > expected:
|
|
findings.append({"check": "6.1.x", "issue": f"{fpath}: mode {mode} > {expected}", "severity": "MEDIUM"})
|
|
return findings
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(
|
|
description="Audit Linux endpoint against CIS Benchmark"
|
|
)
|
|
parser.add_argument("--output", "-o", help="Output JSON report")
|
|
parser.add_argument("--verbose", "-v", action="store_true")
|
|
args = parser.parse_args()
|
|
|
|
print("[*] Linux CIS Benchmark Hardening Audit Agent")
|
|
all_findings = []
|
|
all_findings.extend(check_filesystem_config())
|
|
all_findings.extend(check_services())
|
|
all_findings.extend(check_network_parameters())
|
|
all_findings.extend(check_access_auth())
|
|
all_findings.extend(check_audit_logging())
|
|
all_findings.extend(check_file_permissions())
|
|
|
|
high = sum(1 for f in all_findings if f["severity"] == "HIGH")
|
|
report = {
|
|
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
"findings": all_findings,
|
|
"total": len(all_findings),
|
|
"high_severity": high,
|
|
"compliance_score": max(0, 100 - len(all_findings) * 5),
|
|
}
|
|
print(f"[*] Findings: {len(all_findings)} (HIGH: {high})")
|
|
|
|
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()
|