zen-ai-pentest 2.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agents/__init__.py +28 -0
- agents/agent_base.py +239 -0
- agents/agent_orchestrator.py +346 -0
- agents/analysis_agent.py +225 -0
- agents/cli.py +258 -0
- agents/exploit_agent.py +224 -0
- agents/integration.py +211 -0
- agents/post_scan_agent.py +937 -0
- agents/react_agent.py +384 -0
- agents/react_agent_enhanced.py +616 -0
- agents/react_agent_vm.py +298 -0
- agents/research_agent.py +176 -0
- api/__init__.py +11 -0
- api/auth.py +123 -0
- api/main.py +1027 -0
- api/schemas.py +357 -0
- api/websocket.py +97 -0
- autonomous/__init__.py +122 -0
- autonomous/agent.py +253 -0
- autonomous/agent_loop.py +1370 -0
- autonomous/exploit_validator.py +1537 -0
- autonomous/memory.py +448 -0
- autonomous/react.py +339 -0
- autonomous/tool_executor.py +488 -0
- backends/__init__.py +16 -0
- backends/chatgpt_direct.py +133 -0
- backends/claude_direct.py +130 -0
- backends/duckduckgo.py +138 -0
- backends/openrouter.py +120 -0
- benchmarks/__init__.py +149 -0
- benchmarks/benchmark_engine.py +904 -0
- benchmarks/ci_benchmark.py +785 -0
- benchmarks/comparison.py +729 -0
- benchmarks/metrics.py +553 -0
- benchmarks/run_benchmarks.py +809 -0
- ci_cd/__init__.py +2 -0
- core/__init__.py +17 -0
- core/async_pool.py +282 -0
- core/asyncio_fix.py +222 -0
- core/cache.py +472 -0
- core/container.py +277 -0
- core/database.py +114 -0
- core/input_validator.py +353 -0
- core/models.py +288 -0
- core/orchestrator.py +611 -0
- core/plugin_manager.py +571 -0
- core/rate_limiter.py +405 -0
- core/secure_config.py +328 -0
- core/shield_integration.py +296 -0
- modules/__init__.py +46 -0
- modules/cve_database.py +362 -0
- modules/exploit_assist.py +330 -0
- modules/nuclei_integration.py +480 -0
- modules/osint.py +604 -0
- modules/protonvpn.py +554 -0
- modules/recon.py +165 -0
- modules/sql_injection_db.py +826 -0
- modules/tool_orchestrator.py +498 -0
- modules/vuln_scanner.py +292 -0
- modules/wordlist_generator.py +566 -0
- risk_engine/__init__.py +99 -0
- risk_engine/business_impact.py +267 -0
- risk_engine/business_impact_calculator.py +563 -0
- risk_engine/cvss.py +156 -0
- risk_engine/epss.py +190 -0
- risk_engine/example_usage.py +294 -0
- risk_engine/false_positive_engine.py +1073 -0
- risk_engine/scorer.py +304 -0
- web_ui/backend/main.py +471 -0
- zen_ai_pentest-2.0.0.dist-info/METADATA +795 -0
- zen_ai_pentest-2.0.0.dist-info/RECORD +75 -0
- zen_ai_pentest-2.0.0.dist-info/WHEEL +5 -0
- zen_ai_pentest-2.0.0.dist-info/entry_points.txt +2 -0
- zen_ai_pentest-2.0.0.dist-info/licenses/LICENSE +21 -0
- zen_ai_pentest-2.0.0.dist-info/top_level.txt +10 -0
|
@@ -0,0 +1,1537 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Exploit Validator System for Zen AI Pentest Framework
|
|
4
|
+
|
|
5
|
+
Automatisierte Validierung und Beweisführung von Exploits in isolierten Umgebungen.
|
|
6
|
+
|
|
7
|
+
Features:
|
|
8
|
+
- Docker-Container-basierte Sandboxed-Ausführung
|
|
9
|
+
- Proof-of-Exploit Generierung (Screenshots, HTTP-Capture, PCAP)
|
|
10
|
+
- Evidence Collection mit Chain-of-Custody
|
|
11
|
+
- Unterstützung für verschiedene Exploit-Typen
|
|
12
|
+
- Safety Features (Read-Only, Scope-Validierung, Kill-Switch)
|
|
13
|
+
|
|
14
|
+
FOR AUTHORIZED SECURITY TESTING ONLY
|
|
15
|
+
|
|
16
|
+
Author: SHAdd0WTAka
|
|
17
|
+
Version: 1.0.0
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
import asyncio
|
|
21
|
+
import hashlib
|
|
22
|
+
import json
|
|
23
|
+
import logging
|
|
24
|
+
import os
|
|
25
|
+
import shlex
|
|
26
|
+
import tempfile
|
|
27
|
+
import time
|
|
28
|
+
import uuid
|
|
29
|
+
|
|
30
|
+
from dataclasses import dataclass, field, asdict
|
|
31
|
+
from datetime import datetime
|
|
32
|
+
from enum import Enum, auto
|
|
33
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
34
|
+
from urllib.parse import urlparse
|
|
35
|
+
import warnings
|
|
36
|
+
|
|
37
|
+
# Optional imports - handle gracefully if not available
|
|
38
|
+
try:
|
|
39
|
+
import docker
|
|
40
|
+
from docker.errors import DockerException, ContainerError, ImageNotFound
|
|
41
|
+
DOCKER_AVAILABLE = True
|
|
42
|
+
except ImportError:
|
|
43
|
+
DOCKER_AVAILABLE = False
|
|
44
|
+
warnings.warn("Docker SDK not available. Container isolation disabled.")
|
|
45
|
+
|
|
46
|
+
try:
|
|
47
|
+
import aiohttp
|
|
48
|
+
AIOHTTP_AVAILABLE = True
|
|
49
|
+
except ImportError:
|
|
50
|
+
AIOHTTP_AVAILABLE = False
|
|
51
|
+
|
|
52
|
+
try:
|
|
53
|
+
from playwright.async_api import async_playwright
|
|
54
|
+
PLAYWRIGHT_AVAILABLE = True
|
|
55
|
+
except ImportError:
|
|
56
|
+
PLAYWRIGHT_AVAILABLE = False
|
|
57
|
+
warnings.warn("Playwright not available. Screenshot capture disabled.")
|
|
58
|
+
|
|
59
|
+
# Configure logging
|
|
60
|
+
logger = logging.getLogger("ZenAI.ExploitValidator")
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class ExploitType(Enum):
|
|
64
|
+
"""Enumeration of supported exploit types."""
|
|
65
|
+
WEB_SQLI = "web_sqli"
|
|
66
|
+
WEB_XSS = "web_xss"
|
|
67
|
+
WEB_LFI = "web_lfi"
|
|
68
|
+
WEB_RFI = "web_rfi"
|
|
69
|
+
WEB_RCE = "web_rce"
|
|
70
|
+
WEB_CMD_INJECTION = "web_cmd_injection"
|
|
71
|
+
WEB_CSRF = "web_csrf"
|
|
72
|
+
WEB_SSRF = "web_ssrf"
|
|
73
|
+
WEB_XXE = "web_xxe"
|
|
74
|
+
WEB_PATH_TRAVERSAL = "web_path_traversal"
|
|
75
|
+
SERVICE = "service"
|
|
76
|
+
PRIVESC = "privilege_escalation"
|
|
77
|
+
POST_EXPLOITATION = "post_exploitation"
|
|
78
|
+
LOCAL = "local"
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class ExploitStatus(Enum):
|
|
82
|
+
"""Status of exploit execution."""
|
|
83
|
+
PENDING = auto()
|
|
84
|
+
RUNNING = auto()
|
|
85
|
+
SUCCESS = auto()
|
|
86
|
+
FAILED = auto()
|
|
87
|
+
TIMEOUT = auto()
|
|
88
|
+
BLOCKED = auto()
|
|
89
|
+
ERROR = auto()
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class SafetyLevel(Enum):
|
|
93
|
+
"""Safety levels for exploit execution."""
|
|
94
|
+
READ_ONLY = 1 # Passive validation only, no modifications
|
|
95
|
+
VALIDATE_ONLY = 2 # Validate exploit without full execution
|
|
96
|
+
CONTROLLED = 3 # Controlled execution with limits
|
|
97
|
+
FULL = 4 # Full exploitation (requires explicit approval)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
@dataclass
|
|
101
|
+
class ScopeConfig:
|
|
102
|
+
"""Configuration for scope validation."""
|
|
103
|
+
allowed_hosts: List[str] = field(default_factory=list)
|
|
104
|
+
allowed_ports: List[int] = field(default_factory=lambda: [80, 443, 8080])
|
|
105
|
+
blocked_hosts: List[str] = field(default_factory=lambda: [
|
|
106
|
+
"localhost", "127.0.0.1", "::1", "0.0.0.0",
|
|
107
|
+
"169.254.169.254" # AWS metadata
|
|
108
|
+
])
|
|
109
|
+
blocked_ports: List[int] = field(default_factory=lambda: [
|
|
110
|
+
22, 23, 25, 53, 110, 143, 587, 993, 995 # Email/DNS services
|
|
111
|
+
])
|
|
112
|
+
require_https: bool = False
|
|
113
|
+
max_redirects: int = 5
|
|
114
|
+
allowed_schemes: List[str] = field(default_factory=lambda: ["http", "https"])
|
|
115
|
+
|
|
116
|
+
def validate_target(self, target: str) -> Tuple[bool, Optional[str]]:
|
|
117
|
+
"""
|
|
118
|
+
Validate if target is within scope.
|
|
119
|
+
|
|
120
|
+
Args:
|
|
121
|
+
target: Target URL or host
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
Tuple of (is_valid, error_message)
|
|
125
|
+
"""
|
|
126
|
+
try:
|
|
127
|
+
# Parse URL if it's a full URL
|
|
128
|
+
if "//" in target:
|
|
129
|
+
parsed = urlparse(target)
|
|
130
|
+
host = parsed.hostname
|
|
131
|
+
port = parsed.port
|
|
132
|
+
scheme = parsed.scheme
|
|
133
|
+
|
|
134
|
+
if not host:
|
|
135
|
+
return False, "Could not parse host from target"
|
|
136
|
+
|
|
137
|
+
# Check scheme
|
|
138
|
+
if scheme not in self.allowed_schemes:
|
|
139
|
+
return False, f"Scheme '{scheme}' not allowed"
|
|
140
|
+
else:
|
|
141
|
+
host = target.split(":")[0]
|
|
142
|
+
port = int(target.split(":")[1]) if ":" in target else None
|
|
143
|
+
|
|
144
|
+
# Check blocked hosts
|
|
145
|
+
if host in self.blocked_hosts:
|
|
146
|
+
return False, f"Host '{host}' is in blocklist"
|
|
147
|
+
|
|
148
|
+
# Check IP ranges
|
|
149
|
+
try:
|
|
150
|
+
import ipaddress
|
|
151
|
+
ip = ipaddress.ip_address(host)
|
|
152
|
+
# Block private IPs unless explicitly allowed
|
|
153
|
+
if ip.is_private and host not in self.allowed_hosts:
|
|
154
|
+
return False, f"Private IP '{host}' not in allowed list"
|
|
155
|
+
if ip.is_loopback:
|
|
156
|
+
return False, f"Loopback address '{host}' not allowed"
|
|
157
|
+
if ip.is_multicast:
|
|
158
|
+
return False, f"Multicast address '{host}' not allowed"
|
|
159
|
+
except ValueError:
|
|
160
|
+
# Not an IP, continue with hostname check
|
|
161
|
+
pass
|
|
162
|
+
|
|
163
|
+
# Check blocked ports
|
|
164
|
+
if port and port in self.blocked_ports:
|
|
165
|
+
return False, f"Port {port} is blocked"
|
|
166
|
+
|
|
167
|
+
# Check allowed hosts (if specified)
|
|
168
|
+
if self.allowed_hosts and host not in self.allowed_hosts:
|
|
169
|
+
# Allow wildcards
|
|
170
|
+
allowed = any(
|
|
171
|
+
host.endswith(allowed.replace("*.", ".")) or host == allowed
|
|
172
|
+
for allowed in self.allowed_hosts
|
|
173
|
+
)
|
|
174
|
+
if not allowed:
|
|
175
|
+
return False, f"Host '{host}' not in allowed scope"
|
|
176
|
+
|
|
177
|
+
return True, None
|
|
178
|
+
|
|
179
|
+
except Exception as e:
|
|
180
|
+
return False, f"Validation error: {str(e)}"
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
@dataclass
|
|
184
|
+
class Evidence:
|
|
185
|
+
"""Collection of evidence from exploit execution."""
|
|
186
|
+
screenshots: List[str] = field(default_factory=list)
|
|
187
|
+
http_responses: List[Dict[str, Any]] = field(default_factory=list)
|
|
188
|
+
pcap_file: Optional[str] = None
|
|
189
|
+
shell_session: Optional[str] = None
|
|
190
|
+
video_recording: Optional[str] = None
|
|
191
|
+
logs: List[str] = field(default_factory=list)
|
|
192
|
+
files: Dict[str, str] = field(default_factory=dict)
|
|
193
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
194
|
+
timestamps: Dict[str, datetime] = field(default_factory=dict)
|
|
195
|
+
hashes: Dict[str, str] = field(default_factory=dict)
|
|
196
|
+
|
|
197
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
198
|
+
"""Convert to dictionary with serializable values."""
|
|
199
|
+
result = asdict(self)
|
|
200
|
+
# Convert timestamps to ISO format
|
|
201
|
+
result['timestamps'] = {
|
|
202
|
+
k: v.isoformat() if isinstance(v, datetime) else v
|
|
203
|
+
for k, v in self.timestamps.items()
|
|
204
|
+
}
|
|
205
|
+
return result
|
|
206
|
+
|
|
207
|
+
def compute_hashes(self) -> None:
|
|
208
|
+
"""Compute SHA256 hashes for all evidence files."""
|
|
209
|
+
for attr in ['screenshots', 'pcap_file', 'video_recording']:
|
|
210
|
+
value = getattr(self, attr)
|
|
211
|
+
if value:
|
|
212
|
+
if isinstance(value, list):
|
|
213
|
+
for f in value:
|
|
214
|
+
self._hash_file(f)
|
|
215
|
+
else:
|
|
216
|
+
self._hash_file(value)
|
|
217
|
+
|
|
218
|
+
def _hash_file(self, filepath: str) -> None:
|
|
219
|
+
"""Compute hash for a single file."""
|
|
220
|
+
try:
|
|
221
|
+
if os.path.exists(filepath):
|
|
222
|
+
sha256 = hashlib.sha256()
|
|
223
|
+
with open(filepath, 'rb') as f:
|
|
224
|
+
for chunk in iter(lambda: f.read(8192), b''):
|
|
225
|
+
sha256.update(chunk)
|
|
226
|
+
self.hashes[filepath] = sha256.hexdigest()
|
|
227
|
+
except Exception as e:
|
|
228
|
+
logger.warning(f"Failed to hash {filepath}: {e}")
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
@dataclass
|
|
232
|
+
class ExploitResult:
|
|
233
|
+
"""Result of exploit validation."""
|
|
234
|
+
success: bool
|
|
235
|
+
exploit_type: ExploitType
|
|
236
|
+
target: str
|
|
237
|
+
timestamp: datetime
|
|
238
|
+
evidence: Evidence
|
|
239
|
+
output: str
|
|
240
|
+
status: ExploitStatus = ExploitStatus.PENDING
|
|
241
|
+
error: Optional[str] = None
|
|
242
|
+
remediation: Optional[str] = None
|
|
243
|
+
cvss_score: Optional[float] = None
|
|
244
|
+
severity: Optional[str] = None
|
|
245
|
+
execution_time: float = 0.0
|
|
246
|
+
validator_id: str = field(default_factory=lambda: str(uuid.uuid4()))
|
|
247
|
+
chain_of_custody: List[Dict[str, Any]] = field(default_factory=list)
|
|
248
|
+
|
|
249
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
250
|
+
"""Convert to dictionary."""
|
|
251
|
+
return {
|
|
252
|
+
'success': self.success,
|
|
253
|
+
'exploit_type': self.exploit_type.value,
|
|
254
|
+
'target': self.target,
|
|
255
|
+
'timestamp': self.timestamp.isoformat(),
|
|
256
|
+
'evidence': self.evidence.to_dict(),
|
|
257
|
+
'output': self.output,
|
|
258
|
+
'status': self.status.name,
|
|
259
|
+
'error': self.error,
|
|
260
|
+
'remediation': self.remediation,
|
|
261
|
+
'cvss_score': self.cvss_score,
|
|
262
|
+
'severity': self.severity,
|
|
263
|
+
'execution_time': self.execution_time,
|
|
264
|
+
'validator_id': self.validator_id,
|
|
265
|
+
'chain_of_custody': self.chain_of_custody
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
def to_json(self) -> str:
|
|
269
|
+
"""Convert to JSON string."""
|
|
270
|
+
return json.dumps(self.to_dict(), indent=2, default=str)
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
@dataclass
|
|
274
|
+
class SandboxConfig:
|
|
275
|
+
"""Configuration for sandboxed execution."""
|
|
276
|
+
use_docker: bool = True
|
|
277
|
+
use_gvisor: bool = False # Experimental gVisor support
|
|
278
|
+
network_isolated: bool = True
|
|
279
|
+
cpu_limit: float = 1.0 # CPU cores
|
|
280
|
+
memory_limit: str = "512m"
|
|
281
|
+
timeout: int = 60
|
|
282
|
+
read_only_root: bool = True
|
|
283
|
+
tmpfs_size: str = "100m"
|
|
284
|
+
cap_drop: List[str] = field(default_factory=lambda: [
|
|
285
|
+
"ALL"
|
|
286
|
+
])
|
|
287
|
+
cap_add: List[str] = field(default_factory=lambda: [
|
|
288
|
+
"NET_BIND_SERVICE"
|
|
289
|
+
])
|
|
290
|
+
dns_servers: List[str] = field(default_factory=lambda: [
|
|
291
|
+
"8.8.8.8", "1.1.1.1"
|
|
292
|
+
])
|
|
293
|
+
image: str = "zenai/exploit-sandbox:latest"
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
class ExploitValidator:
|
|
297
|
+
"""
|
|
298
|
+
Main class for validating exploits in sandboxed environments.
|
|
299
|
+
|
|
300
|
+
This validator provides comprehensive exploit testing capabilities
|
|
301
|
+
with strong isolation, evidence collection, and safety controls.
|
|
302
|
+
|
|
303
|
+
Usage:
|
|
304
|
+
validator = ExploitValidator(
|
|
305
|
+
safety_level=SafetyLevel.CONTROLLED,
|
|
306
|
+
scope_config=ScopeConfig(allowed_hosts=["example.com"])
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
result = await validator.validate(
|
|
310
|
+
exploit_code="...",
|
|
311
|
+
target="https://example.com/vuln",
|
|
312
|
+
exploit_type=ExploitType.WEB_SQLI
|
|
313
|
+
)
|
|
314
|
+
"""
|
|
315
|
+
|
|
316
|
+
def __init__(
|
|
317
|
+
self,
|
|
318
|
+
safety_level: SafetyLevel = SafetyLevel.CONTROLLED,
|
|
319
|
+
scope_config: Optional[ScopeConfig] = None,
|
|
320
|
+
sandbox_config: Optional[SandboxConfig] = None,
|
|
321
|
+
output_dir: Optional[str] = None,
|
|
322
|
+
enable_playwright: bool = True
|
|
323
|
+
):
|
|
324
|
+
"""
|
|
325
|
+
Initialize the ExploitValidator.
|
|
326
|
+
|
|
327
|
+
Args:
|
|
328
|
+
safety_level: Maximum allowed safety level
|
|
329
|
+
scope_config: Scope validation configuration
|
|
330
|
+
sandbox_config: Sandbox execution configuration
|
|
331
|
+
output_dir: Directory for evidence storage
|
|
332
|
+
enable_playwright: Enable Playwright for screenshot capture
|
|
333
|
+
"""
|
|
334
|
+
self.safety_level = safety_level
|
|
335
|
+
self.scope_config = scope_config or ScopeConfig()
|
|
336
|
+
self.sandbox_config = sandbox_config or SandboxConfig()
|
|
337
|
+
self.output_dir = output_dir or tempfile.gettempdir()
|
|
338
|
+
self.enable_playwright = enable_playwright and PLAYWRIGHT_AVAILABLE
|
|
339
|
+
|
|
340
|
+
# Initialize Docker client if available
|
|
341
|
+
self.docker_client = None
|
|
342
|
+
if DOCKER_AVAILABLE and self.sandbox_config.use_docker:
|
|
343
|
+
try:
|
|
344
|
+
self.docker_client = docker.from_env()
|
|
345
|
+
logger.info("Docker client initialized")
|
|
346
|
+
except DockerException as e:
|
|
347
|
+
logger.warning(f"Docker not available: {e}")
|
|
348
|
+
|
|
349
|
+
# Rate limiting
|
|
350
|
+
self._rate_limiter = asyncio.Semaphore(5) # Max 5 concurrent exploits
|
|
351
|
+
self._last_execution: Dict[str, float] = {}
|
|
352
|
+
self._min_interval = 1.0 # Minimum seconds between executions to same target
|
|
353
|
+
|
|
354
|
+
# Kill switch
|
|
355
|
+
self._killed = False
|
|
356
|
+
|
|
357
|
+
# Evidence storage
|
|
358
|
+
self._evidence_counter = 0
|
|
359
|
+
|
|
360
|
+
logger.info(f"ExploitValidator initialized (safety={safety_level.name})")
|
|
361
|
+
|
|
362
|
+
async def validate(
|
|
363
|
+
self,
|
|
364
|
+
exploit_code: str,
|
|
365
|
+
target: str,
|
|
366
|
+
exploit_type: ExploitType,
|
|
367
|
+
parameters: Optional[Dict[str, Any]] = None,
|
|
368
|
+
timeout: Optional[int] = None
|
|
369
|
+
) -> ExploitResult:
|
|
370
|
+
"""
|
|
371
|
+
Validate an exploit against a target.
|
|
372
|
+
|
|
373
|
+
Args:
|
|
374
|
+
exploit_code: The exploit code/payload to execute
|
|
375
|
+
target: Target URL, host, or service
|
|
376
|
+
exploit_type: Type of exploit being tested
|
|
377
|
+
parameters: Additional exploit parameters
|
|
378
|
+
timeout: Execution timeout override
|
|
379
|
+
|
|
380
|
+
Returns:
|
|
381
|
+
ExploitResult with execution details and evidence
|
|
382
|
+
"""
|
|
383
|
+
start_time = time.time()
|
|
384
|
+
timestamp = datetime.utcnow()
|
|
385
|
+
validator_id = str(uuid.uuid4())
|
|
386
|
+
|
|
387
|
+
# Initialize chain of custody
|
|
388
|
+
chain_of_custody = [
|
|
389
|
+
{
|
|
390
|
+
'action': 'validation_started',
|
|
391
|
+
'timestamp': timestamp.isoformat(),
|
|
392
|
+
'validator_id': validator_id,
|
|
393
|
+
'hash': hashlib.sha256(exploit_code.encode()).hexdigest()[:16]
|
|
394
|
+
}
|
|
395
|
+
]
|
|
396
|
+
|
|
397
|
+
# Check kill switch
|
|
398
|
+
if self._killed:
|
|
399
|
+
return ExploitResult(
|
|
400
|
+
success=False,
|
|
401
|
+
exploit_type=exploit_type,
|
|
402
|
+
target=target,
|
|
403
|
+
timestamp=timestamp,
|
|
404
|
+
evidence=Evidence(),
|
|
405
|
+
output="",
|
|
406
|
+
status=ExploitStatus.BLOCKED,
|
|
407
|
+
error="Kill switch activated",
|
|
408
|
+
chain_of_custody=chain_of_custody
|
|
409
|
+
)
|
|
410
|
+
|
|
411
|
+
# Validate scope
|
|
412
|
+
is_valid, error_msg = self.scope_config.validate_target(target)
|
|
413
|
+
if not is_valid:
|
|
414
|
+
return ExploitResult(
|
|
415
|
+
success=False,
|
|
416
|
+
exploit_type=exploit_type,
|
|
417
|
+
target=target,
|
|
418
|
+
timestamp=timestamp,
|
|
419
|
+
evidence=Evidence(),
|
|
420
|
+
output="",
|
|
421
|
+
status=ExploitStatus.BLOCKED,
|
|
422
|
+
error=f"Scope validation failed: {error_msg}",
|
|
423
|
+
chain_of_custody=chain_of_custody
|
|
424
|
+
)
|
|
425
|
+
|
|
426
|
+
chain_of_custody.append({
|
|
427
|
+
'action': 'scope_validated',
|
|
428
|
+
'timestamp': datetime.utcnow().isoformat()
|
|
429
|
+
})
|
|
430
|
+
|
|
431
|
+
# Rate limiting check
|
|
432
|
+
await self._check_rate_limit(target)
|
|
433
|
+
|
|
434
|
+
# Check safety level compatibility
|
|
435
|
+
if not self._is_safety_compatible(exploit_type):
|
|
436
|
+
return ExploitResult(
|
|
437
|
+
success=False,
|
|
438
|
+
exploit_type=exploit_type,
|
|
439
|
+
target=target,
|
|
440
|
+
timestamp=timestamp,
|
|
441
|
+
evidence=Evidence(),
|
|
442
|
+
output="",
|
|
443
|
+
status=ExploitStatus.BLOCKED,
|
|
444
|
+
error=f"Exploit type {exploit_type.value} exceeds safety level {self.safety_level.name}",
|
|
445
|
+
chain_of_custody=chain_of_custody
|
|
446
|
+
)
|
|
447
|
+
|
|
448
|
+
# Execute with semaphore for rate limiting
|
|
449
|
+
async with self._rate_limiter:
|
|
450
|
+
try:
|
|
451
|
+
if self.sandbox_config.use_docker and self.docker_client:
|
|
452
|
+
result = await self._execute_docker_sandboxed(
|
|
453
|
+
exploit_code, target, exploit_type,
|
|
454
|
+
parameters or {}, timeout or self.sandbox_config.timeout
|
|
455
|
+
)
|
|
456
|
+
else:
|
|
457
|
+
result = await self._execute_local_sandboxed(
|
|
458
|
+
exploit_code, target, exploit_type,
|
|
459
|
+
parameters or {}, timeout or self.sandbox_config.timeout
|
|
460
|
+
)
|
|
461
|
+
|
|
462
|
+
result.chain_of_custody = chain_of_custody + result.chain_of_custody
|
|
463
|
+
result.execution_time = time.time() - start_time
|
|
464
|
+
return result
|
|
465
|
+
|
|
466
|
+
except asyncio.TimeoutError:
|
|
467
|
+
execution_time = time.time() - start_time
|
|
468
|
+
return ExploitResult(
|
|
469
|
+
success=False,
|
|
470
|
+
exploit_type=exploit_type,
|
|
471
|
+
target=target,
|
|
472
|
+
timestamp=timestamp,
|
|
473
|
+
evidence=Evidence(),
|
|
474
|
+
output="",
|
|
475
|
+
status=ExploitStatus.TIMEOUT,
|
|
476
|
+
error=f"Execution timeout after {timeout or self.sandbox_config.timeout}s",
|
|
477
|
+
execution_time=execution_time,
|
|
478
|
+
validator_id=validator_id,
|
|
479
|
+
chain_of_custody=chain_of_custody
|
|
480
|
+
)
|
|
481
|
+
except Exception as e:
|
|
482
|
+
execution_time = time.time() - start_time
|
|
483
|
+
logger.exception("Exploit validation failed")
|
|
484
|
+
return ExploitResult(
|
|
485
|
+
success=False,
|
|
486
|
+
exploit_type=exploit_type,
|
|
487
|
+
target=target,
|
|
488
|
+
timestamp=timestamp,
|
|
489
|
+
evidence=Evidence(),
|
|
490
|
+
output="",
|
|
491
|
+
status=ExploitStatus.ERROR,
|
|
492
|
+
error=f"Execution error: {str(e)}",
|
|
493
|
+
execution_time=execution_time,
|
|
494
|
+
validator_id=validator_id,
|
|
495
|
+
chain_of_custody=chain_of_custody
|
|
496
|
+
)
|
|
497
|
+
|
|
498
|
+
async def execute_sandboxed(
|
|
499
|
+
self,
|
|
500
|
+
command: str,
|
|
501
|
+
timeout: int = 60,
|
|
502
|
+
network_access: bool = False
|
|
503
|
+
) -> Dict[str, Any]:
|
|
504
|
+
"""
|
|
505
|
+
Execute a command in a sandboxed environment.
|
|
506
|
+
|
|
507
|
+
Args:
|
|
508
|
+
command: Command to execute
|
|
509
|
+
timeout: Execution timeout
|
|
510
|
+
network_access: Whether to allow network access
|
|
511
|
+
|
|
512
|
+
Returns:
|
|
513
|
+
Dictionary with stdout, stderr, return_code, and execution details
|
|
514
|
+
"""
|
|
515
|
+
if self.sandbox_config.use_docker and self.docker_client:
|
|
516
|
+
return await self._execute_docker_command(command, timeout, network_access)
|
|
517
|
+
else:
|
|
518
|
+
return await self._execute_local_command(command, timeout)
|
|
519
|
+
|
|
520
|
+
async def capture_evidence(
|
|
521
|
+
self,
|
|
522
|
+
target: str,
|
|
523
|
+
exploit_type: ExploitType,
|
|
524
|
+
capture_screenshot: bool = True,
|
|
525
|
+
capture_http: bool = True,
|
|
526
|
+
capture_pcap: bool = False
|
|
527
|
+
) -> Evidence:
|
|
528
|
+
"""
|
|
529
|
+
Capture evidence from target interaction.
|
|
530
|
+
|
|
531
|
+
Args:
|
|
532
|
+
target: Target URL
|
|
533
|
+
exploit_type: Type of exploit
|
|
534
|
+
capture_screenshot: Capture browser screenshot
|
|
535
|
+
capture_http: Capture HTTP responses
|
|
536
|
+
capture_pcap: Capture network traffic (requires permissions)
|
|
537
|
+
|
|
538
|
+
Returns:
|
|
539
|
+
Evidence object with collected artifacts
|
|
540
|
+
"""
|
|
541
|
+
evidence = Evidence()
|
|
542
|
+
evidence.timestamps['started'] = datetime.utcnow()
|
|
543
|
+
|
|
544
|
+
# Create evidence directory
|
|
545
|
+
evidence_dir = os.path.join(
|
|
546
|
+
self.output_dir,
|
|
547
|
+
f"evidence_{int(time.time())}_{self._evidence_counter}"
|
|
548
|
+
)
|
|
549
|
+
self._evidence_counter += 1
|
|
550
|
+
os.makedirs(evidence_dir, exist_ok=True)
|
|
551
|
+
|
|
552
|
+
try:
|
|
553
|
+
# Capture HTTP responses
|
|
554
|
+
if capture_http and AIOHTTP_AVAILABLE:
|
|
555
|
+
http_evidence = await self._capture_http_evidence(target, evidence_dir)
|
|
556
|
+
evidence.http_responses = http_evidence
|
|
557
|
+
|
|
558
|
+
# Capture screenshot for web exploits
|
|
559
|
+
if capture_screenshot and self.enable_playwright and self._is_web_exploit(exploit_type):
|
|
560
|
+
screenshot_path = await self._capture_screenshot(target, evidence_dir)
|
|
561
|
+
if screenshot_path:
|
|
562
|
+
evidence.screenshots.append(screenshot_path)
|
|
563
|
+
|
|
564
|
+
# Capture PCAP (if enabled and running as root)
|
|
565
|
+
if capture_pcap:
|
|
566
|
+
pcap_path = await self._capture_pcap(target, evidence_dir)
|
|
567
|
+
if pcap_path:
|
|
568
|
+
evidence.pcap_file = pcap_path
|
|
569
|
+
|
|
570
|
+
# Collect metadata
|
|
571
|
+
evidence.metadata = {
|
|
572
|
+
'target': target,
|
|
573
|
+
'exploit_type': exploit_type.value,
|
|
574
|
+
'evidence_dir': evidence_dir,
|
|
575
|
+
'user_agent': 'ZenAI-ExploitValidator/1.0',
|
|
576
|
+
'source_ip': await self._get_source_ip()
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
evidence.timestamps['completed'] = datetime.utcnow()
|
|
580
|
+
evidence.compute_hashes()
|
|
581
|
+
|
|
582
|
+
except Exception as e:
|
|
583
|
+
logger.error(f"Evidence capture failed: {e}")
|
|
584
|
+
evidence.metadata['error'] = str(e)
|
|
585
|
+
|
|
586
|
+
return evidence
|
|
587
|
+
|
|
588
|
+
def generate_remediation(self, vulnerability: Dict[str, Any]) -> str:
|
|
589
|
+
"""
|
|
590
|
+
Generate remediation advice for a vulnerability.
|
|
591
|
+
|
|
592
|
+
Args:
|
|
593
|
+
vulnerability: Vulnerability details dictionary
|
|
594
|
+
|
|
595
|
+
Returns:
|
|
596
|
+
Remediation advice as markdown text
|
|
597
|
+
"""
|
|
598
|
+
vuln_type = vulnerability.get('type', 'unknown')
|
|
599
|
+
severity = vulnerability.get('severity', 'medium')
|
|
600
|
+
|
|
601
|
+
remediation_templates = {
|
|
602
|
+
ExploitType.WEB_SQLI.value: """
|
|
603
|
+
## SQL Injection Remediation
|
|
604
|
+
|
|
605
|
+
### Immediate Actions
|
|
606
|
+
1. **Use Parameterized Queries**: Replace all dynamic SQL with prepared statements
|
|
607
|
+
2. **Input Validation**: Validate and sanitize all user inputs
|
|
608
|
+
3. **Least Privilege**: Ensure database user has minimal permissions
|
|
609
|
+
|
|
610
|
+
### Code Example (Python/Flask)
|
|
611
|
+
```python
|
|
612
|
+
# VULNERABLE
|
|
613
|
+
cursor.execute(f"SELECT * FROM users WHERE id = '{user_id}'")
|
|
614
|
+
|
|
615
|
+
# SECURE
|
|
616
|
+
cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))
|
|
617
|
+
```
|
|
618
|
+
|
|
619
|
+
### Long-term Recommendations
|
|
620
|
+
- Implement Web Application Firewall (WAF)
|
|
621
|
+
- Enable SQL query logging and monitoring
|
|
622
|
+
- Regular security code reviews
|
|
623
|
+
- Use ORM frameworks that prevent SQL injection
|
|
624
|
+
""",
|
|
625
|
+
ExploitType.WEB_XSS.value: """
|
|
626
|
+
## Cross-Site Scripting (XSS) Remediation
|
|
627
|
+
|
|
628
|
+
### Immediate Actions
|
|
629
|
+
1. **Output Encoding**: Encode all output based on context (HTML, JS, URL, CSS)
|
|
630
|
+
2. **Content Security Policy**: Implement strict CSP headers
|
|
631
|
+
3. **Input Validation**: Validate input against expected patterns
|
|
632
|
+
|
|
633
|
+
### Code Example
|
|
634
|
+
```python
|
|
635
|
+
# VULNERABLE
|
|
636
|
+
<div>{{ user_input }}</div>
|
|
637
|
+
|
|
638
|
+
# SECURE (with Jinja2 autoescape)
|
|
639
|
+
<div>{{ user_input | e }}</div>
|
|
640
|
+
```
|
|
641
|
+
|
|
642
|
+
### Headers to Implement
|
|
643
|
+
```
|
|
644
|
+
Content-Security-Policy: default-src 'self'
|
|
645
|
+
X-XSS-Protection: 1; mode=block
|
|
646
|
+
X-Content-Type-Options: nosniff
|
|
647
|
+
```
|
|
648
|
+
""",
|
|
649
|
+
ExploitType.WEB_RCE.value: """
|
|
650
|
+
## Remote Code Execution Remediation
|
|
651
|
+
|
|
652
|
+
### Immediate Actions
|
|
653
|
+
1. **Disable Dangerous Functions**: Disable eval(), exec(), system() in PHP/Python
|
|
654
|
+
2. **Input Sanitization**: Never pass user input to system commands
|
|
655
|
+
3. **Sandboxing**: Run application in restricted environment
|
|
656
|
+
|
|
657
|
+
### Code Example
|
|
658
|
+
```python
|
|
659
|
+
# VULNERABLE
|
|
660
|
+
os.system(f"ping {user_input}")
|
|
661
|
+
|
|
662
|
+
# SECURE
|
|
663
|
+
allowed_hosts = ['8.8.8.8', '1.1.1.1']
|
|
664
|
+
if user_input in allowed_hosts:
|
|
665
|
+
subprocess.run(['ping', '-c', '4', user_input], check=True)
|
|
666
|
+
```
|
|
667
|
+
|
|
668
|
+
### Critical Recommendations
|
|
669
|
+
- Remove all command execution functionality if not essential
|
|
670
|
+
- Use allowlists for allowed commands/arguments
|
|
671
|
+
- Implement strong network segmentation
|
|
672
|
+
""",
|
|
673
|
+
ExploitType.WEB_LFI.value: """
|
|
674
|
+
## Local File Inclusion Remediation
|
|
675
|
+
|
|
676
|
+
### Immediate Actions
|
|
677
|
+
1. **Path Validation**: Validate and sanitize file paths
|
|
678
|
+
2. **Allowlist**: Use allowlist of allowed files
|
|
679
|
+
3. **Disable Remote Includes**: Set allow_url_include=Off in PHP
|
|
680
|
+
|
|
681
|
+
### Code Example
|
|
682
|
+
```php
|
|
683
|
+
// VULNERABLE
|
|
684
|
+
include($_GET['page']);
|
|
685
|
+
|
|
686
|
+
// SECURE
|
|
687
|
+
$allowed = ['home', 'about', 'contact'];
|
|
688
|
+
$page = $_GET['page'];
|
|
689
|
+
if (in_array($page, $allowed)) {
|
|
690
|
+
include("pages/" . $page . ".php");
|
|
691
|
+
}
|
|
692
|
+
```
|
|
693
|
+
""",
|
|
694
|
+
'default': f"""
|
|
695
|
+
## Vulnerability Remediation
|
|
696
|
+
|
|
697
|
+
**Severity**: {severity}
|
|
698
|
+
**Type**: {vuln_type}
|
|
699
|
+
|
|
700
|
+
### General Recommendations
|
|
701
|
+
1. **Update Software**: Apply latest security patches
|
|
702
|
+
2. **Input Validation**: Validate all user inputs
|
|
703
|
+
3. **Principle of Least Privilege**: Minimize permissions
|
|
704
|
+
4. **Security Headers**: Implement security headers
|
|
705
|
+
5. **Monitoring**: Enable comprehensive logging
|
|
706
|
+
|
|
707
|
+
### Verification
|
|
708
|
+
After applying fixes:
|
|
709
|
+
1. Re-run the vulnerability scan
|
|
710
|
+
2. Test functionality to ensure no breakage
|
|
711
|
+
3. Monitor for any suspicious activity
|
|
712
|
+
"""
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
return remediation_templates.get(vuln_type, remediation_templates['default'])
|
|
716
|
+
|
|
717
|
+
async def safe_execute(self, exploit: Dict[str, Any]) -> bool:
|
|
718
|
+
"""
|
|
719
|
+
Safely execute an exploit with comprehensive checks.
|
|
720
|
+
|
|
721
|
+
Args:
|
|
722
|
+
exploit: Exploit dictionary with code, target, type
|
|
723
|
+
|
|
724
|
+
Returns:
|
|
725
|
+
True if exploit execution was successful
|
|
726
|
+
"""
|
|
727
|
+
try:
|
|
728
|
+
# Pre-execution checks
|
|
729
|
+
if not self._validate_exploit_structure(exploit):
|
|
730
|
+
logger.error("Invalid exploit structure")
|
|
731
|
+
return False
|
|
732
|
+
|
|
733
|
+
# Execute validation
|
|
734
|
+
result = await self.validate(
|
|
735
|
+
exploit_code=exploit['code'],
|
|
736
|
+
target=exploit['target'],
|
|
737
|
+
exploit_type=ExploitType(exploit.get('type', 'web_rce')),
|
|
738
|
+
parameters=exploit.get('parameters', {})
|
|
739
|
+
)
|
|
740
|
+
|
|
741
|
+
return result.success
|
|
742
|
+
|
|
743
|
+
except Exception as e:
|
|
744
|
+
logger.error(f"Safe execution failed: {e}")
|
|
745
|
+
return False
|
|
746
|
+
|
|
747
|
+
def kill_switch(self) -> None:
|
|
748
|
+
"""Activate kill switch to stop all executions."""
|
|
749
|
+
logger.warning("Kill switch activated!")
|
|
750
|
+
self._killed = True
|
|
751
|
+
|
|
752
|
+
def reset_kill_switch(self) -> None:
|
|
753
|
+
"""Reset kill switch."""
|
|
754
|
+
logger.info("Kill switch reset")
|
|
755
|
+
self._killed = False
|
|
756
|
+
|
|
757
|
+
# Private helper methods
|
|
758
|
+
|
|
759
|
+
def _is_safety_compatible(self, exploit_type: ExploitType) -> bool:
|
|
760
|
+
"""Check if exploit type is compatible with current safety level."""
|
|
761
|
+
safety_mapping = {
|
|
762
|
+
SafetyLevel.READ_ONLY: [],
|
|
763
|
+
SafetyLevel.VALIDATE_ONLY: [
|
|
764
|
+
ExploitType.WEB_XSS, ExploitType.WEB_SQLI,
|
|
765
|
+
ExploitType.WEB_LFI, ExploitType.WEB_CSRF
|
|
766
|
+
],
|
|
767
|
+
SafetyLevel.CONTROLLED: [
|
|
768
|
+
ExploitType.WEB_XSS, ExploitType.WEB_SQLI, ExploitType.WEB_LFI,
|
|
769
|
+
ExploitType.WEB_RFI, ExploitType.WEB_RCE, ExploitType.WEB_CMD_INJECTION,
|
|
770
|
+
ExploitType.WEB_SSRF, ExploitType.WEB_XXE, ExploitType.WEB_PATH_TRAVERSAL,
|
|
771
|
+
ExploitType.SERVICE
|
|
772
|
+
],
|
|
773
|
+
SafetyLevel.FULL: list(ExploitType)
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
allowed = safety_mapping.get(self.safety_level, [])
|
|
777
|
+
return exploit_type in allowed or self.safety_level == SafetyLevel.FULL
|
|
778
|
+
|
|
779
|
+
def _is_web_exploit(self, exploit_type: ExploitType) -> bool:
|
|
780
|
+
"""Check if exploit type is web-based."""
|
|
781
|
+
return exploit_type.value.startswith('web_')
|
|
782
|
+
|
|
783
|
+
async def _check_rate_limit(self, target: str) -> None:
|
|
784
|
+
"""Check and enforce rate limiting."""
|
|
785
|
+
now = time.time()
|
|
786
|
+
host = urlparse(target).hostname or target
|
|
787
|
+
|
|
788
|
+
if host in self._last_execution:
|
|
789
|
+
elapsed = now - self._last_execution[host]
|
|
790
|
+
if elapsed < self._min_interval:
|
|
791
|
+
wait_time = self._min_interval - elapsed
|
|
792
|
+
logger.debug(f"Rate limiting: waiting {wait_time:.2f}s for {host}")
|
|
793
|
+
await asyncio.sleep(wait_time)
|
|
794
|
+
|
|
795
|
+
self._last_execution[host] = now
|
|
796
|
+
|
|
797
|
+
def _validate_exploit_structure(self, exploit: Dict[str, Any]) -> bool:
|
|
798
|
+
"""Validate exploit dictionary structure."""
|
|
799
|
+
required_fields = ['code', 'target', 'type']
|
|
800
|
+
return all(field in exploit for field in required_fields)
|
|
801
|
+
|
|
802
|
+
async def _execute_docker_sandboxed(
|
|
803
|
+
self,
|
|
804
|
+
exploit_code: str,
|
|
805
|
+
target: str,
|
|
806
|
+
exploit_type: ExploitType,
|
|
807
|
+
parameters: Dict[str, Any],
|
|
808
|
+
timeout: int
|
|
809
|
+
) -> ExploitResult:
|
|
810
|
+
"""Execute exploit in Docker sandbox."""
|
|
811
|
+
timestamp = datetime.utcnow()
|
|
812
|
+
chain_of_custody = []
|
|
813
|
+
|
|
814
|
+
# Prepare exploit script
|
|
815
|
+
script_content = self._generate_exploit_script(
|
|
816
|
+
exploit_code, target, exploit_type, parameters
|
|
817
|
+
)
|
|
818
|
+
|
|
819
|
+
# Create temporary directory for volumes
|
|
820
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
821
|
+
script_path = os.path.join(tmpdir, 'exploit.py')
|
|
822
|
+
with open(script_path, 'w') as f:
|
|
823
|
+
f.write(script_content)
|
|
824
|
+
|
|
825
|
+
# Output directory for results
|
|
826
|
+
output_dir = os.path.join(tmpdir, 'output')
|
|
827
|
+
os.makedirs(output_dir, exist_ok=True)
|
|
828
|
+
|
|
829
|
+
try:
|
|
830
|
+
# Build container configuration
|
|
831
|
+
container_config = {
|
|
832
|
+
'image': self.sandbox_config.image,
|
|
833
|
+
'command': ['python3', '/exploit/exploit.py'],
|
|
834
|
+
'volumes': {
|
|
835
|
+
tmpdir: {'bind': '/exploit', 'mode': 'ro'},
|
|
836
|
+
output_dir: {'bind': '/output', 'mode': 'rw'}
|
|
837
|
+
},
|
|
838
|
+
'network_disabled': self.sandbox_config.network_isolated,
|
|
839
|
+
'mem_limit': self.sandbox_config.memory_limit,
|
|
840
|
+
'cpu_quota': int(self.sandbox_config.cpu_limit * 100000),
|
|
841
|
+
'read_only': self.sandbox_config.read_only_root,
|
|
842
|
+
'cap_drop': self.sandbox_config.cap_drop,
|
|
843
|
+
'cap_add': self.sandbox_config.cap_add,
|
|
844
|
+
'dns': self.sandbox_config.dns_servers,
|
|
845
|
+
'detach': True,
|
|
846
|
+
'auto_remove': True
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
# Run container
|
|
850
|
+
chain_of_custody.append({
|
|
851
|
+
'action': 'container_created',
|
|
852
|
+
'timestamp': datetime.utcnow().isoformat(),
|
|
853
|
+
'image': self.sandbox_config.image
|
|
854
|
+
})
|
|
855
|
+
|
|
856
|
+
container = self.docker_client.containers.run(**container_config)
|
|
857
|
+
|
|
858
|
+
# Wait for completion with timeout
|
|
859
|
+
try:
|
|
860
|
+
result = container.wait(timeout=timeout)
|
|
861
|
+
logs = container.logs().decode('utf-8', errors='ignore')
|
|
862
|
+
|
|
863
|
+
# Read output files
|
|
864
|
+
output_data = {}
|
|
865
|
+
for filename in os.listdir(output_dir):
|
|
866
|
+
filepath = os.path.join(output_dir, filename)
|
|
867
|
+
try:
|
|
868
|
+
with open(filepath, 'r') as f:
|
|
869
|
+
output_data[filename] = f.read()
|
|
870
|
+
except Exception:
|
|
871
|
+
output_data[filename] = '[binary]'
|
|
872
|
+
|
|
873
|
+
# Parse results
|
|
874
|
+
success = result.get('StatusCode', 1) == 0
|
|
875
|
+
|
|
876
|
+
# Capture evidence
|
|
877
|
+
evidence = await self.capture_evidence(
|
|
878
|
+
target, exploit_type,
|
|
879
|
+
capture_screenshot=self._is_web_exploit(exploit_type)
|
|
880
|
+
)
|
|
881
|
+
|
|
882
|
+
chain_of_custody.append({
|
|
883
|
+
'action': 'execution_completed',
|
|
884
|
+
'timestamp': datetime.utcnow().isoformat(),
|
|
885
|
+
'exit_code': result.get('StatusCode', -1)
|
|
886
|
+
})
|
|
887
|
+
|
|
888
|
+
return ExploitResult(
|
|
889
|
+
success=success,
|
|
890
|
+
exploit_type=exploit_type,
|
|
891
|
+
target=target,
|
|
892
|
+
timestamp=timestamp,
|
|
893
|
+
evidence=evidence,
|
|
894
|
+
output=logs + "\n" + json.dumps(output_data, indent=2),
|
|
895
|
+
status=ExploitStatus.SUCCESS if success else ExploitStatus.FAILED,
|
|
896
|
+
chain_of_custody=chain_of_custody
|
|
897
|
+
)
|
|
898
|
+
|
|
899
|
+
except Exception:
|
|
900
|
+
container.kill()
|
|
901
|
+
raise
|
|
902
|
+
|
|
903
|
+
except ContainerError as e:
|
|
904
|
+
return ExploitResult(
|
|
905
|
+
success=False,
|
|
906
|
+
exploit_type=exploit_type,
|
|
907
|
+
target=target,
|
|
908
|
+
timestamp=timestamp,
|
|
909
|
+
evidence=Evidence(),
|
|
910
|
+
output=str(e),
|
|
911
|
+
status=ExploitStatus.ERROR,
|
|
912
|
+
error=f"Container error: {str(e)}",
|
|
913
|
+
chain_of_custody=chain_of_custody
|
|
914
|
+
)
|
|
915
|
+
except ImageNotFound:
|
|
916
|
+
return ExploitResult(
|
|
917
|
+
success=False,
|
|
918
|
+
exploit_type=exploit_type,
|
|
919
|
+
target=target,
|
|
920
|
+
timestamp=timestamp,
|
|
921
|
+
evidence=Evidence(),
|
|
922
|
+
output="",
|
|
923
|
+
status=ExploitStatus.ERROR,
|
|
924
|
+
error=f"Docker image '{self.sandbox_config.image}' not found",
|
|
925
|
+
chain_of_custody=chain_of_custody
|
|
926
|
+
)
|
|
927
|
+
|
|
928
|
+
async def _execute_local_sandboxed(
|
|
929
|
+
self,
|
|
930
|
+
exploit_code: str,
|
|
931
|
+
target: str,
|
|
932
|
+
exploit_type: ExploitType,
|
|
933
|
+
parameters: Dict[str, Any],
|
|
934
|
+
timeout: int
|
|
935
|
+
) -> ExploitResult:
|
|
936
|
+
"""Execute exploit locally with restrictions."""
|
|
937
|
+
timestamp = datetime.utcnow()
|
|
938
|
+
chain_of_custody = []
|
|
939
|
+
|
|
940
|
+
# Generate and execute exploit script
|
|
941
|
+
script_content = self._generate_exploit_script(
|
|
942
|
+
exploit_code, target, exploit_type, parameters
|
|
943
|
+
)
|
|
944
|
+
|
|
945
|
+
with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as f:
|
|
946
|
+
f.write(script_content)
|
|
947
|
+
script_path = f.name
|
|
948
|
+
|
|
949
|
+
try:
|
|
950
|
+
chain_of_custody.append({
|
|
951
|
+
'action': 'local_execution_started',
|
|
952
|
+
'timestamp': datetime.utcnow().isoformat()
|
|
953
|
+
})
|
|
954
|
+
|
|
955
|
+
# Execute with timeout
|
|
956
|
+
process = await asyncio.create_subprocess_exec(
|
|
957
|
+
'python3', script_path,
|
|
958
|
+
stdout=asyncio.subprocess.PIPE,
|
|
959
|
+
stderr=asyncio.subprocess.PIPE
|
|
960
|
+
)
|
|
961
|
+
|
|
962
|
+
try:
|
|
963
|
+
stdout, stderr = await asyncio.wait_for(
|
|
964
|
+
process.communicate(), timeout=timeout
|
|
965
|
+
)
|
|
966
|
+
|
|
967
|
+
output = stdout.decode('utf-8', errors='ignore')
|
|
968
|
+
error = stderr.decode('utf-8', errors='ignore')
|
|
969
|
+
|
|
970
|
+
success = process.returncode == 0 and not error
|
|
971
|
+
|
|
972
|
+
# Capture evidence
|
|
973
|
+
evidence = await self.capture_evidence(
|
|
974
|
+
target, exploit_type,
|
|
975
|
+
capture_screenshot=self._is_web_exploit(exploit_type)
|
|
976
|
+
)
|
|
977
|
+
|
|
978
|
+
chain_of_custody.append({
|
|
979
|
+
'action': 'execution_completed',
|
|
980
|
+
'timestamp': datetime.utcnow().isoformat(),
|
|
981
|
+
'exit_code': process.returncode
|
|
982
|
+
})
|
|
983
|
+
|
|
984
|
+
return ExploitResult(
|
|
985
|
+
success=success,
|
|
986
|
+
exploit_type=exploit_type,
|
|
987
|
+
target=target,
|
|
988
|
+
timestamp=timestamp,
|
|
989
|
+
evidence=evidence,
|
|
990
|
+
output=output,
|
|
991
|
+
status=ExploitStatus.SUCCESS if success else ExploitStatus.FAILED,
|
|
992
|
+
error=error if error else None,
|
|
993
|
+
chain_of_custody=chain_of_custody
|
|
994
|
+
)
|
|
995
|
+
|
|
996
|
+
except asyncio.TimeoutError:
|
|
997
|
+
process.kill()
|
|
998
|
+
raise
|
|
999
|
+
|
|
1000
|
+
finally:
|
|
1001
|
+
os.unlink(script_path)
|
|
1002
|
+
|
|
1003
|
+
async def _execute_docker_command(
|
|
1004
|
+
self,
|
|
1005
|
+
command: str,
|
|
1006
|
+
timeout: int,
|
|
1007
|
+
network_access: bool
|
|
1008
|
+
) -> Dict[str, Any]:
|
|
1009
|
+
"""Execute a command in Docker container."""
|
|
1010
|
+
try:
|
|
1011
|
+
container = self.docker_client.containers.run(
|
|
1012
|
+
image=self.sandbox_config.image,
|
|
1013
|
+
command=['sh', '-c', command],
|
|
1014
|
+
network_disabled=not network_access,
|
|
1015
|
+
mem_limit=self.sandbox_config.memory_limit,
|
|
1016
|
+
detach=True,
|
|
1017
|
+
auto_remove=True
|
|
1018
|
+
)
|
|
1019
|
+
|
|
1020
|
+
result = container.wait(timeout=timeout)
|
|
1021
|
+
logs = container.logs().decode('utf-8', errors='ignore')
|
|
1022
|
+
|
|
1023
|
+
return {
|
|
1024
|
+
'stdout': logs,
|
|
1025
|
+
'stderr': '',
|
|
1026
|
+
'return_code': result.get('StatusCode', -1),
|
|
1027
|
+
'success': result.get('StatusCode', 1) == 0
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
except Exception as e:
|
|
1031
|
+
return {
|
|
1032
|
+
'stdout': '',
|
|
1033
|
+
'stderr': str(e),
|
|
1034
|
+
'return_code': -1,
|
|
1035
|
+
'success': False
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
async def _execute_local_command(
|
|
1039
|
+
self,
|
|
1040
|
+
command: str,
|
|
1041
|
+
timeout: int
|
|
1042
|
+
) -> Dict[str, Any]:
|
|
1043
|
+
"""Execute command locally."""
|
|
1044
|
+
try:
|
|
1045
|
+
process = await asyncio.create_subprocess_shell(
|
|
1046
|
+
command,
|
|
1047
|
+
stdout=asyncio.subprocess.PIPE,
|
|
1048
|
+
stderr=asyncio.subprocess.PIPE
|
|
1049
|
+
)
|
|
1050
|
+
|
|
1051
|
+
stdout, stderr = await asyncio.wait_for(
|
|
1052
|
+
process.communicate(), timeout=timeout
|
|
1053
|
+
)
|
|
1054
|
+
|
|
1055
|
+
return {
|
|
1056
|
+
'stdout': stdout.decode('utf-8', errors='ignore'),
|
|
1057
|
+
'stderr': stderr.decode('utf-8', errors='ignore'),
|
|
1058
|
+
'return_code': process.returncode,
|
|
1059
|
+
'success': process.returncode == 0
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
except asyncio.TimeoutError:
|
|
1063
|
+
process.kill()
|
|
1064
|
+
return {
|
|
1065
|
+
'stdout': '',
|
|
1066
|
+
'stderr': 'Timeout',
|
|
1067
|
+
'return_code': -1,
|
|
1068
|
+
'success': False
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
async def _capture_http_evidence(
|
|
1072
|
+
self,
|
|
1073
|
+
target: str,
|
|
1074
|
+
evidence_dir: str
|
|
1075
|
+
) -> List[Dict[str, Any]]:
|
|
1076
|
+
"""Capture HTTP response evidence."""
|
|
1077
|
+
responses = []
|
|
1078
|
+
|
|
1079
|
+
if not AIOHTTP_AVAILABLE:
|
|
1080
|
+
return responses
|
|
1081
|
+
|
|
1082
|
+
try:
|
|
1083
|
+
# Ensure URL has scheme
|
|
1084
|
+
if not target.startswith(('http://', 'https://')):
|
|
1085
|
+
target = f"http://{target}"
|
|
1086
|
+
|
|
1087
|
+
timeout = aiohttp.ClientTimeout(total=30)
|
|
1088
|
+
async with aiohttp.ClientSession(timeout=timeout) as session:
|
|
1089
|
+
async with session.get(target, ssl=False) as response:
|
|
1090
|
+
body_text = await response.text()
|
|
1091
|
+
response_data = {
|
|
1092
|
+
'url': str(response.url),
|
|
1093
|
+
'status': response.status,
|
|
1094
|
+
'headers': dict(response.headers),
|
|
1095
|
+
'timestamp': datetime.utcnow().isoformat(),
|
|
1096
|
+
'body': body_text[:10000] # Limit body size
|
|
1097
|
+
}
|
|
1098
|
+
responses.append(response_data)
|
|
1099
|
+
|
|
1100
|
+
# Save to file
|
|
1101
|
+
http_file = os.path.join(evidence_dir, 'http_response.json')
|
|
1102
|
+
with open(http_file, 'w') as f:
|
|
1103
|
+
json.dump(response_data, f, indent=2, default=str)
|
|
1104
|
+
|
|
1105
|
+
except Exception as e:
|
|
1106
|
+
logger.warning(f"HTTP capture failed: {e}")
|
|
1107
|
+
|
|
1108
|
+
return responses
|
|
1109
|
+
|
|
1110
|
+
async def _capture_screenshot(
|
|
1111
|
+
self,
|
|
1112
|
+
target: str,
|
|
1113
|
+
evidence_dir: str
|
|
1114
|
+
) -> Optional[str]:
|
|
1115
|
+
"""Capture browser screenshot using Playwright."""
|
|
1116
|
+
if not PLAYWRIGHT_AVAILABLE or not self.enable_playwright:
|
|
1117
|
+
return None
|
|
1118
|
+
|
|
1119
|
+
screenshot_path = None
|
|
1120
|
+
|
|
1121
|
+
try:
|
|
1122
|
+
# Ensure URL has scheme
|
|
1123
|
+
if not target.startswith(('http://', 'https://')):
|
|
1124
|
+
target = f"http://{target}"
|
|
1125
|
+
|
|
1126
|
+
async with async_playwright() as p:
|
|
1127
|
+
browser = await p.chromium.launch(headless=True)
|
|
1128
|
+
context = await browser.new_context(
|
|
1129
|
+
viewport={'width': 1920, 'height': 1080},
|
|
1130
|
+
user_agent='ZenAI-ExploitValidator/1.0'
|
|
1131
|
+
)
|
|
1132
|
+
|
|
1133
|
+
page = await context.new_page()
|
|
1134
|
+
|
|
1135
|
+
try:
|
|
1136
|
+
await page.goto(target, wait_until='networkidle', timeout=30000)
|
|
1137
|
+
|
|
1138
|
+
# Capture screenshot
|
|
1139
|
+
screenshot_path = os.path.join(evidence_dir, 'screenshot.png')
|
|
1140
|
+
await page.screenshot(path=screenshot_path, full_page=True)
|
|
1141
|
+
|
|
1142
|
+
# Also capture page content
|
|
1143
|
+
content = await page.content()
|
|
1144
|
+
html_path = os.path.join(evidence_dir, 'page.html')
|
|
1145
|
+
with open(html_path, 'w', encoding='utf-8') as f:
|
|
1146
|
+
f.write(content)
|
|
1147
|
+
|
|
1148
|
+
except Exception as e:
|
|
1149
|
+
logger.warning(f"Page navigation failed: {e}")
|
|
1150
|
+
|
|
1151
|
+
await browser.close()
|
|
1152
|
+
|
|
1153
|
+
except Exception as e:
|
|
1154
|
+
logger.warning(f"Screenshot capture failed: {e}")
|
|
1155
|
+
|
|
1156
|
+
return screenshot_path
|
|
1157
|
+
|
|
1158
|
+
async def _capture_pcap(
|
|
1159
|
+
self,
|
|
1160
|
+
target: str,
|
|
1161
|
+
evidence_dir: str
|
|
1162
|
+
) -> Optional[str]:
|
|
1163
|
+
"""Capture network traffic using tcpdump."""
|
|
1164
|
+
# PCAP capture requires root and is complex in async context
|
|
1165
|
+
# This is a placeholder implementation
|
|
1166
|
+
logger.debug("PCAP capture not implemented - requires elevated privileges")
|
|
1167
|
+
return None
|
|
1168
|
+
|
|
1169
|
+
async def _get_source_ip(self) -> str:
|
|
1170
|
+
"""Get the source IP address."""
|
|
1171
|
+
try:
|
|
1172
|
+
# Connect to a public DNS server to determine outbound IP
|
|
1173
|
+
reader, writer = await asyncio.wait_for(
|
|
1174
|
+
asyncio.open_connection('8.8.8.8', 53),
|
|
1175
|
+
timeout=5
|
|
1176
|
+
)
|
|
1177
|
+
sock = writer.get_extra_info('socket')
|
|
1178
|
+
ip = sock.getsockname()[0]
|
|
1179
|
+
writer.close()
|
|
1180
|
+
await writer.wait_closed()
|
|
1181
|
+
return ip
|
|
1182
|
+
except Exception:
|
|
1183
|
+
return 'unknown'
|
|
1184
|
+
|
|
1185
|
+
def _generate_exploit_script(
|
|
1186
|
+
self,
|
|
1187
|
+
exploit_code: str,
|
|
1188
|
+
target: str,
|
|
1189
|
+
exploit_type: ExploitType,
|
|
1190
|
+
parameters: Dict[str, Any]
|
|
1191
|
+
) -> str:
|
|
1192
|
+
"""Generate a safe exploit execution script."""
|
|
1193
|
+
|
|
1194
|
+
# Sanitize inputs
|
|
1195
|
+
safe_target = shlex.quote(target)
|
|
1196
|
+
|
|
1197
|
+
script = f'''#!/usr/bin/env python3
|
|
1198
|
+
"""
|
|
1199
|
+
Auto-generated exploit validation script
|
|
1200
|
+
Generated: {datetime.utcnow().isoformat()}
|
|
1201
|
+
Exploit Type: {exploit_type.value}
|
|
1202
|
+
Target: {target}
|
|
1203
|
+
"""
|
|
1204
|
+
|
|
1205
|
+
import json
|
|
1206
|
+
import sys
|
|
1207
|
+
import os
|
|
1208
|
+
|
|
1209
|
+
def main():
|
|
1210
|
+
target = {safe_target}
|
|
1211
|
+
parameters = {json.dumps(parameters)}
|
|
1212
|
+
|
|
1213
|
+
# Output directory
|
|
1214
|
+
output_dir = "/output" if os.path.exists("/output") else "."
|
|
1215
|
+
|
|
1216
|
+
result = {{
|
|
1217
|
+
"success": False,
|
|
1218
|
+
"target": target,
|
|
1219
|
+
"output": "",
|
|
1220
|
+
"error": None
|
|
1221
|
+
}}
|
|
1222
|
+
|
|
1223
|
+
try:
|
|
1224
|
+
# User exploit code
|
|
1225
|
+
{self._indent_code(exploit_code, 8)}
|
|
1226
|
+
|
|
1227
|
+
result["success"] = True
|
|
1228
|
+
result["output"] = "Exploit executed successfully"
|
|
1229
|
+
|
|
1230
|
+
except Exception as e:
|
|
1231
|
+
result["error"] = str(e)
|
|
1232
|
+
result["output"] = f"Exploit failed: {{e}}"
|
|
1233
|
+
|
|
1234
|
+
# Write results
|
|
1235
|
+
with open(os.path.join(output_dir, "result.json"), "w") as f:
|
|
1236
|
+
json.dump(result, f, indent=2)
|
|
1237
|
+
|
|
1238
|
+
print(json.dumps(result))
|
|
1239
|
+
return 0 if result["success"] else 1
|
|
1240
|
+
|
|
1241
|
+
if __name__ == "__main__":
|
|
1242
|
+
sys.exit(main())
|
|
1243
|
+
'''
|
|
1244
|
+
return script
|
|
1245
|
+
|
|
1246
|
+
def _indent_code(self, code: str, indent: int) -> str:
|
|
1247
|
+
"""Indent code block for script generation."""
|
|
1248
|
+
lines = code.split('\n')
|
|
1249
|
+
spaces = ' ' * indent
|
|
1250
|
+
return '\n'.join(spaces + line for line in lines)
|
|
1251
|
+
|
|
1252
|
+
|
|
1253
|
+
class ExploitValidatorPool:
|
|
1254
|
+
"""
|
|
1255
|
+
Pool of ExploitValidators for concurrent execution.
|
|
1256
|
+
|
|
1257
|
+
Manages multiple validators with load balancing and
|
|
1258
|
+
resource management.
|
|
1259
|
+
"""
|
|
1260
|
+
|
|
1261
|
+
def __init__(
|
|
1262
|
+
self,
|
|
1263
|
+
pool_size: int = 3,
|
|
1264
|
+
safety_level: SafetyLevel = SafetyLevel.CONTROLLED,
|
|
1265
|
+
scope_config: Optional[ScopeConfig] = None
|
|
1266
|
+
):
|
|
1267
|
+
"""
|
|
1268
|
+
Initialize validator pool.
|
|
1269
|
+
|
|
1270
|
+
Args:
|
|
1271
|
+
pool_size: Number of validators in pool
|
|
1272
|
+
safety_level: Safety level for all validators
|
|
1273
|
+
scope_config: Scope configuration
|
|
1274
|
+
"""
|
|
1275
|
+
self.pool_size = pool_size
|
|
1276
|
+
self.safety_level = safety_level
|
|
1277
|
+
self.scope_config = scope_config or ScopeConfig()
|
|
1278
|
+
|
|
1279
|
+
self._validators: List[ExploitValidator] = []
|
|
1280
|
+
self._queue: asyncio.Queue = asyncio.Queue()
|
|
1281
|
+
self._semaphore = asyncio.Semaphore(pool_size)
|
|
1282
|
+
|
|
1283
|
+
# Statistics
|
|
1284
|
+
self._stats = {
|
|
1285
|
+
'total_executed': 0,
|
|
1286
|
+
'successful': 0,
|
|
1287
|
+
'failed': 0,
|
|
1288
|
+
'blocked': 0
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
async def initialize(self) -> None:
|
|
1292
|
+
"""Initialize all validators in the pool."""
|
|
1293
|
+
for i in range(self.pool_size):
|
|
1294
|
+
validator = ExploitValidator(
|
|
1295
|
+
safety_level=self.safety_level,
|
|
1296
|
+
scope_config=self.scope_config
|
|
1297
|
+
)
|
|
1298
|
+
self._validators.append(validator)
|
|
1299
|
+
logger.info(f"Validator pool initialized with {self.pool_size} validators")
|
|
1300
|
+
|
|
1301
|
+
async def submit(
|
|
1302
|
+
self,
|
|
1303
|
+
exploit_code: str,
|
|
1304
|
+
target: str,
|
|
1305
|
+
exploit_type: ExploitType,
|
|
1306
|
+
parameters: Optional[Dict[str, Any]] = None
|
|
1307
|
+
) -> ExploitResult:
|
|
1308
|
+
"""
|
|
1309
|
+
Submit an exploit for validation.
|
|
1310
|
+
|
|
1311
|
+
Args:
|
|
1312
|
+
exploit_code: Exploit code to execute
|
|
1313
|
+
target: Target for exploitation
|
|
1314
|
+
exploit_type: Type of exploit
|
|
1315
|
+
parameters: Additional parameters
|
|
1316
|
+
|
|
1317
|
+
Returns:
|
|
1318
|
+
ExploitResult from validation
|
|
1319
|
+
"""
|
|
1320
|
+
async with self._semaphore:
|
|
1321
|
+
# Round-robin selection
|
|
1322
|
+
validator = self._validators[
|
|
1323
|
+
self._stats['total_executed'] % len(self._validators)
|
|
1324
|
+
]
|
|
1325
|
+
|
|
1326
|
+
self._stats['total_executed'] += 1
|
|
1327
|
+
|
|
1328
|
+
result = await validator.validate(
|
|
1329
|
+
exploit_code=exploit_code,
|
|
1330
|
+
target=target,
|
|
1331
|
+
exploit_type=exploit_type,
|
|
1332
|
+
parameters=parameters
|
|
1333
|
+
)
|
|
1334
|
+
|
|
1335
|
+
# Update statistics
|
|
1336
|
+
if result.success:
|
|
1337
|
+
self._stats['successful'] += 1
|
|
1338
|
+
elif result.status == ExploitStatus.BLOCKED:
|
|
1339
|
+
self._stats['blocked'] += 1
|
|
1340
|
+
else:
|
|
1341
|
+
self._stats['failed'] += 1
|
|
1342
|
+
|
|
1343
|
+
return result
|
|
1344
|
+
|
|
1345
|
+
async def submit_batch(
|
|
1346
|
+
self,
|
|
1347
|
+
exploits: List[Dict[str, Any]]
|
|
1348
|
+
) -> List[ExploitResult]:
|
|
1349
|
+
"""
|
|
1350
|
+
Submit multiple exploits for validation.
|
|
1351
|
+
|
|
1352
|
+
Args:
|
|
1353
|
+
exploits: List of exploit dictionaries
|
|
1354
|
+
|
|
1355
|
+
Returns:
|
|
1356
|
+
List of ExploitResults
|
|
1357
|
+
"""
|
|
1358
|
+
tasks = []
|
|
1359
|
+
for exploit in exploits:
|
|
1360
|
+
task = self.submit(
|
|
1361
|
+
exploit_code=exploit['code'],
|
|
1362
|
+
target=exploit['target'],
|
|
1363
|
+
exploit_type=ExploitType(exploit.get('type', 'web_rce')),
|
|
1364
|
+
parameters=exploit.get('parameters', {})
|
|
1365
|
+
)
|
|
1366
|
+
tasks.append(task)
|
|
1367
|
+
|
|
1368
|
+
return await asyncio.gather(*tasks, return_exceptions=True)
|
|
1369
|
+
|
|
1370
|
+
def get_stats(self) -> Dict[str, int]:
|
|
1371
|
+
"""Get pool statistics."""
|
|
1372
|
+
return self._stats.copy()
|
|
1373
|
+
|
|
1374
|
+
def kill_all(self) -> None:
|
|
1375
|
+
"""Activate kill switch on all validators."""
|
|
1376
|
+
for validator in self._validators:
|
|
1377
|
+
validator.kill_switch()
|
|
1378
|
+
|
|
1379
|
+
|
|
1380
|
+
# Convenience functions for common use cases
|
|
1381
|
+
|
|
1382
|
+
async def validate_sql_injection(
|
|
1383
|
+
target: str,
|
|
1384
|
+
parameter: str,
|
|
1385
|
+
payload: str,
|
|
1386
|
+
method: str = "GET",
|
|
1387
|
+
**kwargs
|
|
1388
|
+
) -> ExploitResult:
|
|
1389
|
+
"""
|
|
1390
|
+
Validate SQL injection vulnerability.
|
|
1391
|
+
|
|
1392
|
+
Args:
|
|
1393
|
+
target: Target URL
|
|
1394
|
+
parameter: Vulnerable parameter
|
|
1395
|
+
payload: SQL injection payload
|
|
1396
|
+
method: HTTP method
|
|
1397
|
+
**kwargs: Additional options for ExploitValidator
|
|
1398
|
+
|
|
1399
|
+
Returns:
|
|
1400
|
+
ExploitResult
|
|
1401
|
+
"""
|
|
1402
|
+
exploit_code = f'''
|
|
1403
|
+
import requests
|
|
1404
|
+
|
|
1405
|
+
url = "{target}"
|
|
1406
|
+
params = {{"{parameter}": "{payload}"}}
|
|
1407
|
+
response = requests.{method.lower()}(url, params=params, timeout=30)
|
|
1408
|
+
|
|
1409
|
+
# Check for SQL error indicators
|
|
1410
|
+
sql_errors = [
|
|
1411
|
+
"sql syntax", "mysql_fetch", "pg_query", "ora-",
|
|
1412
|
+
"sqlserver", "jdbc", "odbc", "syntax error"
|
|
1413
|
+
]
|
|
1414
|
+
|
|
1415
|
+
found_error = any(err in response.text.lower() for err in sql_errors)
|
|
1416
|
+
print(f"Response status: {{response.status_code}}")
|
|
1417
|
+
print(f"SQL error detected: {{found_error}}")
|
|
1418
|
+
print(f"Response length: {{len(response.text)}}")
|
|
1419
|
+
'''
|
|
1420
|
+
|
|
1421
|
+
validator = ExploitValidator(**kwargs)
|
|
1422
|
+
return await validator.validate(
|
|
1423
|
+
exploit_code=exploit_code,
|
|
1424
|
+
target=target,
|
|
1425
|
+
exploit_type=ExploitType.WEB_SQLI
|
|
1426
|
+
)
|
|
1427
|
+
|
|
1428
|
+
|
|
1429
|
+
async def validate_xss(
|
|
1430
|
+
target: str,
|
|
1431
|
+
parameter: str,
|
|
1432
|
+
payload: str = "<script>alert(1)</script>",
|
|
1433
|
+
**kwargs
|
|
1434
|
+
) -> ExploitResult:
|
|
1435
|
+
"""
|
|
1436
|
+
Validate XSS vulnerability.
|
|
1437
|
+
|
|
1438
|
+
Args:
|
|
1439
|
+
target: Target URL
|
|
1440
|
+
parameter: Vulnerable parameter
|
|
1441
|
+
payload: XSS payload
|
|
1442
|
+
**kwargs: Additional options for ExploitValidator
|
|
1443
|
+
|
|
1444
|
+
Returns:
|
|
1445
|
+
ExploitResult
|
|
1446
|
+
"""
|
|
1447
|
+
exploit_code = f'''
|
|
1448
|
+
import requests
|
|
1449
|
+
|
|
1450
|
+
url = "{target}"
|
|
1451
|
+
payload = "{payload}"
|
|
1452
|
+
response = requests.get(url, params={{"{parameter}": payload}}, timeout=30)
|
|
1453
|
+
|
|
1454
|
+
# Check if payload is reflected
|
|
1455
|
+
is_reflected = payload in response.text
|
|
1456
|
+
print(f"Payload reflected: {{is_reflected}}")
|
|
1457
|
+
print(f"Response status: {{response.status_code}}")
|
|
1458
|
+
|
|
1459
|
+
if is_reflected:
|
|
1460
|
+
print("XSS vulnerability confirmed - payload reflected")
|
|
1461
|
+
else:
|
|
1462
|
+
print("Payload not reflected in response")
|
|
1463
|
+
'''
|
|
1464
|
+
|
|
1465
|
+
validator = ExploitValidator(**kwargs)
|
|
1466
|
+
return await validator.validate(
|
|
1467
|
+
exploit_code=exploit_code,
|
|
1468
|
+
target=target,
|
|
1469
|
+
exploit_type=ExploitType.WEB_XSS
|
|
1470
|
+
)
|
|
1471
|
+
|
|
1472
|
+
|
|
1473
|
+
async def validate_command_injection(
|
|
1474
|
+
target: str,
|
|
1475
|
+
parameter: str,
|
|
1476
|
+
command: str = "echo ZENAI_TEST",
|
|
1477
|
+
**kwargs
|
|
1478
|
+
) -> ExploitResult:
|
|
1479
|
+
"""
|
|
1480
|
+
Validate command injection vulnerability.
|
|
1481
|
+
|
|
1482
|
+
Args:
|
|
1483
|
+
target: Target URL
|
|
1484
|
+
parameter: Vulnerable parameter
|
|
1485
|
+
command: Command to inject
|
|
1486
|
+
**kwargs: Additional options for ExploitValidator
|
|
1487
|
+
|
|
1488
|
+
Returns:
|
|
1489
|
+
ExploitResult
|
|
1490
|
+
"""
|
|
1491
|
+
exploit_code = f'''
|
|
1492
|
+
import requests
|
|
1493
|
+
import time
|
|
1494
|
+
|
|
1495
|
+
url = "{target}"
|
|
1496
|
+
|
|
1497
|
+
# Test command injection with timing
|
|
1498
|
+
payload = "; {command}; #"
|
|
1499
|
+
start = time.time()
|
|
1500
|
+
response = requests.get(url, params={{"{parameter}": payload}}, timeout=60)
|
|
1501
|
+
elapsed = time.time() - start
|
|
1502
|
+
|
|
1503
|
+
print(f"Response time: {{elapsed:.2f}}s")
|
|
1504
|
+
print(f"Response status: {{response.status_code}}")
|
|
1505
|
+
|
|
1506
|
+
# Check for command output
|
|
1507
|
+
if "ZENAI_TEST" in response.text:
|
|
1508
|
+
print("Command injection confirmed - output reflected")
|
|
1509
|
+
elif elapsed > 4:
|
|
1510
|
+
print("Possible blind command injection - timing detected")
|
|
1511
|
+
else:
|
|
1512
|
+
print("Command injection not confirmed")
|
|
1513
|
+
'''
|
|
1514
|
+
|
|
1515
|
+
validator = ExploitValidator(**kwargs)
|
|
1516
|
+
return await validator.validate(
|
|
1517
|
+
exploit_code=exploit_code,
|
|
1518
|
+
target=target,
|
|
1519
|
+
exploit_type=ExploitType.WEB_CMD_INJECTION
|
|
1520
|
+
)
|
|
1521
|
+
|
|
1522
|
+
|
|
1523
|
+
# Export public API
|
|
1524
|
+
__all__ = [
|
|
1525
|
+
'ExploitValidator',
|
|
1526
|
+
'ExploitValidatorPool',
|
|
1527
|
+
'ExploitResult',
|
|
1528
|
+
'ExploitType',
|
|
1529
|
+
'ExploitStatus',
|
|
1530
|
+
'SafetyLevel',
|
|
1531
|
+
'Evidence',
|
|
1532
|
+
'ScopeConfig',
|
|
1533
|
+
'SandboxConfig',
|
|
1534
|
+
'validate_sql_injection',
|
|
1535
|
+
'validate_xss',
|
|
1536
|
+
'validate_command_injection',
|
|
1537
|
+
]
|