mirror of
https://github.com/FuzzingLabs/fuzzforge_ai.git
synced 2026-07-02 02:15:32 +02:00
Initial commit
This commit is contained in:
@@ -0,0 +1,671 @@
|
||||
"""
|
||||
SQLMap Penetration Testing Module
|
||||
|
||||
This module uses SQLMap for automatic SQL injection detection and exploitation.
|
||||
"""
|
||||
# Copyright (c) 2025 FuzzingLabs
|
||||
#
|
||||
# Licensed under the Business Source License 1.1 (BSL). See the LICENSE file
|
||||
# at the root of this repository for details.
|
||||
#
|
||||
# After the Change Date (four years from publication), this version of the
|
||||
# Licensed Work will be made available under the Apache License, Version 2.0.
|
||||
# See the LICENSE-APACHE file or http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Additional attribution and requirements are provided in the NOTICE file.
|
||||
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Dict, Any, List
|
||||
import subprocess
|
||||
import logging
|
||||
|
||||
from ..base import BaseModule, ModuleMetadata, ModuleFinding, ModuleResult
|
||||
from . import register_module
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@register_module
|
||||
class SQLMapModule(BaseModule):
|
||||
"""SQLMap automatic SQL injection detection and exploitation module"""
|
||||
|
||||
def get_metadata(self) -> ModuleMetadata:
|
||||
"""Get module metadata"""
|
||||
return ModuleMetadata(
|
||||
name="sqlmap",
|
||||
version="1.7.11",
|
||||
description="Automatic SQL injection detection and exploitation tool",
|
||||
author="FuzzForge Team",
|
||||
category="penetration_testing",
|
||||
tags=["sql-injection", "web", "database", "vulnerability", "exploitation"],
|
||||
input_schema={
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"target_url": {
|
||||
"type": "string",
|
||||
"description": "Target URL to test for SQL injection"
|
||||
},
|
||||
"target_file": {
|
||||
"type": "string",
|
||||
"description": "File containing URLs to test"
|
||||
},
|
||||
"request_file": {
|
||||
"type": "string",
|
||||
"description": "Load HTTP request from file (Burp log, etc.)"
|
||||
},
|
||||
"data": {
|
||||
"type": "string",
|
||||
"description": "Data string to be sent through POST"
|
||||
},
|
||||
"cookie": {
|
||||
"type": "string",
|
||||
"description": "HTTP Cookie header value"
|
||||
},
|
||||
"user_agent": {
|
||||
"type": "string",
|
||||
"description": "HTTP User-Agent header value"
|
||||
},
|
||||
"referer": {
|
||||
"type": "string",
|
||||
"description": "HTTP Referer header value"
|
||||
},
|
||||
"headers": {
|
||||
"type": "object",
|
||||
"description": "Additional HTTP headers"
|
||||
},
|
||||
"method": {
|
||||
"type": "string",
|
||||
"enum": ["GET", "POST", "PUT", "DELETE", "PATCH"],
|
||||
"default": "GET",
|
||||
"description": "HTTP method to use"
|
||||
},
|
||||
"testable_parameters": {
|
||||
"type": "array",
|
||||
"items": {"type": "string"},
|
||||
"description": "Comma-separated list of testable parameter(s)"
|
||||
},
|
||||
"skip_parameters": {
|
||||
"type": "array",
|
||||
"items": {"type": "string"},
|
||||
"description": "Parameters to skip during testing"
|
||||
},
|
||||
"dbms": {
|
||||
"type": "string",
|
||||
"enum": ["mysql", "postgresql", "oracle", "mssql", "sqlite", "access", "firebird", "sybase", "db2", "hsqldb", "h2"],
|
||||
"description": "Force back-end DBMS to provided value"
|
||||
},
|
||||
"level": {
|
||||
"type": "integer",
|
||||
"enum": [1, 2, 3, 4, 5],
|
||||
"default": 1,
|
||||
"description": "Level of tests to perform (1-5)"
|
||||
},
|
||||
"risk": {
|
||||
"type": "integer",
|
||||
"enum": [1, 2, 3],
|
||||
"default": 1,
|
||||
"description": "Risk of tests to perform (1-3)"
|
||||
},
|
||||
"technique": {
|
||||
"type": "array",
|
||||
"items": {"type": "string", "enum": ["B", "E", "U", "S", "T", "Q"]},
|
||||
"description": "SQL injection techniques to use (B=Boolean, E=Error, U=Union, S=Stacked, T=Time, Q=Inline)"
|
||||
},
|
||||
"time_sec": {
|
||||
"type": "integer",
|
||||
"default": 5,
|
||||
"description": "Seconds to delay DBMS response for time-based blind SQL injection"
|
||||
},
|
||||
"union_cols": {
|
||||
"type": "string",
|
||||
"description": "Range of columns to test for UNION query SQL injection"
|
||||
},
|
||||
"threads": {
|
||||
"type": "integer",
|
||||
"default": 1,
|
||||
"description": "Maximum number of concurrent HTTP requests"
|
||||
},
|
||||
"timeout": {
|
||||
"type": "integer",
|
||||
"default": 30,
|
||||
"description": "Seconds to wait before timeout connection"
|
||||
},
|
||||
"retries": {
|
||||
"type": "integer",
|
||||
"default": 3,
|
||||
"description": "Retries when connection timeouts"
|
||||
},
|
||||
"randomize": {
|
||||
"type": "boolean",
|
||||
"default": True,
|
||||
"description": "Randomly change value of given parameter(s)"
|
||||
},
|
||||
"safe_url": {
|
||||
"type": "string",
|
||||
"description": "URL to visit frequently during testing"
|
||||
},
|
||||
"safe_freq": {
|
||||
"type": "integer",
|
||||
"description": "Test requests between visits to safe URL"
|
||||
},
|
||||
"crawl": {
|
||||
"type": "integer",
|
||||
"description": "Crawl website starting from target URL (depth)"
|
||||
},
|
||||
"forms": {
|
||||
"type": "boolean",
|
||||
"default": False,
|
||||
"description": "Parse and test forms on target URL"
|
||||
},
|
||||
"batch": {
|
||||
"type": "boolean",
|
||||
"default": True,
|
||||
"description": "Never ask for user input, use default behavior"
|
||||
},
|
||||
"cleanup": {
|
||||
"type": "boolean",
|
||||
"default": True,
|
||||
"description": "Clean up files used by SQLMap"
|
||||
},
|
||||
"check_waf": {
|
||||
"type": "boolean",
|
||||
"default": False,
|
||||
"description": "Check for existence of WAF/IPS protection"
|
||||
},
|
||||
"tamper": {
|
||||
"type": "array",
|
||||
"items": {"type": "string"},
|
||||
"description": "Use tamper scripts to modify requests"
|
||||
}
|
||||
}
|
||||
},
|
||||
output_schema={
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"findings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"url": {"type": "string"},
|
||||
"parameter": {"type": "string"},
|
||||
"technique": {"type": "string"},
|
||||
"dbms": {"type": "string"},
|
||||
"payload": {"type": "string"}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
def validate_config(self, config: Dict[str, Any]) -> bool:
|
||||
"""Validate configuration"""
|
||||
target_url = config.get("target_url")
|
||||
target_file = config.get("target_file")
|
||||
request_file = config.get("request_file")
|
||||
|
||||
if not any([target_url, target_file, request_file]):
|
||||
raise ValueError("Either 'target_url', 'target_file', or 'request_file' must be specified")
|
||||
|
||||
level = config.get("level", 1)
|
||||
if level not in [1, 2, 3, 4, 5]:
|
||||
raise ValueError("Level must be between 1 and 5")
|
||||
|
||||
risk = config.get("risk", 1)
|
||||
if risk not in [1, 2, 3]:
|
||||
raise ValueError("Risk must be between 1 and 3")
|
||||
|
||||
return True
|
||||
|
||||
async def execute(self, config: Dict[str, Any], workspace: Path) -> ModuleResult:
|
||||
"""Execute SQLMap SQL injection testing"""
|
||||
self.start_timer()
|
||||
|
||||
try:
|
||||
# Validate inputs
|
||||
self.validate_config(config)
|
||||
self.validate_workspace(workspace)
|
||||
|
||||
logger.info("Running SQLMap SQL injection scan")
|
||||
|
||||
# Run SQLMap scan
|
||||
findings = await self._run_sqlmap_scan(config, workspace)
|
||||
|
||||
# Create summary
|
||||
summary = self._create_summary(findings)
|
||||
|
||||
logger.info(f"SQLMap found {len(findings)} SQL injection vulnerabilities")
|
||||
|
||||
return self.create_result(
|
||||
findings=findings,
|
||||
status="success",
|
||||
summary=summary
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"SQLMap module failed: {e}")
|
||||
return self.create_result(
|
||||
findings=[],
|
||||
status="failed",
|
||||
error=str(e)
|
||||
)
|
||||
|
||||
async def _run_sqlmap_scan(self, config: Dict[str, Any], workspace: Path) -> List[ModuleFinding]:
|
||||
"""Run SQLMap scan"""
|
||||
findings = []
|
||||
|
||||
try:
|
||||
# Build sqlmap command
|
||||
cmd = ["sqlmap"]
|
||||
|
||||
# Add target specification
|
||||
target_url = config.get("target_url")
|
||||
if target_url:
|
||||
cmd.extend(["-u", target_url])
|
||||
|
||||
target_file = config.get("target_file")
|
||||
if target_file:
|
||||
target_path = workspace / target_file
|
||||
if target_path.exists():
|
||||
cmd.extend(["-m", str(target_path)])
|
||||
else:
|
||||
raise FileNotFoundError(f"Target file not found: {target_file}")
|
||||
|
||||
request_file = config.get("request_file")
|
||||
if request_file:
|
||||
request_path = workspace / request_file
|
||||
if request_path.exists():
|
||||
cmd.extend(["-r", str(request_path)])
|
||||
else:
|
||||
raise FileNotFoundError(f"Request file not found: {request_file}")
|
||||
|
||||
# Add HTTP options
|
||||
data = config.get("data")
|
||||
if data:
|
||||
cmd.extend(["--data", data])
|
||||
|
||||
cookie = config.get("cookie")
|
||||
if cookie:
|
||||
cmd.extend(["--cookie", cookie])
|
||||
|
||||
user_agent = config.get("user_agent")
|
||||
if user_agent:
|
||||
cmd.extend(["--user-agent", user_agent])
|
||||
|
||||
referer = config.get("referer")
|
||||
if referer:
|
||||
cmd.extend(["--referer", referer])
|
||||
|
||||
headers = config.get("headers", {})
|
||||
for key, value in headers.items():
|
||||
cmd.extend(["--header", f"{key}: {value}"])
|
||||
|
||||
method = config.get("method")
|
||||
if method and method != "GET":
|
||||
cmd.extend(["--method", method])
|
||||
|
||||
# Add parameter options
|
||||
testable_params = config.get("testable_parameters", [])
|
||||
if testable_params:
|
||||
cmd.extend(["-p", ",".join(testable_params)])
|
||||
|
||||
skip_params = config.get("skip_parameters", [])
|
||||
if skip_params:
|
||||
cmd.extend(["--skip", ",".join(skip_params)])
|
||||
|
||||
# Add injection options
|
||||
dbms = config.get("dbms")
|
||||
if dbms:
|
||||
cmd.extend(["--dbms", dbms])
|
||||
|
||||
level = config.get("level", 1)
|
||||
cmd.extend(["--level", str(level)])
|
||||
|
||||
risk = config.get("risk", 1)
|
||||
cmd.extend(["--risk", str(risk)])
|
||||
|
||||
techniques = config.get("technique", [])
|
||||
if techniques:
|
||||
cmd.extend(["--technique", "".join(techniques)])
|
||||
|
||||
time_sec = config.get("time_sec", 5)
|
||||
cmd.extend(["--time-sec", str(time_sec)])
|
||||
|
||||
union_cols = config.get("union_cols")
|
||||
if union_cols:
|
||||
cmd.extend(["--union-cols", union_cols])
|
||||
|
||||
# Add performance options
|
||||
threads = config.get("threads", 1)
|
||||
cmd.extend(["--threads", str(threads)])
|
||||
|
||||
timeout = config.get("timeout", 30)
|
||||
cmd.extend(["--timeout", str(timeout)])
|
||||
|
||||
retries = config.get("retries", 3)
|
||||
cmd.extend(["--retries", str(retries)])
|
||||
|
||||
# Add request options
|
||||
if config.get("randomize", True):
|
||||
cmd.append("--randomize")
|
||||
|
||||
safe_url = config.get("safe_url")
|
||||
if safe_url:
|
||||
cmd.extend(["--safe-url", safe_url])
|
||||
|
||||
safe_freq = config.get("safe_freq")
|
||||
if safe_freq:
|
||||
cmd.extend(["--safe-freq", str(safe_freq)])
|
||||
|
||||
# Add crawling options
|
||||
crawl_depth = config.get("crawl")
|
||||
if crawl_depth:
|
||||
cmd.extend(["--crawl", str(crawl_depth)])
|
||||
|
||||
if config.get("forms", False):
|
||||
cmd.append("--forms")
|
||||
|
||||
# Add behavioral options
|
||||
if config.get("batch", True):
|
||||
cmd.append("--batch")
|
||||
|
||||
if config.get("cleanup", True):
|
||||
cmd.append("--cleanup")
|
||||
|
||||
if config.get("check_waf", False):
|
||||
cmd.append("--check-waf")
|
||||
|
||||
# Add tamper scripts
|
||||
tamper_scripts = config.get("tamper", [])
|
||||
if tamper_scripts:
|
||||
cmd.extend(["--tamper", ",".join(tamper_scripts)])
|
||||
|
||||
# Set output directory
|
||||
output_dir = workspace / "sqlmap_output"
|
||||
output_dir.mkdir(exist_ok=True)
|
||||
cmd.extend(["--output-dir", str(output_dir)])
|
||||
|
||||
# Add format for easier parsing
|
||||
cmd.append("--flush-session") # Start fresh
|
||||
cmd.append("--fresh-queries") # Ignore previous results
|
||||
|
||||
logger.debug(f"Running command: {' '.join(cmd)}")
|
||||
|
||||
# Run sqlmap
|
||||
process = await asyncio.create_subprocess_exec(
|
||||
*cmd,
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE,
|
||||
cwd=workspace
|
||||
)
|
||||
|
||||
stdout, stderr = await process.communicate()
|
||||
|
||||
# Parse results from output directory
|
||||
findings = self._parse_sqlmap_output(output_dir, stdout.decode(), workspace)
|
||||
|
||||
# Log results
|
||||
if findings:
|
||||
logger.info(f"SQLMap detected {len(findings)} SQL injection vulnerabilities")
|
||||
else:
|
||||
logger.info("No SQL injection vulnerabilities found")
|
||||
# Check for errors
|
||||
stderr_text = stderr.decode()
|
||||
if stderr_text:
|
||||
logger.warning(f"SQLMap warnings/errors: {stderr_text}")
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(f"Error running SQLMap scan: {e}")
|
||||
|
||||
return findings
|
||||
|
||||
def _parse_sqlmap_output(self, output_dir: Path, stdout: str, workspace: Path) -> List[ModuleFinding]:
|
||||
"""Parse SQLMap output into findings"""
|
||||
findings = []
|
||||
|
||||
try:
|
||||
# Look for session files in output directory
|
||||
session_files = list(output_dir.glob("**/*.sqlite"))
|
||||
log_files = list(output_dir.glob("**/*.log"))
|
||||
|
||||
# Parse stdout for injection information
|
||||
findings.extend(self._parse_stdout_output(stdout))
|
||||
|
||||
# Parse log files for additional details
|
||||
for log_file in log_files:
|
||||
findings.extend(self._parse_log_file(log_file))
|
||||
|
||||
# If we have session files, we can extract more detailed information
|
||||
# For now, we'll rely on stdout parsing
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(f"Error parsing SQLMap output: {e}")
|
||||
|
||||
return findings
|
||||
|
||||
def _parse_stdout_output(self, stdout: str) -> List[ModuleFinding]:
|
||||
"""Parse SQLMap stdout for SQL injection findings"""
|
||||
findings = []
|
||||
|
||||
try:
|
||||
lines = stdout.split('\n')
|
||||
current_url = None
|
||||
current_parameter = None
|
||||
current_technique = None
|
||||
current_dbms = None
|
||||
injection_found = False
|
||||
|
||||
for line in lines:
|
||||
line = line.strip()
|
||||
|
||||
# Extract URL being tested
|
||||
if "testing URL" in line or "testing connection to the target URL" in line:
|
||||
# Extract URL from line
|
||||
if "'" in line:
|
||||
url_start = line.find("'") + 1
|
||||
url_end = line.find("'", url_start)
|
||||
if url_end > url_start:
|
||||
current_url = line[url_start:url_end]
|
||||
|
||||
# Extract parameter being tested
|
||||
elif "testing parameter" in line or "testing" in line and "parameter" in line:
|
||||
if "'" in line:
|
||||
param_parts = line.split("'")
|
||||
if len(param_parts) >= 2:
|
||||
current_parameter = param_parts[1]
|
||||
|
||||
# Detect SQL injection found
|
||||
elif any(indicator in line.lower() for indicator in [
|
||||
"parameter appears to be vulnerable",
|
||||
"injectable",
|
||||
"parameter is vulnerable"
|
||||
]):
|
||||
injection_found = True
|
||||
|
||||
# Extract technique information
|
||||
elif "Type:" in line:
|
||||
current_technique = line.replace("Type:", "").strip()
|
||||
|
||||
# Extract database information
|
||||
elif "back-end DBMS:" in line.lower():
|
||||
current_dbms = line.split(":")[-1].strip()
|
||||
|
||||
# Extract payload information
|
||||
elif "Payload:" in line:
|
||||
payload = line.replace("Payload:", "").strip()
|
||||
|
||||
# Create finding if we have injection
|
||||
if injection_found and current_url and current_parameter:
|
||||
finding = self._create_sqlmap_finding(
|
||||
current_url, current_parameter, current_technique,
|
||||
current_dbms, payload
|
||||
)
|
||||
if finding:
|
||||
findings.append(finding)
|
||||
|
||||
# Reset state
|
||||
injection_found = False
|
||||
current_technique = None
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(f"Error parsing SQLMap stdout: {e}")
|
||||
|
||||
return findings
|
||||
|
||||
def _parse_log_file(self, log_file: Path) -> List[ModuleFinding]:
|
||||
"""Parse SQLMap log file for additional findings"""
|
||||
findings = []
|
||||
|
||||
try:
|
||||
with open(log_file, 'r') as f:
|
||||
content = f.read()
|
||||
|
||||
# Look for injection indicators in log
|
||||
if "injectable" in content.lower() or "vulnerable" in content.lower():
|
||||
# Could parse more detailed information from log
|
||||
# For now, we'll rely on stdout parsing
|
||||
pass
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(f"Error parsing log file {log_file}: {e}")
|
||||
|
||||
return findings
|
||||
|
||||
def _create_sqlmap_finding(self, url: str, parameter: str, technique: str, dbms: str, payload: str) -> ModuleFinding:
|
||||
"""Create a ModuleFinding for SQL injection"""
|
||||
try:
|
||||
# Map technique to readable description
|
||||
technique_map = {
|
||||
"boolean-based blind": "Boolean-based blind SQL injection",
|
||||
"time-based blind": "Time-based blind SQL injection",
|
||||
"error-based": "Error-based SQL injection",
|
||||
"UNION query": "UNION-based SQL injection",
|
||||
"stacked queries": "Stacked queries SQL injection",
|
||||
"inline query": "Inline query SQL injection"
|
||||
}
|
||||
|
||||
technique_desc = technique_map.get(technique, technique or "SQL injection")
|
||||
|
||||
# Create description
|
||||
description = f"SQL injection vulnerability detected in parameter '{parameter}' using {technique_desc}"
|
||||
if dbms:
|
||||
description += f" against {dbms} database"
|
||||
|
||||
# Determine severity based on technique
|
||||
severity = self._get_injection_severity(technique, dbms)
|
||||
|
||||
# Create finding
|
||||
finding = self.create_finding(
|
||||
title=f"SQL Injection: {parameter}",
|
||||
description=description,
|
||||
severity=severity,
|
||||
category="sql_injection",
|
||||
file_path=None, # Web application testing
|
||||
recommendation=self._get_sqlinjection_recommendation(technique, dbms),
|
||||
metadata={
|
||||
"url": url,
|
||||
"parameter": parameter,
|
||||
"technique": technique,
|
||||
"dbms": dbms,
|
||||
"payload": payload[:500] if payload else "", # Limit payload length
|
||||
"injection_type": technique_desc
|
||||
}
|
||||
)
|
||||
|
||||
return finding
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(f"Error creating SQLMap finding: {e}")
|
||||
return None
|
||||
|
||||
def _get_injection_severity(self, technique: str, dbms: str) -> str:
|
||||
"""Determine severity based on injection technique and database"""
|
||||
if not technique:
|
||||
return "high" # Any SQL injection is serious
|
||||
|
||||
technique_lower = technique.lower()
|
||||
|
||||
# Critical severity for techniques that allow easy data extraction
|
||||
if any(term in technique_lower for term in ["union", "error-based"]):
|
||||
return "critical"
|
||||
|
||||
# High severity for techniques that allow some data extraction
|
||||
elif any(term in technique_lower for term in ["boolean-based", "time-based"]):
|
||||
return "high"
|
||||
|
||||
# Stacked queries are very dangerous as they allow multiple statements
|
||||
elif "stacked" in technique_lower:
|
||||
return "critical"
|
||||
|
||||
else:
|
||||
return "high"
|
||||
|
||||
def _get_sqlinjection_recommendation(self, technique: str, dbms: str) -> str:
|
||||
"""Generate recommendation for SQL injection"""
|
||||
base_recommendation = "Implement parameterized queries/prepared statements and input validation to prevent SQL injection attacks."
|
||||
|
||||
if technique:
|
||||
technique_lower = technique.lower()
|
||||
if "union" in technique_lower:
|
||||
base_recommendation += " The UNION-based injection allows direct data extraction - immediate remediation required."
|
||||
elif "error-based" in technique_lower:
|
||||
base_recommendation += " Error-based injection reveals database structure - disable error messages in production."
|
||||
elif "time-based" in technique_lower:
|
||||
base_recommendation += " Time-based injection allows blind data extraction - implement query timeout limits."
|
||||
elif "stacked" in technique_lower:
|
||||
base_recommendation += " Stacked queries injection allows multiple SQL statements - extremely dangerous, fix immediately."
|
||||
|
||||
if dbms:
|
||||
dbms_lower = dbms.lower()
|
||||
if "mysql" in dbms_lower:
|
||||
base_recommendation += " For MySQL: disable LOAD_FILE and INTO OUTFILE if not needed."
|
||||
elif "postgresql" in dbms_lower:
|
||||
base_recommendation += " For PostgreSQL: review user privileges and disable unnecessary functions."
|
||||
elif "mssql" in dbms_lower:
|
||||
base_recommendation += " For SQL Server: disable xp_cmdshell and review extended stored procedures."
|
||||
|
||||
return base_recommendation
|
||||
|
||||
def _create_summary(self, findings: List[ModuleFinding]) -> Dict[str, Any]:
|
||||
"""Create analysis summary"""
|
||||
severity_counts = {"critical": 0, "high": 0, "medium": 0, "low": 0, "info": 0}
|
||||
technique_counts = {}
|
||||
dbms_counts = {}
|
||||
parameter_counts = {}
|
||||
url_counts = {}
|
||||
|
||||
for finding in findings:
|
||||
# Count by severity
|
||||
severity_counts[finding.severity] += 1
|
||||
|
||||
# Count by technique
|
||||
technique = finding.metadata.get("technique", "unknown")
|
||||
technique_counts[technique] = technique_counts.get(technique, 0) + 1
|
||||
|
||||
# Count by DBMS
|
||||
dbms = finding.metadata.get("dbms", "unknown")
|
||||
if dbms != "unknown":
|
||||
dbms_counts[dbms] = dbms_counts.get(dbms, 0) + 1
|
||||
|
||||
# Count by parameter
|
||||
parameter = finding.metadata.get("parameter", "unknown")
|
||||
parameter_counts[parameter] = parameter_counts.get(parameter, 0) + 1
|
||||
|
||||
# Count by URL
|
||||
url = finding.metadata.get("url", "unknown")
|
||||
url_counts[url] = url_counts.get(url, 0) + 1
|
||||
|
||||
return {
|
||||
"total_findings": len(findings),
|
||||
"severity_counts": severity_counts,
|
||||
"technique_counts": technique_counts,
|
||||
"dbms_counts": dbms_counts,
|
||||
"vulnerable_parameters": list(parameter_counts.keys()),
|
||||
"vulnerable_urls": len(url_counts),
|
||||
"most_common_techniques": dict(sorted(technique_counts.items(), key=lambda x: x[1], reverse=True)[:5]),
|
||||
"affected_databases": list(dbms_counts.keys())
|
||||
}
|
||||
Reference in New Issue
Block a user