npx skills add mukul975/Anthropic-Cybersecurity-SkillsMITRE ATT&CK
Overview
Nikto is an open-source web server and web application scanner that tests against over 7,000 potentially dangerous files/programs, checks for outdated versions of over 1,250 servers, and identifies version-specific problems on over 270 servers. It performs comprehensive tests including XSS, SQL injection, server misconfigurations, default credentials, and known vulnerable CGI scripts.
When to Use
- When conducting security assessments that involve performing web application scanning with nikto
- When following incident response procedures for related security events
- When performing scheduled security testing or auditing activities
- When validating security controls through hands-on testing
Prerequisites
- Nikto installed (Perl-based, included in Kali Linux)
- Written authorization to scan target web servers
- Network access to target web applications
- Understanding of HTTP/HTTPS protocols
Core Concepts
What Nikto Detects
- Server misconfigurations and dangerous default files
- Outdated server software versions with known CVEs
- Common CGI vulnerabilities and dangerous scripts
- Default credentials and admin pages
- HTTP methods that should be disabled (PUT, DELETE, TRACE)
- SSL/TLS misconfigurations and weak ciphers
- Missing security headers (X-Frame-Options, CSP, HSTS)
- Information disclosure through headers and error pages
Nikto vs Other Web Scanners
| Feature | Nikto | OWASP ZAP | Burp Suite | Nuclei |
|---|---|---|---|---|
| License | Open Source | Open Source | Commercial | Open Source |
| Focus | Server/Config | App Logic | Full Pentest | Template-Based |
| Speed | Fast | Medium | Slow | Very Fast |
| False Positives | Moderate | Low | Low | Low |
| Authentication | Basic | Full | Full | Template |
| Active Community | Yes | Yes | Yes | Yes |
Workflow
Step 1: Basic Scanning
# Basic scan against a target
nikto -h https://target.example.com
# Scan specific port
nikto -h target.example.com -p 8443
# Scan multiple ports
nikto -h target.example.com -p 80,443,8080,8443
# Scan with SSL enforcement
nikto -h target.example.com -ssl
# Scan from a host list file
nikto -h targets.txtStep 2: Advanced Scanning Options
# Comprehensive scan with all tuning options
nikto -h https://target.example.com \
-Tuning 123456789abcde \
-timeout 10 \
-Pause 2 \
-Display V \
-output report.html \
-Format htm
# Tuning options control test types:
# 0 - File Upload
# 1 - Interesting File / Seen in logs
# 2 - Misconfiguration / Default File
# 3 - Information Disclosure
# 4 - Injection (XSS/Script/HTML)
# 5 - Remote File Retrieval - Inside Web Root
# 6 - Denial of Service
# 7 - Remote File Retrieval - Server Wide
# 8 - Command Execution / Remote Shell
# 9 - SQL Injection
# a - Authentication Bypass
# b - Software Identification
# c - Remote Source Inclusion
# d - WebService
# e - Administrative Console
# Scan with specific tuning (XSS + SQL injection + auth bypass)
nikto -h https://target.example.com -Tuning 49a
# Scan with authentication
nikto -h https://target.example.com -id admin:password
# Scan through a proxy
nikto -h https://target.example.com -useproxy http://proxy:8080
# Scan with custom User-Agent
nikto -h https://target.example.com -useragent "Mozilla/5.0 (Security Scan)"
# Scan specific CGI directories
nikto -h https://target.example.com -Cgidirs /cgi-bin/,/scripts/
# Evasion techniques (IDS avoidance for authorized testing)
# 1-Random URI encoding, 2-Directory self-reference
# 3-Premature URL ending, 4-Prepend long random string
nikto -h https://target.example.com -evasion 1234Step 3: Output and Reporting
# Generate multiple output formats
nikto -h https://target.example.com -output scan.csv -Format csv
nikto -h https://target.example.com -output scan.xml -Format xml
nikto -h https://target.example.com -output scan.html -Format htm
nikto -h https://target.example.com -output scan.txt -Format txt
# JSON output (newer versions)
nikto -h https://target.example.com -output scan.json -Format json
# Save to multiple formats simultaneously
nikto -h https://target.example.com \
-output scan_report \
-Format htmStep 4: Scan Multiple Targets
# Create targets file (one per line)
cat > targets.txt << 'EOF'
https://app1.example.com
https://app2.example.com:8443
http://internal-app.corp.local
192.168.1.100:8080
EOF
# Scan all targets
nikto -h targets.txt -output multi_scan.html -Format htm
# Parallel scanning with GNU parallel
cat targets.txt | parallel -j 5 "nikto -h {} -output {/}_report.html -Format htm"Step 5: SSL/TLS Assessment
# Comprehensive SSL scan
nikto -h https://target.example.com -ssl \
-Tuning b \
-Display V
# Check for specific SSL vulnerabilities
# Nikto checks for:
# - Expired certificates
# - Self-signed certificates
# - Weak cipher suites
# - SSLv2/SSLv3 enabled
# - BEAST, POODLE, Heartbleed indicators
# - Missing HSTS headerStep 6: Integration with Other Tools
# Pipe Nmap results into Nikto
nmap -p 80,443,8080 --open -oG - 192.168.1.0/24 | \
awk '/open/{print $2}' | \
while read host; do nikto -h "$host" -output "${host}_nikto.html" -Format htm; done
# Export to Metasploit-compatible format
nikto -h target.example.com -output msf_import.xml -Format xml
# Parse Nikto XML output with Python for custom reporting
python3 -c "
import xml.etree.ElementTree as ET
tree = ET.parse('scan.xml')
for item in tree.findall('.//item'):
print(f\"[{item.get('id')}] {item.findtext('description', '')[:100]}\")
"Interpreting Results
Severity Classification
- OSVDB/CVE References: Cross-reference with NVD for CVSS scores
- Server Information Disclosure: Version banners, technology stack
- Dangerous HTTP Methods: PUT, DELETE, TRACE enabled
- Default/Backup Files: .bak, .old, .swp, web.config.bak
- Admin Interfaces: /admin, /manager, /console exposed
- Missing Security Headers: CSP, X-Frame-Options, HSTS
Common False Positives
- Generic checks triggered by custom 404 pages
- Anti-CSRF tokens flagged as form vulnerabilities
- CDN/WAF responses misidentified as vulnerable
- Load balancer health check pages
Best Practices
- Always obtain written authorization before scanning
- Run Nikto in conjunction with application-level scanners (ZAP, Burp)
- Use -Pause flag to reduce load on production servers
- Validate findings manually before reporting
- Combine with SSL testing tools (testssl.sh, sslyze) for comprehensive coverage
- Schedule regular scans as part of continuous vulnerability management
- Keep Nikto database updated for latest vulnerability checks
- Use appropriate evasion settings only for authorized IDS testing
Common Pitfalls
- Running Nikto without authorization (legal liability)
- Treating Nikto as a complete web application scanner (it focuses on server/config issues)
- Not validating results leading to false positive reports
- Scanning too aggressively against production systems
- Ignoring SSL/TLS findings as "informational"
References and resources
Everything below is rendered for inspection. Script files are read-only and never run.
References 3
api-reference.md1.7 KB
API Reference: Web Application Scanning with Nikto
Nikto CLI Options
| Flag | Description |
|---|---|
-h <host> |
Target hostname or IP |
-port <ports> |
Target ports (comma-separated) |
-ssl |
Force SSL/TLS connection |
-Format xml|json|csv|htm |
Output format |
-output <file> |
Save results to file |
-Tuning <options> |
Scan tuning categories |
-Plugins <list> |
Specific plugins to run |
-maxtime <seconds>s |
Maximum scan duration |
-nointeractive |
Disable interactive prompts |
-useproxy <url> |
Use HTTP proxy |
-id <user:pass> |
HTTP Basic auth credentials |
Tuning Categories
| Code | Category |
|---|---|
| 1 | Interesting File / Seen in logs |
| 2 | Misconfiguration / Default File |
| 3 | Information Disclosure |
| 4 | Injection (XSS/Script/HTML) |
| 5 | Remote File Retrieval - Inside Web Root |
| 6 | Denial of Service |
| 7 | Remote File Retrieval - Server Wide |
| 8 | Command Execution / Remote Shell |
| 9 | SQL Injection |
| 0 | File Upload |
XML Output Structure
| Element | Description |
|---|---|
<niktoscan> |
Root element |
<scandetails> |
Scan metadata |
<item> |
Individual finding |
<item id="..." osvdbid="..."> |
Finding with OSVDB reference |
<uri> |
Affected URI path |
<description> |
Finding description |
Python Libraries
| Library | Version | Purpose |
|---|---|---|
subprocess |
stdlib | Execute Nikto CLI |
xml.etree.ElementTree |
stdlib | Parse Nikto XML output |
json |
stdlib | Report generation |
References
- Nikto GitHub: https://github.com/sullo/nikto
- Nikto Documentation: https://cirt.net/Nikto2
- OSVDB (archived): https://vulndb.cyberriskanalytics.com/
standards.md2.1 KB
Standards and References - Web Application Scanning with Nikto
Industry Standards
- OWASP Testing Guide v4.2: Web application security testing methodology
- NIST SP 800-115: Technical Guide to Information Security Testing and Assessment
- PCI DSS v4.0 Req 6.4: Address common coding vulnerabilities in development
- PCI DSS v4.0 Req 11.3.3: Perform external vulnerability scans via ASV
Nikto Resources
- Nikto GitHub Repository: https://github.com/sullo/nikto
- Nikto Documentation: https://cirt.net/Nikto2
- Nikto Plugin Database: https://github.com/sullo/nikto/tree/master/plugins
- Nikto Cheat Sheet: https://highon.coffee/blog/nikto-cheat-sheet/
Complementary Web Scanning Tools
| Tool | Purpose | URL |
|---|---|---|
| OWASP ZAP | Application-level scanning | https://www.zaproxy.org/ |
| Nuclei | Template-based scanning | https://github.com/projectdiscovery/nuclei |
| testssl.sh | SSL/TLS assessment | https://testssl.sh/ |
| Wapiti | Web application fuzzer | https://wapiti-scanner.github.io/ |
| WhatWeb | Web technology fingerprinting | https://github.com/urbanadventurer/WhatWeb |
Nikto Tuning Reference
| Code | Category | Description |
|---|---|---|
| 0 | File Upload | Checks for file upload vulnerabilities |
| 1 | Interesting File | Files commonly seen in server logs |
| 2 | Misconfiguration | Default files and misconfigurations |
| 3 | Information Disclosure | Information leakage through headers/pages |
| 4 | Injection (XSS) | Cross-site scripting and HTML injection |
| 5 | Remote File Retrieval | Inside web root file access |
| 6 | Denial of Service | DoS vulnerability checks |
| 7 | Remote File Retrieval | Server-wide file access |
| 8 | Command Execution | RCE and remote shell vulnerabilities |
| 9 | SQL Injection | SQL injection vulnerability checks |
| a | Authentication Bypass | Authentication bypass techniques |
| b | Software Identification | Server and software version detection |
| c | Remote Source Inclusion | RFI/LFI vulnerability checks |
| d | WebService | Web service specific vulnerabilities |
| e | Administrative Console | Admin interface discovery |
workflows.md2.1 KB
Workflows - Web Application Scanning with Nikto
Workflow 1: Standard Web Server Assessment
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Enumerate │──>│ Run Nikto │──>│ Parse XML │
│ Web Servers │ │ Scan │ │ Results │
│ (Nmap/DNS) │ │ (-Format xml)│ │ │
└──────────────┘ └──────────────┘ └──────────────┘
│
┌───────────────────────────────────┘
v
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Validate │──>│ Cross-ref │──>│ Generate │
│ Findings │ │ with NVD │ │ Report │
│ (Manual) │ │ (CVE/CVSS) │ │ │
└──────────────┘ └──────────────┘ └──────────────┘Workflow 2: CI/CD Integration
Code Push → Build → Deploy to Staging
│
Run Nikto Scan
│
┌───────┴───────┐
│ │
No Findings Findings Found
│ │
Deploy to Block Deploy
Production Notify TeamWorkflow 3: Multi-Tool Web Assessment
- Nikto: Server configuration and known vulnerability checks
- OWASP ZAP: Application logic and dynamic analysis
- testssl.sh: Comprehensive SSL/TLS assessment
- Nuclei: Template-based CVE validation
- Manual Testing: Validate and verify all findings
- Consolidated Report: Merge results from all tools
Scripts 2
agent.py5.3 KB
#!/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()
process.py12.0 KB
#!/usr/bin/env python3
"""
Nikto Web Application Scanning Automation
Automates Nikto scanning across multiple targets, parses results,
and generates consolidated vulnerability reports.
Requirements:
pip install pandas defusedxml jinja2
System: nikto installed and in PATH
Usage:
python process.py scan --targets targets.txt --output-dir ./results
python process.py parse --xml-dir ./results --report report.html
"""
import argparse
import os
import subprocess
import sys
from concurrent.futures import ThreadPoolExecutor, as_completed
from datetime import datetime
from pathlib import Path
from urllib.parse import urlparse
import defusedxml.ElementTree as ET
import pandas as pd
class NiktoScanner:
"""Automated Nikto web scanning manager."""
def __init__(self, output_dir: str = "./nikto_results", timeout: int = 600):
self.output_dir = Path(output_dir)
self.output_dir.mkdir(parents=True, exist_ok=True)
self.timeout = timeout
self.results = []
def scan_target(self, target: str, tuning: str = "123456789abc",
pause: int = 1, ssl: bool = False) -> dict:
"""Run Nikto scan against a single target."""
parsed = urlparse(target if "://" in target else f"http://{target}")
safe_name = parsed.netloc.replace(":", "_").replace("/", "_")
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
xml_output = self.output_dir / f"{safe_name}_{timestamp}.xml"
cmd = [
"nikto",
"-h", target,
"-Tuning", tuning,
"-Pause", str(pause),
"-timeout", "10",
"-nointeractive",
"-output", str(xml_output),
"-Format", "xml",
]
if ssl or parsed.scheme == "https":
cmd.append("-ssl")
result = {
"target": target,
"status": "unknown",
"output_file": str(xml_output),
"findings": [],
"start_time": datetime.now().isoformat(),
}
print(f"[*] Scanning {target}...")
try:
proc = subprocess.run(
cmd, capture_output=True, text=True, timeout=self.timeout
)
result["status"] = "completed"
result["end_time"] = datetime.now().isoformat()
result["stdout"] = proc.stdout[-2000:] if proc.stdout else ""
result["stderr"] = proc.stderr[-500:] if proc.stderr else ""
if xml_output.exists():
result["findings"] = self.parse_xml(str(xml_output))
finding_count = len(result["findings"])
print(f"[+] {target}: {finding_count} findings")
else:
print(f"[!] {target}: No XML output generated")
result["status"] = "no_output"
except subprocess.TimeoutExpired:
result["status"] = "timeout"
print(f"[-] {target}: Scan timed out after {self.timeout}s")
except FileNotFoundError:
result["status"] = "error"
result["error"] = "nikto not found in PATH"
print("[-] Error: nikto not found. Install with: apt install nikto")
except Exception as e:
result["status"] = "error"
result["error"] = str(e)
print(f"[-] {target}: Error - {e}")
self.results.append(result)
return result
def scan_targets(self, targets: list, max_parallel: int = 3, **kwargs) -> list:
"""Scan multiple targets with optional parallelism."""
self.results = []
with ThreadPoolExecutor(max_workers=max_parallel) as executor:
futures = {
executor.submit(self.scan_target, target, **kwargs): target
for target in targets
}
for future in as_completed(futures):
try:
future.result()
except Exception as e:
target = futures[future]
print(f"[-] {target}: Scan failed - {e}")
return self.results
@staticmethod
def parse_xml(xml_path: str) -> list:
"""Parse Nikto XML output into structured findings."""
findings = []
try:
tree = ET.parse(xml_path)
root = tree.getroot()
for scan_details in root.findall(".//scandetails"):
target_ip = scan_details.get("targetip", "")
target_host = scan_details.get("targethostname", "")
target_port = scan_details.get("targetport", "")
target_banner = scan_details.get("targetbanner", "")
for item in scan_details.findall("item"):
finding = {
"target_ip": target_ip,
"target_host": target_host,
"target_port": target_port,
"server_banner": target_banner,
"nikto_id": item.get("id", ""),
"osvdb_id": item.get("osvdbid", ""),
"osvdb_link": item.get("osvdblink", ""),
"method": item.get("method", "GET"),
"uri": item.findtext("uri", ""),
"description": item.findtext("description", ""),
"name_link": item.findtext("namelink", ""),
"ip_link": item.findtext("iplink", ""),
}
# Classify severity based on description keywords
desc_lower = finding["description"].lower()
if any(w in desc_lower for w in ["remote code", "command execution", "backdoor", "rce"]):
finding["severity"] = "Critical"
elif any(w in desc_lower for w in ["sql injection", "xss", "cross-site", "file inclusion"]):
finding["severity"] = "High"
elif any(w in desc_lower for w in ["directory listing", "information disclosure", "version"]):
finding["severity"] = "Medium"
elif any(w in desc_lower for w in ["header", "cookie", "x-frame"]):
finding["severity"] = "Low"
else:
finding["severity"] = "Info"
findings.append(finding)
except Exception as e:
print(f"[!] XML parse error for {xml_path}: {e}")
return findings
def generate_report(self, output_path: str):
"""Generate consolidated HTML report from all scan results."""
all_findings = []
for result in self.results:
for finding in result.get("findings", []):
finding["scan_target"] = result["target"]
finding["scan_status"] = result["status"]
all_findings.append(finding)
if not all_findings:
print("[-] No findings to report")
return
df = pd.DataFrame(all_findings)
# Severity counts
sev_counts = df["severity"].value_counts().to_dict()
# Target summary
target_summary = (df.groupby(["scan_target", "target_port"])
.agg(findings=("nikto_id", "count"),
critical=("severity", lambda x: (x == "Critical").sum()),
high=("severity", lambda x: (x == "High").sum()))
.reset_index())
html = f"""<!DOCTYPE html>
<html>
<head>
<title>Nikto Scan Report - {datetime.now().strftime('%Y-%m-%d')}</title>
<style>
body {{ font-family: Arial, sans-serif; margin: 20px; background: #f5f5f5; }}
.header {{ background: #16213e; color: white; padding: 20px; border-radius: 8px; }}
.cards {{ display: flex; gap: 15px; margin: 20px 0; }}
.card {{ background: white; padding: 20px; border-radius: 8px; flex: 1; text-align: center;
box-shadow: 0 2px 4px rgba(0,0,0,0.1); }}
.card h3 {{ margin: 0; font-size: 2em; }}
table {{ width: 100%; border-collapse: collapse; background: white; margin: 15px 0;
border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }}
th {{ background: #2c3e50; color: white; padding: 10px; text-align: left; }}
td {{ padding: 8px 10px; border-bottom: 1px solid #eee; font-size: 0.9em; }}
.sev-critical {{ color: #e74c3c; font-weight: bold; }}
.sev-high {{ color: #e67e22; font-weight: bold; }}
.sev-medium {{ color: #f39c12; }}
.sev-low {{ color: #3498db; }}
</style>
</head>
<body>
<div class="header">
<h1>Nikto Web Scan Report</h1>
<p>Targets: {len(self.results)} | Generated: {datetime.now().strftime('%Y-%m-%d %H:%M')}</p>
</div>
<div class="cards">
<div class="card" style="border-top:4px solid #e74c3c"><h3>{sev_counts.get('Critical', 0)}</h3><p>Critical</p></div>
<div class="card" style="border-top:4px solid #e67e22"><h3>{sev_counts.get('High', 0)}</h3><p>High</p></div>
<div class="card" style="border-top:4px solid #f39c12"><h3>{sev_counts.get('Medium', 0)}</h3><p>Medium</p></div>
<div class="card" style="border-top:4px solid #3498db"><h3>{sev_counts.get('Low', 0)}</h3><p>Low</p></div>
</div>
<h2>Target Summary</h2>
<table>
<tr><th>Target</th><th>Port</th><th>Total</th><th>Critical</th><th>High</th></tr>
{''.join(f"<tr><td>{r.scan_target}</td><td>{r.target_port}</td><td>{r.findings}</td><td>{r.critical}</td><td>{r.high}</td></tr>" for r in target_summary.itertuples())}
</table>
<h2>All Findings</h2>
<table>
<tr><th>Target</th><th>Severity</th><th>URI</th><th>Description</th><th>OSVDB</th></tr>
{''.join(f'<tr><td>{r.scan_target}</td><td class="sev-{r.severity.lower()}">{r.severity}</td><td>{r.uri}</td><td>{r.description[:150]}</td><td>{r.osvdb_id}</td></tr>' for r in df.sort_values("severity").itertuples())}
</table>
</body>
</html>"""
with open(output_path, "w", encoding="utf-8") as f:
f.write(html)
print(f"[+] Report saved to: {output_path}")
def main():
parser = argparse.ArgumentParser(description="Nikto Web Scanning Automation")
subparsers = parser.add_subparsers(dest="command")
scan_p = subparsers.add_parser("scan", help="Scan web targets with Nikto")
scan_p.add_argument("--targets", required=True, help="File with target URLs")
scan_p.add_argument("--output-dir", default="./nikto_results")
scan_p.add_argument("--report", default=None, help="HTML report output path")
scan_p.add_argument("--tuning", default="123456789abc", help="Nikto tuning options")
scan_p.add_argument("--parallel", type=int, default=3, help="Max parallel scans")
scan_p.add_argument("--timeout", type=int, default=600, help="Per-target timeout")
scan_p.add_argument("--pause", type=int, default=1, help="Pause between requests")
parse_p = subparsers.add_parser("parse", help="Parse existing Nikto XML results")
parse_p.add_argument("--xml-dir", required=True, help="Directory with Nikto XML files")
parse_p.add_argument("--report", required=True, help="HTML report output path")
args = parser.parse_args()
if args.command == "scan":
with open(args.targets) as f:
targets = [line.strip() for line in f if line.strip() and not line.startswith("#")]
scanner = NiktoScanner(args.output_dir, args.timeout)
scanner.scan_targets(targets, max_parallel=args.parallel,
tuning=args.tuning, pause=args.pause)
report_path = args.report or os.path.join(args.output_dir, "nikto_report.html")
scanner.generate_report(report_path)
elif args.command == "parse":
scanner = NiktoScanner()
xml_dir = Path(args.xml_dir)
for xml_file in xml_dir.glob("*.xml"):
findings = scanner.parse_xml(str(xml_file))
scanner.results.append({
"target": xml_file.stem,
"status": "parsed",
"findings": findings,
})
scanner.generate_report(args.report)
else:
parser.print_help()
if __name__ == "__main__":
main()