mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-11 13:44:56 +03:00
220 lines
7.6 KiB
Python
220 lines
7.6 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Agentless Vulnerability Scanning Orchestrator
|
|
|
|
Performs SSH-based agentless vulnerability scanning on Linux hosts
|
|
by enumerating packages and checking against known vulnerabilities.
|
|
|
|
Requirements:
|
|
pip install paramiko requests pandas
|
|
|
|
Usage:
|
|
python process.py scan --hosts hosts.txt --key /path/to/ssh_key
|
|
python process.py check --host 192.168.1.10 --user scanner --key /path/to/ssh_key
|
|
python process.py report --input scan_results.json --output report.csv
|
|
"""
|
|
|
|
import argparse
|
|
import json
|
|
import sys
|
|
from datetime import datetime
|
|
|
|
import pandas as pd
|
|
import paramiko
|
|
import requests
|
|
|
|
|
|
NVD_API = "https://services.nvd.nist.gov/rest/json/cves/2.0"
|
|
|
|
|
|
class AgentlessScanner:
|
|
"""SSH-based agentless vulnerability scanner."""
|
|
|
|
def __init__(self, key_path, username="scanner"):
|
|
self.key_path = key_path
|
|
self.username = username
|
|
|
|
def _connect(self, hostname, port=22):
|
|
"""Establish SSH connection."""
|
|
client = paramiko.SSHClient()
|
|
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
|
try:
|
|
key = paramiko.Ed25519Key.from_private_key_file(self.key_path)
|
|
except paramiko.ssh_exception.SSHException:
|
|
key = paramiko.RSAKey.from_private_key_file(self.key_path)
|
|
client.connect(hostname, port=port, username=self.username,
|
|
pkey=key, timeout=30)
|
|
return client
|
|
|
|
def _exec(self, client, command, timeout=30):
|
|
"""Execute command and return output."""
|
|
_, stdout, stderr = client.exec_command(command, timeout=timeout)
|
|
return stdout.read().decode().strip(), stderr.read().decode().strip()
|
|
|
|
def get_os_info(self, client):
|
|
"""Detect OS distribution and version."""
|
|
out, _ = self._exec(client, "cat /etc/os-release")
|
|
info = {}
|
|
for line in out.split("\n"):
|
|
if "=" in line:
|
|
k, v = line.split("=", 1)
|
|
info[k] = v.strip('"')
|
|
return info
|
|
|
|
def get_packages_dpkg(self, client):
|
|
"""Get packages via dpkg (Debian/Ubuntu)."""
|
|
out, _ = self._exec(
|
|
client,
|
|
"dpkg-query -W -f='${Package}|${Version}|${Architecture}\\n'"
|
|
)
|
|
packages = []
|
|
for line in out.split("\n"):
|
|
parts = line.split("|")
|
|
if len(parts) >= 2:
|
|
packages.append({
|
|
"name": parts[0],
|
|
"version": parts[1],
|
|
"arch": parts[2] if len(parts) > 2 else "",
|
|
})
|
|
return packages
|
|
|
|
def get_packages_rpm(self, client):
|
|
"""Get packages via rpm (RHEL/CentOS/Fedora)."""
|
|
out, _ = self._exec(
|
|
client,
|
|
"rpm -qa --queryformat '%{NAME}|%{VERSION}-%{RELEASE}|%{ARCH}\\n'"
|
|
)
|
|
packages = []
|
|
for line in out.split("\n"):
|
|
parts = line.split("|")
|
|
if len(parts) >= 2:
|
|
packages.append({
|
|
"name": parts[0],
|
|
"version": parts[1],
|
|
"arch": parts[2] if len(parts) > 2 else "",
|
|
})
|
|
return packages
|
|
|
|
def get_kernel(self, client):
|
|
"""Get running kernel version."""
|
|
out, _ = self._exec(client, "uname -r")
|
|
return out
|
|
|
|
def get_listening_services(self, client):
|
|
"""Get listening network services."""
|
|
out, _ = self._exec(client, "ss -tlnp 2>/dev/null || netstat -tlnp 2>/dev/null")
|
|
return out
|
|
|
|
def scan_host(self, hostname, port=22):
|
|
"""Perform full agentless scan."""
|
|
result = {
|
|
"hostname": hostname,
|
|
"scan_time": datetime.utcnow().isoformat(),
|
|
"status": "success",
|
|
"os_info": {},
|
|
"kernel": "",
|
|
"packages": [],
|
|
"listening_services": "",
|
|
}
|
|
|
|
try:
|
|
client = self._connect(hostname, port)
|
|
result["os_info"] = self.get_os_info(client)
|
|
result["kernel"] = self.get_kernel(client)
|
|
|
|
os_id = result["os_info"].get("ID", "").lower()
|
|
if os_id in ("ubuntu", "debian", "kali"):
|
|
result["packages"] = self.get_packages_dpkg(client)
|
|
else:
|
|
result["packages"] = self.get_packages_rpm(client)
|
|
|
|
result["listening_services"] = self.get_listening_services(client)
|
|
client.close()
|
|
|
|
print(f" [+] {hostname}: {result['os_info'].get('PRETTY_NAME', 'Unknown')} | "
|
|
f"{len(result['packages'])} packages | kernel {result['kernel']}")
|
|
|
|
except Exception as e:
|
|
result["status"] = "error"
|
|
result["error"] = str(e)
|
|
print(f" [!] {hostname}: {e}")
|
|
|
|
return result
|
|
|
|
def scan_hosts(self, hosts, port=22):
|
|
"""Scan multiple hosts."""
|
|
results = []
|
|
for host in hosts:
|
|
host = host.strip()
|
|
if not host or host.startswith("#"):
|
|
continue
|
|
print(f"[*] Scanning {host}...")
|
|
results.append(self.scan_host(host, port))
|
|
return results
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(
|
|
description="Agentless Vulnerability Scanning Orchestrator"
|
|
)
|
|
subparsers = parser.add_subparsers(dest="command")
|
|
|
|
scan_p = subparsers.add_parser("scan", help="Scan hosts from file")
|
|
scan_p.add_argument("--hosts", required=True, help="File with hostnames/IPs")
|
|
scan_p.add_argument("--key", required=True, help="SSH private key path")
|
|
scan_p.add_argument("--user", default="scanner", help="SSH username")
|
|
scan_p.add_argument("--port", type=int, default=22, help="SSH port")
|
|
scan_p.add_argument("--output", default="scan_results.json")
|
|
|
|
check_p = subparsers.add_parser("check", help="Scan a single host")
|
|
check_p.add_argument("--host", required=True)
|
|
check_p.add_argument("--key", required=True)
|
|
check_p.add_argument("--user", default="scanner")
|
|
check_p.add_argument("--port", type=int, default=22)
|
|
|
|
report_p = subparsers.add_parser("report", help="Generate CSV report")
|
|
report_p.add_argument("--input", required=True, help="Scan results JSON")
|
|
report_p.add_argument("--output", default="scan_report.csv")
|
|
|
|
args = parser.parse_args()
|
|
|
|
if args.command == "scan":
|
|
scanner = AgentlessScanner(args.key, args.user)
|
|
with open(args.hosts) as f:
|
|
hosts = f.readlines()
|
|
results = scanner.scan_hosts(hosts, args.port)
|
|
with open(args.output, "w") as f:
|
|
json.dump(results, f, indent=2)
|
|
print(f"\n[+] Results saved to {args.output}")
|
|
successful = sum(1 for r in results if r["status"] == "success")
|
|
print(f" Scanned: {len(results)} | Success: {successful}")
|
|
|
|
elif args.command == "check":
|
|
scanner = AgentlessScanner(args.key, args.user)
|
|
result = scanner.scan_host(args.host, args.port)
|
|
print(json.dumps(result, indent=2, default=str))
|
|
|
|
elif args.command == "report":
|
|
with open(args.input) as f:
|
|
results = json.load(f)
|
|
rows = []
|
|
for r in results:
|
|
for pkg in r.get("packages", []):
|
|
rows.append({
|
|
"hostname": r["hostname"],
|
|
"os": r.get("os_info", {}).get("PRETTY_NAME", ""),
|
|
"kernel": r.get("kernel", ""),
|
|
"package": pkg["name"],
|
|
"version": pkg["version"],
|
|
})
|
|
df = pd.DataFrame(rows)
|
|
df.to_csv(args.output, index=False)
|
|
print(f"[+] Report with {len(rows)} package entries saved to {args.output}")
|
|
|
|
else:
|
|
parser.print_help()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|