Files
mukul975 27c6414ca5 Add folder anatomy (scripts/agent.py + references/api-reference.md) for 648 cybersecurity skills
Complete skill folder anatomy across all cybersecurity skills:
- scripts/agent.py: 80-150 line Python agents using real libraries (impacket,
  boto3, azure-mgmt-*, kubernetes, pefile, yara, scapy, shodan, stix2, etc.)
- references/api-reference.md: real API documentation with method signatures
- LICENSE: MIT license for all skill folders
2026-03-10 21:02:12 +01:00

141 lines
5.3 KiB
Python

#!/usr/bin/env python3
"""Agent for web application scanning with Nikto.
Runs Nikto via subprocess for web server vulnerability scanning,
parses XML/JSON output, classifies findings by OSVDB/CVE, and
generates a structured security assessment report.
"""
import subprocess
import json
import sys
import xml.etree.ElementTree as ET
from datetime import datetime
from pathlib import Path
class NiktoScanAgent:
"""Automates Nikto web vulnerability scanning and reporting."""
def __init__(self, target, output_dir="./nikto_scans"):
self.target = target
self.output_dir = Path(output_dir)
self.output_dir.mkdir(parents=True, exist_ok=True)
self.findings = []
def run_scan(self, ports="80,443", tuning=None, plugins=None,
ssl_mode=False, timeout=600):
"""Execute Nikto scan against the target."""
xml_output = self.output_dir / f"nikto_{self.target.replace('/', '_')}.xml"
cmd = ["nikto", "-h", self.target, "-port", ports,
"-Format", "xml", "-output", str(xml_output), "-nointeractive"]
if ssl_mode:
cmd.extend(["-ssl"])
if tuning:
cmd.extend(["-Tuning", tuning])
if plugins:
cmd.extend(["-Plugins", plugins])
try:
result = subprocess.run(cmd, capture_output=True, text=True,
timeout=timeout)
return {"return_code": result.returncode,
"xml_output": str(xml_output),
"stderr": result.stderr[:500] if result.stderr else ""}
except FileNotFoundError:
return {"error": "nikto not installed. Install: apt install nikto"}
except subprocess.TimeoutExpired:
return {"error": f"Scan timed out after {timeout}s"}
def parse_xml_results(self, xml_path=None):
"""Parse Nikto XML output into structured findings."""
if xml_path is None:
xml_path = self.output_dir / f"nikto_{self.target.replace('/', '_')}.xml"
try:
tree = ET.parse(xml_path)
root = tree.getroot()
except (ET.ParseError, FileNotFoundError) as exc:
return {"error": str(exc)}
for item in root.iter("item"):
finding = {
"id": item.get("id", ""),
"osvdb": item.get("osvdbid", ""),
"method": item.get("method", "GET"),
"uri": "",
"description": "",
"references": [],
}
uri_elem = item.find("uri")
if uri_elem is not None:
finding["uri"] = uri_elem.text or ""
desc_elem = item.find("description")
if desc_elem is not None:
finding["description"] = desc_elem.text or ""
desc_lower = finding["description"].lower()
if any(kw in desc_lower for kw in ["remote code", "rce", "command injection"]):
finding["severity"] = "Critical"
elif any(kw in desc_lower for kw in ["sql injection", "xss", "file inclusion"]):
finding["severity"] = "High"
elif any(kw in desc_lower for kw in ["directory listing", "information disclosure"]):
finding["severity"] = "Medium"
else:
finding["severity"] = "Low"
self.findings.append(finding)
return self.findings
def run_quick_scan(self, timeout=300):
"""Run a fast Nikto scan with essential checks only."""
cmd = ["nikto", "-h", self.target, "-Tuning", "123", "-maxtime",
str(timeout) + "s", "-nointeractive"]
try:
result = subprocess.run(cmd, capture_output=True, text=True,
timeout=timeout + 30)
lines = result.stdout.splitlines()
for line in lines:
if "+ " in line and "OSVDB" in line:
self.findings.append({
"description": line.strip().lstrip("+ "),
"severity": "Medium", "source": "stdout",
})
return {"lines": len(lines), "findings": len(self.findings)}
except (FileNotFoundError, subprocess.TimeoutExpired) as exc:
return {"error": str(exc)}
def generate_report(self):
"""Generate scan report with severity distribution."""
severity_counts = {}
for f in self.findings:
sev = f.get("severity", "Info")
severity_counts[sev] = severity_counts.get(sev, 0) + 1
report = {
"target": self.target,
"scan_date": datetime.utcnow().isoformat(),
"total_findings": len(self.findings),
"severity_distribution": severity_counts,
"findings": self.findings[:100],
}
report_path = self.output_dir / "nikto_report.json"
with open(report_path, "w") as f:
json.dump(report, f, indent=2)
print(json.dumps(report, indent=2))
return report
def main():
target = sys.argv[1] if len(sys.argv) > 1 else "http://localhost"
agent = NiktoScanAgent(target)
result = agent.run_scan()
if "error" not in result:
agent.parse_xml_results()
else:
agent.run_quick_scan()
agent.generate_report()
if __name__ == "__main__":
main()