Files

155 lines
5.9 KiB
Python

#!/usr/bin/env python3
"""Greenbone/OpenVAS Vulnerability Management agent - creates scan targets, executes scans, and parses reports via python-gvm GMP protocol"""
import argparse
import json
import sys
from collections import Counter, defaultdict
from datetime import datetime
from pathlib import Path
try:
from gvm.connections import UnixSocketConnection, TLSConnection
from gvm.protocols.gmp import Gmp
from gvm.transforms import EtreeTransform
from lxml import etree
HAS_GVM = True
except ImportError:
HAS_GVM = False
def load_data(path):
return json.loads(Path(path).read_text(encoding="utf-8"))
def connect_gvm(host, port=9390, socket_path="/run/gvmd/gvmd.sock", use_tls=False, username="admin", password="admin"):
"""Connect to GVM daemon and authenticate via GMP."""
if not HAS_GVM:
return None, "python-gvm not installed"
transform = EtreeTransform()
if use_tls:
conn = TLSConnection(hostname=host, port=port)
else:
conn = UnixSocketConnection(path=socket_path)
gmp = Gmp(connection=conn, transform=transform)
gmp.authenticate(username, password)
version_resp = gmp.get_version()
version = version_resp.xpath("version/text()")[0] if version_resp.xpath("version/text()") else "unknown"
return gmp, version
def create_target(gmp, name, hosts, port_list_id="33d0cd82-57c6-11e1-8ed1-406186ea4fc5"):
"""Create a scan target. Default port list is 'All IANA assigned TCP'."""
resp = gmp.create_target(name=name, hosts=hosts, port_list_id=port_list_id)
target_id = resp.get("id", "")
return target_id
def create_and_start_task(gmp, task_name, target_id, config_id="daba56c8-73ec-11df-a475-002264764cea", scanner_id="08b69003-5fc2-4037-a479-93b440211c73"):
"""Create scan task and start it. Default config is 'Full and fast', default scanner is 'OpenVAS Default'."""
resp = gmp.create_task(name=task_name, config_id=config_id, target_id=target_id, scanner_id=scanner_id)
task_id = resp.get("id", "")
gmp.start_task(task_id)
return task_id
def parse_report_xml(report_xml):
"""Parse GMP report XML into structured findings."""
findings = []
results = report_xml.findall(".//result") if report_xml is not None else []
for result in results:
host_el = result.find("host")
host = host_el.text if host_el is not None else ""
name_el = result.find("name")
name = name_el.text if name_el is not None else ""
threat_el = result.find("threat")
threat = threat_el.text if threat_el is not None else "Log"
severity_el = result.find("severity")
cvss = float(severity_el.text) if severity_el is not None and severity_el.text else 0.0
nvt = result.find("nvt")
oid = nvt.get("oid", "") if nvt is not None else ""
cve_el = nvt.find("cve") if nvt is not None else None
cve = cve_el.text if cve_el is not None and cve_el.text != "NOCVE" else ""
desc_el = result.find("description")
desc = (desc_el.text or "")[:200] if desc_el is not None else ""
sev_label = "critical" if cvss >= 9.0 else "high" if cvss >= 7.0 else "medium" if cvss >= 4.0 else "low"
findings.append({
"host": host,
"vulnerability": name,
"severity": sev_label,
"cvss": cvss,
"cve": cve,
"nvt_oid": oid,
"description": desc,
})
return findings
def analyze_offline_report(data):
"""Analyze pre-exported GVM report data (JSON format)."""
findings = []
results = data.get("results", data.get("vulnerabilities", []))
if isinstance(data, list):
results = data
for r in results:
cvss = r.get("cvss", r.get("severity_score", 0.0))
if isinstance(cvss, str):
try:
cvss = float(cvss)
except ValueError:
cvss = 0.0
sev_label = "critical" if cvss >= 9.0 else "high" if cvss >= 7.0 else "medium" if cvss >= 4.0 else "low"
findings.append({
"host": r.get("host", r.get("ip", "")),
"vulnerability": r.get("name", r.get("vulnerability", "")),
"severity": sev_label,
"cvss": cvss,
"cve": r.get("cve", r.get("cves", "")),
"nvt_oid": r.get("nvt_oid", r.get("oid", "")),
"description": (r.get("description", r.get("summary", "")) or "")[:200],
})
return findings
def generate_report(input_path):
data = load_data(input_path)
findings = analyze_offline_report(data)
sev = Counter(f["severity"] for f in findings)
host_vulns = defaultdict(int)
for f in findings:
host_vulns[f["host"]] += 1
cve_list = [f["cve"] for f in findings if f["cve"]]
findings.sort(key=lambda x: x["cvss"], reverse=True)
return {
"report": "greenbone_vulnerability_management",
"generated_at": datetime.utcnow().isoformat() + "Z",
"total_vulnerabilities": len(findings),
"severity_summary": dict(sev),
"hosts_scanned": len(host_vulns),
"host_vulnerability_counts": dict(host_vulns),
"unique_cves": len(set(cve_list)),
"top_10_findings": findings[:10],
"findings": findings,
}
def main():
ap = argparse.ArgumentParser(description="Greenbone Vulnerability Management Agent")
ap.add_argument("--input", required=True, help="Input JSON with GVM scan results")
ap.add_argument("--output", help="Output JSON report path")
ap.add_argument("--host", help="GVM host for live scan (requires python-gvm)")
ap.add_argument("--username", default="admin", help="GMP username")
ap.add_argument("--password", default="admin", help="GMP password")
args = ap.parse_args()
report = generate_report(args.input)
out = json.dumps(report, indent=2)
if args.output:
Path(args.output).write_text(out, encoding="utf-8")
print(f"Report written to {args.output}")
else:
print(out)
if __name__ == "__main__":
main()