aiptx 2.0.7__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.
- aipt_v2/__init__.py +110 -0
- aipt_v2/__main__.py +24 -0
- aipt_v2/agents/AIPTxAgent/__init__.py +10 -0
- aipt_v2/agents/AIPTxAgent/aiptx_agent.py +211 -0
- aipt_v2/agents/__init__.py +46 -0
- aipt_v2/agents/base.py +520 -0
- aipt_v2/agents/exploit_agent.py +688 -0
- aipt_v2/agents/ptt.py +406 -0
- aipt_v2/agents/state.py +168 -0
- aipt_v2/app.py +957 -0
- aipt_v2/browser/__init__.py +31 -0
- aipt_v2/browser/automation.py +458 -0
- aipt_v2/browser/crawler.py +453 -0
- aipt_v2/cli.py +2933 -0
- aipt_v2/compliance/__init__.py +71 -0
- aipt_v2/compliance/compliance_report.py +449 -0
- aipt_v2/compliance/framework_mapper.py +424 -0
- aipt_v2/compliance/nist_mapping.py +345 -0
- aipt_v2/compliance/owasp_mapping.py +330 -0
- aipt_v2/compliance/pci_mapping.py +297 -0
- aipt_v2/config.py +341 -0
- aipt_v2/core/__init__.py +43 -0
- aipt_v2/core/agent.py +630 -0
- aipt_v2/core/llm.py +395 -0
- aipt_v2/core/memory.py +305 -0
- aipt_v2/core/ptt.py +329 -0
- aipt_v2/database/__init__.py +14 -0
- aipt_v2/database/models.py +232 -0
- aipt_v2/database/repository.py +384 -0
- aipt_v2/docker/__init__.py +23 -0
- aipt_v2/docker/builder.py +260 -0
- aipt_v2/docker/manager.py +222 -0
- aipt_v2/docker/sandbox.py +371 -0
- aipt_v2/evasion/__init__.py +58 -0
- aipt_v2/evasion/request_obfuscator.py +272 -0
- aipt_v2/evasion/tls_fingerprint.py +285 -0
- aipt_v2/evasion/ua_rotator.py +301 -0
- aipt_v2/evasion/waf_bypass.py +439 -0
- aipt_v2/execution/__init__.py +23 -0
- aipt_v2/execution/executor.py +302 -0
- aipt_v2/execution/parser.py +544 -0
- aipt_v2/execution/terminal.py +337 -0
- aipt_v2/health.py +437 -0
- aipt_v2/intelligence/__init__.py +194 -0
- aipt_v2/intelligence/adaptation.py +474 -0
- aipt_v2/intelligence/auth.py +520 -0
- aipt_v2/intelligence/chaining.py +775 -0
- aipt_v2/intelligence/correlation.py +536 -0
- aipt_v2/intelligence/cve_aipt.py +334 -0
- aipt_v2/intelligence/cve_info.py +1111 -0
- aipt_v2/intelligence/knowledge_graph.py +590 -0
- aipt_v2/intelligence/learning.py +626 -0
- aipt_v2/intelligence/llm_analyzer.py +502 -0
- aipt_v2/intelligence/llm_tool_selector.py +518 -0
- aipt_v2/intelligence/payload_generator.py +562 -0
- aipt_v2/intelligence/rag.py +239 -0
- aipt_v2/intelligence/scope.py +442 -0
- aipt_v2/intelligence/searchers/__init__.py +5 -0
- aipt_v2/intelligence/searchers/exploitdb_searcher.py +523 -0
- aipt_v2/intelligence/searchers/github_searcher.py +467 -0
- aipt_v2/intelligence/searchers/google_searcher.py +281 -0
- aipt_v2/intelligence/tools.json +443 -0
- aipt_v2/intelligence/triage.py +670 -0
- aipt_v2/interactive_shell.py +559 -0
- aipt_v2/interface/__init__.py +5 -0
- aipt_v2/interface/cli.py +230 -0
- aipt_v2/interface/main.py +501 -0
- aipt_v2/interface/tui.py +1276 -0
- aipt_v2/interface/utils.py +583 -0
- aipt_v2/llm/__init__.py +39 -0
- aipt_v2/llm/config.py +26 -0
- aipt_v2/llm/llm.py +514 -0
- aipt_v2/llm/memory.py +214 -0
- aipt_v2/llm/request_queue.py +89 -0
- aipt_v2/llm/utils.py +89 -0
- aipt_v2/local_tool_installer.py +1467 -0
- aipt_v2/models/__init__.py +15 -0
- aipt_v2/models/findings.py +295 -0
- aipt_v2/models/phase_result.py +224 -0
- aipt_v2/models/scan_config.py +207 -0
- aipt_v2/monitoring/grafana/dashboards/aipt-dashboard.json +355 -0
- aipt_v2/monitoring/grafana/dashboards/default.yml +17 -0
- aipt_v2/monitoring/grafana/datasources/prometheus.yml +17 -0
- aipt_v2/monitoring/prometheus.yml +60 -0
- aipt_v2/orchestration/__init__.py +52 -0
- aipt_v2/orchestration/pipeline.py +398 -0
- aipt_v2/orchestration/progress.py +300 -0
- aipt_v2/orchestration/scheduler.py +296 -0
- aipt_v2/orchestrator.py +2427 -0
- aipt_v2/payloads/__init__.py +27 -0
- aipt_v2/payloads/cmdi.py +150 -0
- aipt_v2/payloads/sqli.py +263 -0
- aipt_v2/payloads/ssrf.py +204 -0
- aipt_v2/payloads/templates.py +222 -0
- aipt_v2/payloads/traversal.py +166 -0
- aipt_v2/payloads/xss.py +204 -0
- aipt_v2/prompts/__init__.py +60 -0
- aipt_v2/proxy/__init__.py +29 -0
- aipt_v2/proxy/history.py +352 -0
- aipt_v2/proxy/interceptor.py +452 -0
- aipt_v2/recon/__init__.py +44 -0
- aipt_v2/recon/dns.py +241 -0
- aipt_v2/recon/osint.py +367 -0
- aipt_v2/recon/subdomain.py +372 -0
- aipt_v2/recon/tech_detect.py +311 -0
- aipt_v2/reports/__init__.py +17 -0
- aipt_v2/reports/generator.py +313 -0
- aipt_v2/reports/html_report.py +378 -0
- aipt_v2/runtime/__init__.py +53 -0
- aipt_v2/runtime/base.py +30 -0
- aipt_v2/runtime/docker.py +401 -0
- aipt_v2/runtime/local.py +346 -0
- aipt_v2/runtime/tool_server.py +205 -0
- aipt_v2/runtime/vps.py +830 -0
- aipt_v2/scanners/__init__.py +28 -0
- aipt_v2/scanners/base.py +273 -0
- aipt_v2/scanners/nikto.py +244 -0
- aipt_v2/scanners/nmap.py +402 -0
- aipt_v2/scanners/nuclei.py +273 -0
- aipt_v2/scanners/web.py +454 -0
- aipt_v2/scripts/security_audit.py +366 -0
- aipt_v2/setup_wizard.py +941 -0
- aipt_v2/skills/__init__.py +80 -0
- aipt_v2/skills/agents/__init__.py +14 -0
- aipt_v2/skills/agents/api_tester.py +706 -0
- aipt_v2/skills/agents/base.py +477 -0
- aipt_v2/skills/agents/code_review.py +459 -0
- aipt_v2/skills/agents/security_agent.py +336 -0
- aipt_v2/skills/agents/web_pentest.py +818 -0
- aipt_v2/skills/prompts/__init__.py +647 -0
- aipt_v2/system_detector.py +539 -0
- aipt_v2/telemetry/__init__.py +7 -0
- aipt_v2/telemetry/tracer.py +347 -0
- aipt_v2/terminal/__init__.py +28 -0
- aipt_v2/terminal/executor.py +400 -0
- aipt_v2/terminal/sandbox.py +350 -0
- aipt_v2/tools/__init__.py +44 -0
- aipt_v2/tools/active_directory/__init__.py +78 -0
- aipt_v2/tools/active_directory/ad_config.py +238 -0
- aipt_v2/tools/active_directory/bloodhound_wrapper.py +447 -0
- aipt_v2/tools/active_directory/kerberos_attacks.py +430 -0
- aipt_v2/tools/active_directory/ldap_enum.py +533 -0
- aipt_v2/tools/active_directory/smb_attacks.py +505 -0
- aipt_v2/tools/agents_graph/__init__.py +19 -0
- aipt_v2/tools/agents_graph/agents_graph_actions.py +69 -0
- aipt_v2/tools/api_security/__init__.py +76 -0
- aipt_v2/tools/api_security/api_discovery.py +608 -0
- aipt_v2/tools/api_security/graphql_scanner.py +622 -0
- aipt_v2/tools/api_security/jwt_analyzer.py +577 -0
- aipt_v2/tools/api_security/openapi_fuzzer.py +761 -0
- aipt_v2/tools/browser/__init__.py +5 -0
- aipt_v2/tools/browser/browser_actions.py +238 -0
- aipt_v2/tools/browser/browser_instance.py +535 -0
- aipt_v2/tools/browser/tab_manager.py +344 -0
- aipt_v2/tools/cloud/__init__.py +70 -0
- aipt_v2/tools/cloud/cloud_config.py +273 -0
- aipt_v2/tools/cloud/cloud_scanner.py +639 -0
- aipt_v2/tools/cloud/prowler_tool.py +571 -0
- aipt_v2/tools/cloud/scoutsuite_tool.py +359 -0
- aipt_v2/tools/executor.py +307 -0
- aipt_v2/tools/parser.py +408 -0
- aipt_v2/tools/proxy/__init__.py +5 -0
- aipt_v2/tools/proxy/proxy_actions.py +103 -0
- aipt_v2/tools/proxy/proxy_manager.py +789 -0
- aipt_v2/tools/registry.py +196 -0
- aipt_v2/tools/scanners/__init__.py +343 -0
- aipt_v2/tools/scanners/acunetix_tool.py +712 -0
- aipt_v2/tools/scanners/burp_tool.py +631 -0
- aipt_v2/tools/scanners/config.py +156 -0
- aipt_v2/tools/scanners/nessus_tool.py +588 -0
- aipt_v2/tools/scanners/zap_tool.py +612 -0
- aipt_v2/tools/terminal/__init__.py +5 -0
- aipt_v2/tools/terminal/terminal_actions.py +37 -0
- aipt_v2/tools/terminal/terminal_manager.py +153 -0
- aipt_v2/tools/terminal/terminal_session.py +449 -0
- aipt_v2/tools/tool_processing.py +108 -0
- aipt_v2/utils/__init__.py +17 -0
- aipt_v2/utils/logging.py +202 -0
- aipt_v2/utils/model_manager.py +187 -0
- aipt_v2/utils/searchers/__init__.py +269 -0
- aipt_v2/verify_install.py +793 -0
- aiptx-2.0.7.dist-info/METADATA +345 -0
- aiptx-2.0.7.dist-info/RECORD +187 -0
- aiptx-2.0.7.dist-info/WHEEL +5 -0
- aiptx-2.0.7.dist-info/entry_points.txt +7 -0
- aiptx-2.0.7.dist-info/licenses/LICENSE +21 -0
- aiptx-2.0.7.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AIPT Real-Time Adaptation Engine
|
|
3
|
+
|
|
4
|
+
Adapts scanning strategy in real-time based on target responses:
|
|
5
|
+
- Detects WAF/rate limiting and adjusts accordingly
|
|
6
|
+
- Modifies payloads when blocked
|
|
7
|
+
- Adjusts timing to avoid detection
|
|
8
|
+
- Switches techniques based on feedback
|
|
9
|
+
|
|
10
|
+
This provides intelligent, adaptive scanning that responds to defenses.
|
|
11
|
+
"""
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import asyncio
|
|
15
|
+
import logging
|
|
16
|
+
import time
|
|
17
|
+
from dataclasses import dataclass, field
|
|
18
|
+
from datetime import datetime
|
|
19
|
+
from typing import Any, Callable, Optional
|
|
20
|
+
from enum import Enum
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class DefenseType(Enum):
|
|
26
|
+
"""Types of defenses that can be detected."""
|
|
27
|
+
WAF = "waf"
|
|
28
|
+
RATE_LIMIT = "rate_limit"
|
|
29
|
+
IP_BLOCK = "ip_block"
|
|
30
|
+
GEO_BLOCK = "geo_block"
|
|
31
|
+
CAPTCHA = "captcha"
|
|
32
|
+
HONEYPOT = "honeypot"
|
|
33
|
+
TARPIT = "tarpit"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class AdaptationAction(Enum):
|
|
37
|
+
"""Actions that can be taken in response to defenses."""
|
|
38
|
+
SLOW_DOWN = "slow_down"
|
|
39
|
+
CHANGE_PAYLOAD = "change_payload"
|
|
40
|
+
USE_PROXY = "use_proxy"
|
|
41
|
+
WAIT_AND_RETRY = "wait_and_retry"
|
|
42
|
+
SKIP_ENDPOINT = "skip_endpoint"
|
|
43
|
+
SWITCH_TECHNIQUE = "switch_technique"
|
|
44
|
+
ABORT = "abort"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass
|
|
48
|
+
class DefenseDetection:
|
|
49
|
+
"""Detection of a defensive measure."""
|
|
50
|
+
defense_type: DefenseType
|
|
51
|
+
confidence: float # 0.0 to 1.0
|
|
52
|
+
evidence: str
|
|
53
|
+
detected_at: datetime = field(default_factory=datetime.utcnow)
|
|
54
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dataclass
|
|
58
|
+
class AdaptationStrategy:
|
|
59
|
+
"""Strategy for adapting to detected defenses."""
|
|
60
|
+
action: AdaptationAction
|
|
61
|
+
parameters: dict[str, Any]
|
|
62
|
+
reason: str
|
|
63
|
+
expected_outcome: str
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@dataclass
|
|
67
|
+
class RequestResult:
|
|
68
|
+
"""Result of a request for adaptation analysis."""
|
|
69
|
+
url: str
|
|
70
|
+
status_code: int
|
|
71
|
+
response_time_ms: int
|
|
72
|
+
response_size: int
|
|
73
|
+
blocked: bool = False
|
|
74
|
+
error: Optional[str] = None
|
|
75
|
+
headers: dict[str, str] = field(default_factory=dict)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@dataclass
|
|
79
|
+
class AdaptationState:
|
|
80
|
+
"""Current state of the adaptation engine."""
|
|
81
|
+
request_count: int = 0
|
|
82
|
+
blocked_count: int = 0
|
|
83
|
+
success_count: int = 0
|
|
84
|
+
current_delay_ms: int = 100
|
|
85
|
+
detected_defenses: list[DefenseDetection] = field(default_factory=list)
|
|
86
|
+
payload_failures: dict[str, int] = field(default_factory=dict)
|
|
87
|
+
last_request_time: Optional[datetime] = None
|
|
88
|
+
is_rate_limited: bool = False
|
|
89
|
+
is_blocked: bool = False
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class RealTimeAdapter:
|
|
93
|
+
"""
|
|
94
|
+
Real-time adaptation engine for security scanning.
|
|
95
|
+
|
|
96
|
+
Monitors request/response patterns and adapts scanning strategy
|
|
97
|
+
to avoid detection and maximize effectiveness.
|
|
98
|
+
|
|
99
|
+
Example:
|
|
100
|
+
adapter = RealTimeAdapter()
|
|
101
|
+
|
|
102
|
+
# Register defense handlers
|
|
103
|
+
adapter.on_waf_detected(lambda d: print(f"WAF: {d.evidence}"))
|
|
104
|
+
|
|
105
|
+
# Process results and adapt
|
|
106
|
+
for url in urls:
|
|
107
|
+
result = await send_request(url)
|
|
108
|
+
strategy = adapter.analyze_result(result)
|
|
109
|
+
|
|
110
|
+
if strategy.action == AdaptationAction.SLOW_DOWN:
|
|
111
|
+
await asyncio.sleep(strategy.parameters["delay_ms"] / 1000)
|
|
112
|
+
elif strategy.action == AdaptationAction.CHANGE_PAYLOAD:
|
|
113
|
+
payload = strategy.parameters["new_payload"]
|
|
114
|
+
"""
|
|
115
|
+
|
|
116
|
+
def __init__(
|
|
117
|
+
self,
|
|
118
|
+
base_delay_ms: int = 100,
|
|
119
|
+
max_delay_ms: int = 5000,
|
|
120
|
+
block_threshold: int = 5,
|
|
121
|
+
rate_limit_threshold: int = 10,
|
|
122
|
+
):
|
|
123
|
+
self.base_delay_ms = base_delay_ms
|
|
124
|
+
self.max_delay_ms = max_delay_ms
|
|
125
|
+
self.block_threshold = block_threshold
|
|
126
|
+
self.rate_limit_threshold = rate_limit_threshold
|
|
127
|
+
|
|
128
|
+
self.state = AdaptationState(current_delay_ms=base_delay_ms)
|
|
129
|
+
self._handlers: dict[DefenseType, list[Callable]] = {}
|
|
130
|
+
self._recent_responses: list[RequestResult] = []
|
|
131
|
+
self._max_recent = 50
|
|
132
|
+
|
|
133
|
+
def analyze_result(self, result: RequestResult) -> AdaptationStrategy:
|
|
134
|
+
"""
|
|
135
|
+
Analyze a request result and determine adaptation strategy.
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
result: The request result to analyze
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
AdaptationStrategy with recommended action
|
|
142
|
+
"""
|
|
143
|
+
self._record_result(result)
|
|
144
|
+
|
|
145
|
+
# Check for various defense indicators
|
|
146
|
+
defenses = self._detect_defenses(result)
|
|
147
|
+
for defense in defenses:
|
|
148
|
+
self._record_defense(defense)
|
|
149
|
+
self._trigger_handlers(defense)
|
|
150
|
+
|
|
151
|
+
# Determine strategy based on detected defenses
|
|
152
|
+
return self._determine_strategy(result, defenses)
|
|
153
|
+
|
|
154
|
+
def _detect_defenses(self, result: RequestResult) -> list[DefenseDetection]:
|
|
155
|
+
"""Detect defensive measures from a response."""
|
|
156
|
+
detections = []
|
|
157
|
+
|
|
158
|
+
# Check for WAF signatures
|
|
159
|
+
waf_detection = self._detect_waf(result)
|
|
160
|
+
if waf_detection:
|
|
161
|
+
detections.append(waf_detection)
|
|
162
|
+
|
|
163
|
+
# Check for rate limiting
|
|
164
|
+
rate_limit = self._detect_rate_limit(result)
|
|
165
|
+
if rate_limit:
|
|
166
|
+
detections.append(rate_limit)
|
|
167
|
+
|
|
168
|
+
# Check for IP blocking
|
|
169
|
+
ip_block = self._detect_ip_block(result)
|
|
170
|
+
if ip_block:
|
|
171
|
+
detections.append(ip_block)
|
|
172
|
+
|
|
173
|
+
# Check for captcha
|
|
174
|
+
captcha = self._detect_captcha(result)
|
|
175
|
+
if captcha:
|
|
176
|
+
detections.append(captcha)
|
|
177
|
+
|
|
178
|
+
return detections
|
|
179
|
+
|
|
180
|
+
def _detect_waf(self, result: RequestResult) -> Optional[DefenseDetection]:
|
|
181
|
+
"""Detect Web Application Firewall signatures."""
|
|
182
|
+
waf_indicators = {
|
|
183
|
+
# Status codes
|
|
184
|
+
"status_403": result.status_code == 403,
|
|
185
|
+
"status_406": result.status_code == 406,
|
|
186
|
+
"status_429": result.status_code == 429,
|
|
187
|
+
"status_503": result.status_code == 503,
|
|
188
|
+
|
|
189
|
+
# Headers
|
|
190
|
+
"cloudflare": any("cloudflare" in v.lower() for v in result.headers.values()),
|
|
191
|
+
"akamai": any("akamai" in v.lower() for v in result.headers.values()),
|
|
192
|
+
"aws_waf": "x-amzn-requestid" in result.headers,
|
|
193
|
+
"mod_security": "mod_security" in str(result.headers).lower(),
|
|
194
|
+
|
|
195
|
+
# Response patterns
|
|
196
|
+
"blocked_keyword": result.blocked,
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
active_indicators = [k for k, v in waf_indicators.items() if v]
|
|
200
|
+
|
|
201
|
+
if active_indicators:
|
|
202
|
+
# Determine WAF name from indicators
|
|
203
|
+
waf_name = "Unknown WAF"
|
|
204
|
+
if "cloudflare" in active_indicators:
|
|
205
|
+
waf_name = "Cloudflare"
|
|
206
|
+
elif "akamai" in active_indicators:
|
|
207
|
+
waf_name = "Akamai"
|
|
208
|
+
elif "aws_waf" in active_indicators:
|
|
209
|
+
waf_name = "AWS WAF"
|
|
210
|
+
elif "mod_security" in active_indicators:
|
|
211
|
+
waf_name = "ModSecurity"
|
|
212
|
+
|
|
213
|
+
return DefenseDetection(
|
|
214
|
+
defense_type=DefenseType.WAF,
|
|
215
|
+
confidence=min(len(active_indicators) * 0.3, 1.0),
|
|
216
|
+
evidence=f"{waf_name} detected via: {', '.join(active_indicators)}",
|
|
217
|
+
metadata={"waf_name": waf_name, "indicators": active_indicators},
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
return None
|
|
221
|
+
|
|
222
|
+
def _detect_rate_limit(self, result: RequestResult) -> Optional[DefenseDetection]:
|
|
223
|
+
"""Detect rate limiting."""
|
|
224
|
+
# Check status code
|
|
225
|
+
if result.status_code == 429:
|
|
226
|
+
retry_after = result.headers.get("retry-after", "unknown")
|
|
227
|
+
return DefenseDetection(
|
|
228
|
+
defense_type=DefenseType.RATE_LIMIT,
|
|
229
|
+
confidence=0.95,
|
|
230
|
+
evidence=f"HTTP 429 received, Retry-After: {retry_after}",
|
|
231
|
+
metadata={"retry_after": retry_after},
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
# Check for consistent slow responses indicating throttling
|
|
235
|
+
recent_times = [r.response_time_ms for r in self._recent_responses[-10:]]
|
|
236
|
+
if len(recent_times) >= 5:
|
|
237
|
+
avg_time = sum(recent_times) / len(recent_times)
|
|
238
|
+
if avg_time > 2000: # > 2 seconds average
|
|
239
|
+
return DefenseDetection(
|
|
240
|
+
defense_type=DefenseType.RATE_LIMIT,
|
|
241
|
+
confidence=0.6,
|
|
242
|
+
evidence=f"Throttling suspected: avg response time {avg_time:.0f}ms",
|
|
243
|
+
metadata={"avg_response_time": avg_time},
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
# Check for repeated blocks
|
|
247
|
+
recent_blocks = sum(1 for r in self._recent_responses[-10:] if r.blocked)
|
|
248
|
+
if recent_blocks >= self.rate_limit_threshold:
|
|
249
|
+
return DefenseDetection(
|
|
250
|
+
defense_type=DefenseType.RATE_LIMIT,
|
|
251
|
+
confidence=0.7,
|
|
252
|
+
evidence=f"{recent_blocks} blocks in last 10 requests",
|
|
253
|
+
metadata={"block_count": recent_blocks},
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
return None
|
|
257
|
+
|
|
258
|
+
def _detect_ip_block(self, result: RequestResult) -> Optional[DefenseDetection]:
|
|
259
|
+
"""Detect IP-based blocking."""
|
|
260
|
+
# Consistent 403s or connection resets suggest IP block
|
|
261
|
+
recent_403s = sum(1 for r in self._recent_responses[-10:] if r.status_code == 403)
|
|
262
|
+
|
|
263
|
+
if recent_403s >= 8:
|
|
264
|
+
return DefenseDetection(
|
|
265
|
+
defense_type=DefenseType.IP_BLOCK,
|
|
266
|
+
confidence=0.8,
|
|
267
|
+
evidence=f"{recent_403s}/10 recent requests returned 403",
|
|
268
|
+
metadata={"consecutive_403s": recent_403s},
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
return None
|
|
272
|
+
|
|
273
|
+
def _detect_captcha(self, result: RequestResult) -> Optional[DefenseDetection]:
|
|
274
|
+
"""Detect CAPTCHA challenges."""
|
|
275
|
+
captcha_indicators = [
|
|
276
|
+
"captcha" in str(result.headers).lower(),
|
|
277
|
+
result.status_code == 503, # Often used with challenges
|
|
278
|
+
]
|
|
279
|
+
|
|
280
|
+
if any(captcha_indicators):
|
|
281
|
+
return DefenseDetection(
|
|
282
|
+
defense_type=DefenseType.CAPTCHA,
|
|
283
|
+
confidence=0.7,
|
|
284
|
+
evidence="CAPTCHA challenge detected",
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
return None
|
|
288
|
+
|
|
289
|
+
def _determine_strategy(
|
|
290
|
+
self,
|
|
291
|
+
result: RequestResult,
|
|
292
|
+
defenses: list[DefenseDetection],
|
|
293
|
+
) -> AdaptationStrategy:
|
|
294
|
+
"""Determine the best adaptation strategy."""
|
|
295
|
+
# Priority: IP Block > Rate Limit > WAF > Continue
|
|
296
|
+
|
|
297
|
+
# Check for IP block - most severe
|
|
298
|
+
ip_blocks = [d for d in defenses if d.defense_type == DefenseType.IP_BLOCK]
|
|
299
|
+
if ip_blocks and ip_blocks[0].confidence > 0.7:
|
|
300
|
+
return AdaptationStrategy(
|
|
301
|
+
action=AdaptationAction.USE_PROXY,
|
|
302
|
+
parameters={"reason": "IP appears blocked"},
|
|
303
|
+
reason="IP blocking detected with high confidence",
|
|
304
|
+
expected_outcome="Requests should succeed from new IP",
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
# Check for rate limiting
|
|
308
|
+
rate_limits = [d for d in defenses if d.defense_type == DefenseType.RATE_LIMIT]
|
|
309
|
+
if rate_limits:
|
|
310
|
+
rl = rate_limits[0]
|
|
311
|
+
new_delay = min(self.state.current_delay_ms * 2, self.max_delay_ms)
|
|
312
|
+
|
|
313
|
+
# Check for Retry-After header
|
|
314
|
+
retry_after = rl.metadata.get("retry_after")
|
|
315
|
+
if retry_after and retry_after != "unknown":
|
|
316
|
+
try:
|
|
317
|
+
new_delay = int(retry_after) * 1000
|
|
318
|
+
except ValueError:
|
|
319
|
+
pass
|
|
320
|
+
|
|
321
|
+
self.state.current_delay_ms = new_delay
|
|
322
|
+
self.state.is_rate_limited = True
|
|
323
|
+
|
|
324
|
+
return AdaptationStrategy(
|
|
325
|
+
action=AdaptationAction.SLOW_DOWN,
|
|
326
|
+
parameters={"delay_ms": new_delay},
|
|
327
|
+
reason=f"Rate limiting detected: {rl.evidence}",
|
|
328
|
+
expected_outcome=f"Delay increased to {new_delay}ms between requests",
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
# Check for WAF blocking
|
|
332
|
+
wafs = [d for d in defenses if d.defense_type == DefenseType.WAF]
|
|
333
|
+
if wafs and result.blocked:
|
|
334
|
+
waf = wafs[0]
|
|
335
|
+
waf_name = waf.metadata.get("waf_name", "Unknown")
|
|
336
|
+
|
|
337
|
+
return AdaptationStrategy(
|
|
338
|
+
action=AdaptationAction.CHANGE_PAYLOAD,
|
|
339
|
+
parameters={
|
|
340
|
+
"waf_name": waf_name,
|
|
341
|
+
"bypass_techniques": self._get_waf_bypass_techniques(waf_name),
|
|
342
|
+
},
|
|
343
|
+
reason=f"WAF blocking detected: {waf_name}",
|
|
344
|
+
expected_outcome="Payload modified to bypass WAF",
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
# Check for CAPTCHA
|
|
348
|
+
captchas = [d for d in defenses if d.defense_type == DefenseType.CAPTCHA]
|
|
349
|
+
if captchas:
|
|
350
|
+
return AdaptationStrategy(
|
|
351
|
+
action=AdaptationAction.SKIP_ENDPOINT,
|
|
352
|
+
parameters={"reason": "CAPTCHA required"},
|
|
353
|
+
reason="CAPTCHA challenge cannot be bypassed automatically",
|
|
354
|
+
expected_outcome="Endpoint skipped, manual testing recommended",
|
|
355
|
+
)
|
|
356
|
+
|
|
357
|
+
# No defenses - check if we can speed up
|
|
358
|
+
if (self.state.is_rate_limited and
|
|
359
|
+
self.state.blocked_count == 0 and
|
|
360
|
+
len(self._recent_responses) >= 10):
|
|
361
|
+
|
|
362
|
+
# No blocks in recent requests, try reducing delay
|
|
363
|
+
new_delay = max(self.base_delay_ms, self.state.current_delay_ms // 2)
|
|
364
|
+
self.state.current_delay_ms = new_delay
|
|
365
|
+
|
|
366
|
+
if new_delay == self.base_delay_ms:
|
|
367
|
+
self.state.is_rate_limited = False
|
|
368
|
+
|
|
369
|
+
# Default: continue with current settings
|
|
370
|
+
return AdaptationStrategy(
|
|
371
|
+
action=AdaptationAction.SLOW_DOWN,
|
|
372
|
+
parameters={"delay_ms": self.state.current_delay_ms},
|
|
373
|
+
reason="Maintaining current pace",
|
|
374
|
+
expected_outcome="Continue scanning",
|
|
375
|
+
)
|
|
376
|
+
|
|
377
|
+
def _get_waf_bypass_techniques(self, waf_name: str) -> list[str]:
|
|
378
|
+
"""Get recommended WAF bypass techniques."""
|
|
379
|
+
techniques = {
|
|
380
|
+
"Cloudflare": [
|
|
381
|
+
"double_url_encode",
|
|
382
|
+
"unicode_normalization",
|
|
383
|
+
"case_variation",
|
|
384
|
+
"comment_injection",
|
|
385
|
+
],
|
|
386
|
+
"Akamai": [
|
|
387
|
+
"unicode_normalization",
|
|
388
|
+
"parameter_pollution",
|
|
389
|
+
"json_payload",
|
|
390
|
+
],
|
|
391
|
+
"AWS WAF": [
|
|
392
|
+
"double_url_encode",
|
|
393
|
+
"null_byte_injection",
|
|
394
|
+
],
|
|
395
|
+
"ModSecurity": [
|
|
396
|
+
"comment_injection",
|
|
397
|
+
"case_variation",
|
|
398
|
+
"chunked_encoding",
|
|
399
|
+
],
|
|
400
|
+
}
|
|
401
|
+
return techniques.get(waf_name, ["encoding_variation", "case_variation"])
|
|
402
|
+
|
|
403
|
+
def _record_result(self, result: RequestResult):
|
|
404
|
+
"""Record a request result for analysis."""
|
|
405
|
+
self._recent_responses.append(result)
|
|
406
|
+
if len(self._recent_responses) > self._max_recent:
|
|
407
|
+
self._recent_responses.pop(0)
|
|
408
|
+
|
|
409
|
+
self.state.request_count += 1
|
|
410
|
+
if result.blocked:
|
|
411
|
+
self.state.blocked_count += 1
|
|
412
|
+
elif result.status_code == 200:
|
|
413
|
+
self.state.success_count += 1
|
|
414
|
+
|
|
415
|
+
self.state.last_request_time = datetime.utcnow()
|
|
416
|
+
|
|
417
|
+
def _record_defense(self, defense: DefenseDetection):
|
|
418
|
+
"""Record a detected defense."""
|
|
419
|
+
self.state.detected_defenses.append(defense)
|
|
420
|
+
|
|
421
|
+
def on_waf_detected(self, handler: Callable[[DefenseDetection], None]):
|
|
422
|
+
"""Register handler for WAF detection."""
|
|
423
|
+
self._register_handler(DefenseType.WAF, handler)
|
|
424
|
+
|
|
425
|
+
def on_rate_limit(self, handler: Callable[[DefenseDetection], None]):
|
|
426
|
+
"""Register handler for rate limit detection."""
|
|
427
|
+
self._register_handler(DefenseType.RATE_LIMIT, handler)
|
|
428
|
+
|
|
429
|
+
def on_ip_block(self, handler: Callable[[DefenseDetection], None]):
|
|
430
|
+
"""Register handler for IP block detection."""
|
|
431
|
+
self._register_handler(DefenseType.IP_BLOCK, handler)
|
|
432
|
+
|
|
433
|
+
def _register_handler(self, defense_type: DefenseType, handler: Callable):
|
|
434
|
+
"""Register a handler for a defense type."""
|
|
435
|
+
if defense_type not in self._handlers:
|
|
436
|
+
self._handlers[defense_type] = []
|
|
437
|
+
self._handlers[defense_type].append(handler)
|
|
438
|
+
|
|
439
|
+
def _trigger_handlers(self, defense: DefenseDetection):
|
|
440
|
+
"""Trigger handlers for a detected defense."""
|
|
441
|
+
handlers = self._handlers.get(defense.defense_type, [])
|
|
442
|
+
for handler in handlers:
|
|
443
|
+
try:
|
|
444
|
+
handler(defense)
|
|
445
|
+
except Exception as e:
|
|
446
|
+
logger.warning(f"Handler error: {e}")
|
|
447
|
+
|
|
448
|
+
def get_current_delay(self) -> int:
|
|
449
|
+
"""Get the current recommended delay in milliseconds."""
|
|
450
|
+
return self.state.current_delay_ms
|
|
451
|
+
|
|
452
|
+
def get_statistics(self) -> dict[str, Any]:
|
|
453
|
+
"""Get adaptation statistics."""
|
|
454
|
+
return {
|
|
455
|
+
"total_requests": self.state.request_count,
|
|
456
|
+
"blocked_requests": self.state.blocked_count,
|
|
457
|
+
"successful_requests": self.state.success_count,
|
|
458
|
+
"block_rate": self.state.blocked_count / max(1, self.state.request_count),
|
|
459
|
+
"current_delay_ms": self.state.current_delay_ms,
|
|
460
|
+
"is_rate_limited": self.state.is_rate_limited,
|
|
461
|
+
"detected_defenses": [
|
|
462
|
+
{
|
|
463
|
+
"type": d.defense_type.value,
|
|
464
|
+
"confidence": d.confidence,
|
|
465
|
+
"evidence": d.evidence,
|
|
466
|
+
}
|
|
467
|
+
for d in self.state.detected_defenses[-10:]
|
|
468
|
+
],
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
def reset(self):
|
|
472
|
+
"""Reset adaptation state."""
|
|
473
|
+
self.state = AdaptationState(current_delay_ms=self.base_delay_ms)
|
|
474
|
+
self._recent_responses.clear()
|