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,272 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Request Obfuscator
|
|
3
|
+
|
|
4
|
+
Mutates HTTP requests to evade detection:
|
|
5
|
+
- Header manipulation
|
|
6
|
+
- Parameter encoding
|
|
7
|
+
- Request body obfuscation
|
|
8
|
+
- HTTP method alternatives
|
|
9
|
+
|
|
10
|
+
Usage:
|
|
11
|
+
from aipt_v2.evasion import RequestObfuscator
|
|
12
|
+
|
|
13
|
+
obfuscator = RequestObfuscator()
|
|
14
|
+
modified = obfuscator.obfuscate(request)
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import random
|
|
18
|
+
import string
|
|
19
|
+
import urllib.parse
|
|
20
|
+
from dataclasses import dataclass, field
|
|
21
|
+
from typing import Dict, List, Optional, Any
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass
|
|
25
|
+
class ObfuscationConfig:
|
|
26
|
+
"""Configuration for request obfuscation."""
|
|
27
|
+
encode_parameters: bool = True
|
|
28
|
+
randomize_case: bool = True
|
|
29
|
+
add_junk_headers: bool = True
|
|
30
|
+
add_junk_parameters: bool = False
|
|
31
|
+
pad_content_length: bool = False
|
|
32
|
+
use_http_methods_override: bool = False
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass
|
|
36
|
+
class ObfuscatedRequest:
|
|
37
|
+
"""Obfuscated HTTP request."""
|
|
38
|
+
method: str
|
|
39
|
+
url: str
|
|
40
|
+
headers: Dict[str, str]
|
|
41
|
+
body: Optional[str]
|
|
42
|
+
params: Dict[str, str]
|
|
43
|
+
modifications: List[str]
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class RequestObfuscator:
|
|
47
|
+
"""
|
|
48
|
+
HTTP Request Obfuscator.
|
|
49
|
+
|
|
50
|
+
Applies various obfuscation techniques to HTTP requests
|
|
51
|
+
to evade detection and bypass security controls.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
# Junk headers that are typically ignored
|
|
55
|
+
JUNK_HEADERS = [
|
|
56
|
+
"X-Forwarded-For", "X-Real-IP", "X-Originating-IP",
|
|
57
|
+
"X-Client-IP", "CF-Connecting-IP", "True-Client-IP",
|
|
58
|
+
"X-Custom-Header", "X-Debug", "X-Request-ID",
|
|
59
|
+
"X-Correlation-ID", "X-Trace-ID"
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
# Content-Type variations
|
|
63
|
+
CONTENT_TYPES = [
|
|
64
|
+
"application/json",
|
|
65
|
+
"application/x-www-form-urlencoded",
|
|
66
|
+
"text/plain",
|
|
67
|
+
"application/xml",
|
|
68
|
+
"multipart/form-data"
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
def __init__(self, config: Optional[ObfuscationConfig] = None):
|
|
72
|
+
"""Initialize obfuscator."""
|
|
73
|
+
self.config = config or ObfuscationConfig()
|
|
74
|
+
|
|
75
|
+
def _random_string(self, length: int = 8) -> str:
|
|
76
|
+
"""Generate random string."""
|
|
77
|
+
return "".join(random.choices(string.ascii_letters + string.digits, k=length))
|
|
78
|
+
|
|
79
|
+
def _random_ip(self) -> str:
|
|
80
|
+
"""Generate random IP address."""
|
|
81
|
+
return ".".join(str(random.randint(1, 254)) for _ in range(4))
|
|
82
|
+
|
|
83
|
+
def encode_parameter(self, value: str, encoding: str = "url") -> str:
|
|
84
|
+
"""
|
|
85
|
+
Encode parameter value.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
value: Original value
|
|
89
|
+
encoding: Encoding type (url, double_url, unicode)
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
Encoded value
|
|
93
|
+
"""
|
|
94
|
+
if encoding == "url":
|
|
95
|
+
return urllib.parse.quote(value, safe="")
|
|
96
|
+
elif encoding == "double_url":
|
|
97
|
+
return urllib.parse.quote(urllib.parse.quote(value, safe=""), safe="")
|
|
98
|
+
elif encoding == "unicode":
|
|
99
|
+
return "".join(f"%u00{ord(c):02x}" if c.isalpha() else c for c in value)
|
|
100
|
+
return value
|
|
101
|
+
|
|
102
|
+
def randomize_header_case(self, headers: Dict[str, str]) -> Dict[str, str]:
|
|
103
|
+
"""Randomize header name case."""
|
|
104
|
+
new_headers = {}
|
|
105
|
+
for name, value in headers.items():
|
|
106
|
+
# Random case for header name
|
|
107
|
+
new_name = "".join(
|
|
108
|
+
c.upper() if random.random() > 0.5 else c.lower()
|
|
109
|
+
for c in name
|
|
110
|
+
)
|
|
111
|
+
new_headers[new_name] = value
|
|
112
|
+
return new_headers
|
|
113
|
+
|
|
114
|
+
def add_junk_headers(self, headers: Dict[str, str]) -> Dict[str, str]:
|
|
115
|
+
"""Add junk/decoy headers."""
|
|
116
|
+
new_headers = headers.copy()
|
|
117
|
+
|
|
118
|
+
# Add random IP headers
|
|
119
|
+
ip_headers = ["X-Forwarded-For", "X-Real-IP", "X-Client-IP"]
|
|
120
|
+
for header in random.sample(ip_headers, random.randint(1, 2)):
|
|
121
|
+
new_headers[header] = self._random_ip()
|
|
122
|
+
|
|
123
|
+
# Add random custom headers
|
|
124
|
+
for _ in range(random.randint(1, 3)):
|
|
125
|
+
header_name = f"X-{self._random_string(6)}"
|
|
126
|
+
new_headers[header_name] = self._random_string(12)
|
|
127
|
+
|
|
128
|
+
return new_headers
|
|
129
|
+
|
|
130
|
+
def add_junk_parameters(self, params: Dict[str, str]) -> Dict[str, str]:
|
|
131
|
+
"""Add junk parameters to request."""
|
|
132
|
+
new_params = params.copy()
|
|
133
|
+
|
|
134
|
+
for _ in range(random.randint(1, 3)):
|
|
135
|
+
param_name = self._random_string(6)
|
|
136
|
+
new_params[param_name] = self._random_string(8)
|
|
137
|
+
|
|
138
|
+
return new_params
|
|
139
|
+
|
|
140
|
+
def obfuscate_url(self, url: str) -> str:
|
|
141
|
+
"""Obfuscate URL path."""
|
|
142
|
+
# Add path segments that resolve to same path
|
|
143
|
+
path_tricks = [
|
|
144
|
+
"/./", # Current directory
|
|
145
|
+
"/../..", # Parent then back
|
|
146
|
+
"//", # Double slash
|
|
147
|
+
]
|
|
148
|
+
|
|
149
|
+
# Random insertion point
|
|
150
|
+
if "/" in url:
|
|
151
|
+
parts = url.split("/")
|
|
152
|
+
if len(parts) > 2:
|
|
153
|
+
insert_idx = random.randint(1, len(parts) - 1)
|
|
154
|
+
trick = random.choice(path_tricks)
|
|
155
|
+
parts.insert(insert_idx, trick.strip("/"))
|
|
156
|
+
url = "/".join(parts)
|
|
157
|
+
|
|
158
|
+
return url
|
|
159
|
+
|
|
160
|
+
def use_method_override(
|
|
161
|
+
self,
|
|
162
|
+
method: str,
|
|
163
|
+
headers: Dict[str, str]
|
|
164
|
+
) -> tuple:
|
|
165
|
+
"""
|
|
166
|
+
Use HTTP method override headers.
|
|
167
|
+
|
|
168
|
+
Some applications accept X-HTTP-Method-Override
|
|
169
|
+
to change the actual method.
|
|
170
|
+
"""
|
|
171
|
+
override_headers = [
|
|
172
|
+
"X-HTTP-Method-Override",
|
|
173
|
+
"X-HTTP-Method",
|
|
174
|
+
"X-Method-Override",
|
|
175
|
+
"_method"
|
|
176
|
+
]
|
|
177
|
+
|
|
178
|
+
new_headers = headers.copy()
|
|
179
|
+
|
|
180
|
+
# Use POST with override header
|
|
181
|
+
header_name = random.choice(override_headers)
|
|
182
|
+
new_headers[header_name] = method
|
|
183
|
+
|
|
184
|
+
return "POST", new_headers
|
|
185
|
+
|
|
186
|
+
def obfuscate(
|
|
187
|
+
self,
|
|
188
|
+
method: str,
|
|
189
|
+
url: str,
|
|
190
|
+
headers: Dict[str, str] = None,
|
|
191
|
+
params: Dict[str, str] = None,
|
|
192
|
+
body: str = None
|
|
193
|
+
) -> ObfuscatedRequest:
|
|
194
|
+
"""
|
|
195
|
+
Obfuscate HTTP request.
|
|
196
|
+
|
|
197
|
+
Args:
|
|
198
|
+
method: HTTP method
|
|
199
|
+
url: Request URL
|
|
200
|
+
headers: Request headers
|
|
201
|
+
params: Query parameters
|
|
202
|
+
body: Request body
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
ObfuscatedRequest
|
|
206
|
+
"""
|
|
207
|
+
headers = headers or {}
|
|
208
|
+
params = params or {}
|
|
209
|
+
modifications = []
|
|
210
|
+
|
|
211
|
+
# Encode parameters
|
|
212
|
+
if self.config.encode_parameters and params:
|
|
213
|
+
encoded_params = {}
|
|
214
|
+
for key, value in params.items():
|
|
215
|
+
encoding = random.choice(["url", "double_url", "unicode"])
|
|
216
|
+
encoded_params[key] = self.encode_parameter(value, encoding)
|
|
217
|
+
params = encoded_params
|
|
218
|
+
modifications.append("parameter_encoding")
|
|
219
|
+
|
|
220
|
+
# Randomize header case
|
|
221
|
+
if self.config.randomize_case:
|
|
222
|
+
headers = self.randomize_header_case(headers)
|
|
223
|
+
modifications.append("header_case_randomization")
|
|
224
|
+
|
|
225
|
+
# Add junk headers
|
|
226
|
+
if self.config.add_junk_headers:
|
|
227
|
+
headers = self.add_junk_headers(headers)
|
|
228
|
+
modifications.append("junk_headers_added")
|
|
229
|
+
|
|
230
|
+
# Add junk parameters
|
|
231
|
+
if self.config.add_junk_parameters:
|
|
232
|
+
params = self.add_junk_parameters(params)
|
|
233
|
+
modifications.append("junk_parameters_added")
|
|
234
|
+
|
|
235
|
+
# Method override
|
|
236
|
+
if self.config.use_http_methods_override and method in ["PUT", "DELETE", "PATCH"]:
|
|
237
|
+
method, headers = self.use_method_override(method, headers)
|
|
238
|
+
modifications.append("method_override")
|
|
239
|
+
|
|
240
|
+
return ObfuscatedRequest(
|
|
241
|
+
method=method,
|
|
242
|
+
url=url,
|
|
243
|
+
headers=headers,
|
|
244
|
+
body=body,
|
|
245
|
+
params=params,
|
|
246
|
+
modifications=modifications
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
# Convenience function
|
|
251
|
+
def obfuscate_request(
|
|
252
|
+
method: str,
|
|
253
|
+
url: str,
|
|
254
|
+
headers: Dict[str, str] = None,
|
|
255
|
+
params: Dict[str, str] = None,
|
|
256
|
+
body: str = None
|
|
257
|
+
) -> ObfuscatedRequest:
|
|
258
|
+
"""
|
|
259
|
+
Obfuscate HTTP request.
|
|
260
|
+
|
|
261
|
+
Args:
|
|
262
|
+
method: HTTP method
|
|
263
|
+
url: Request URL
|
|
264
|
+
headers: Request headers
|
|
265
|
+
params: Query parameters
|
|
266
|
+
body: Request body
|
|
267
|
+
|
|
268
|
+
Returns:
|
|
269
|
+
ObfuscatedRequest
|
|
270
|
+
"""
|
|
271
|
+
obfuscator = RequestObfuscator()
|
|
272
|
+
return obfuscator.obfuscate(method, url, headers, params, body)
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
"""
|
|
2
|
+
TLS Fingerprint Randomization
|
|
3
|
+
|
|
4
|
+
Randomizes TLS fingerprint (JA3/JA3S) to evade detection:
|
|
5
|
+
- Cipher suite ordering
|
|
6
|
+
- TLS extension manipulation
|
|
7
|
+
- ALPN protocol ordering
|
|
8
|
+
- Supported versions randomization
|
|
9
|
+
|
|
10
|
+
Usage:
|
|
11
|
+
from aipt_v2.evasion import TLSFingerprint
|
|
12
|
+
|
|
13
|
+
tls = TLSFingerprint()
|
|
14
|
+
context = tls.get_randomized_context()
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import ssl
|
|
18
|
+
import random
|
|
19
|
+
from dataclasses import dataclass
|
|
20
|
+
from typing import List, Optional, Dict, Any
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass
|
|
24
|
+
class TLSProfile:
|
|
25
|
+
"""TLS connection profile."""
|
|
26
|
+
name: str
|
|
27
|
+
ciphers: List[str]
|
|
28
|
+
protocols: List[str]
|
|
29
|
+
extensions: List[str]
|
|
30
|
+
alpn: List[str]
|
|
31
|
+
ja3_hash: str = ""
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class TLSFingerprint:
|
|
35
|
+
"""
|
|
36
|
+
TLS Fingerprint Randomization.
|
|
37
|
+
|
|
38
|
+
Modifies TLS connection parameters to evade
|
|
39
|
+
JA3/JA3S fingerprint detection.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
# Common cipher suites
|
|
43
|
+
CIPHER_SUITES = [
|
|
44
|
+
"TLS_AES_128_GCM_SHA256",
|
|
45
|
+
"TLS_AES_256_GCM_SHA384",
|
|
46
|
+
"TLS_CHACHA20_POLY1305_SHA256",
|
|
47
|
+
"ECDHE-ECDSA-AES128-GCM-SHA256",
|
|
48
|
+
"ECDHE-RSA-AES128-GCM-SHA256",
|
|
49
|
+
"ECDHE-ECDSA-AES256-GCM-SHA384",
|
|
50
|
+
"ECDHE-RSA-AES256-GCM-SHA384",
|
|
51
|
+
"ECDHE-ECDSA-CHACHA20-POLY1305",
|
|
52
|
+
"ECDHE-RSA-CHACHA20-POLY1305",
|
|
53
|
+
"ECDHE-RSA-AES128-SHA",
|
|
54
|
+
"ECDHE-RSA-AES256-SHA",
|
|
55
|
+
"AES128-GCM-SHA256",
|
|
56
|
+
"AES256-GCM-SHA384",
|
|
57
|
+
"AES128-SHA",
|
|
58
|
+
"AES256-SHA",
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
# Browser-like cipher ordering
|
|
62
|
+
BROWSER_PROFILES = {
|
|
63
|
+
"chrome": [
|
|
64
|
+
"TLS_AES_128_GCM_SHA256",
|
|
65
|
+
"TLS_AES_256_GCM_SHA384",
|
|
66
|
+
"TLS_CHACHA20_POLY1305_SHA256",
|
|
67
|
+
"ECDHE-ECDSA-AES128-GCM-SHA256",
|
|
68
|
+
"ECDHE-RSA-AES128-GCM-SHA256",
|
|
69
|
+
"ECDHE-ECDSA-AES256-GCM-SHA384",
|
|
70
|
+
"ECDHE-RSA-AES256-GCM-SHA384",
|
|
71
|
+
],
|
|
72
|
+
"firefox": [
|
|
73
|
+
"TLS_AES_128_GCM_SHA256",
|
|
74
|
+
"TLS_CHACHA20_POLY1305_SHA256",
|
|
75
|
+
"TLS_AES_256_GCM_SHA384",
|
|
76
|
+
"ECDHE-ECDSA-AES128-GCM-SHA256",
|
|
77
|
+
"ECDHE-RSA-AES128-GCM-SHA256",
|
|
78
|
+
"ECDHE-ECDSA-CHACHA20-POLY1305",
|
|
79
|
+
"ECDHE-RSA-CHACHA20-POLY1305",
|
|
80
|
+
],
|
|
81
|
+
"safari": [
|
|
82
|
+
"TLS_AES_128_GCM_SHA256",
|
|
83
|
+
"TLS_AES_256_GCM_SHA384",
|
|
84
|
+
"TLS_CHACHA20_POLY1305_SHA256",
|
|
85
|
+
"ECDHE-ECDSA-AES256-GCM-SHA384",
|
|
86
|
+
"ECDHE-ECDSA-AES128-GCM-SHA256",
|
|
87
|
+
"ECDHE-RSA-AES256-GCM-SHA384",
|
|
88
|
+
"ECDHE-RSA-AES128-GCM-SHA256",
|
|
89
|
+
],
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
# ALPN protocols
|
|
93
|
+
ALPN_PROTOCOLS = ["h2", "http/1.1"]
|
|
94
|
+
|
|
95
|
+
def __init__(self):
|
|
96
|
+
"""Initialize TLS fingerprint handler."""
|
|
97
|
+
self.profiles = self._create_profiles()
|
|
98
|
+
|
|
99
|
+
def _create_profiles(self) -> Dict[str, TLSProfile]:
|
|
100
|
+
"""Create TLS profiles for different browsers."""
|
|
101
|
+
return {
|
|
102
|
+
"chrome": TLSProfile(
|
|
103
|
+
name="Chrome",
|
|
104
|
+
ciphers=self.BROWSER_PROFILES["chrome"],
|
|
105
|
+
protocols=["TLSv1.2", "TLSv1.3"],
|
|
106
|
+
extensions=["server_name", "ec_point_formats", "supported_groups"],
|
|
107
|
+
alpn=["h2", "http/1.1"]
|
|
108
|
+
),
|
|
109
|
+
"firefox": TLSProfile(
|
|
110
|
+
name="Firefox",
|
|
111
|
+
ciphers=self.BROWSER_PROFILES["firefox"],
|
|
112
|
+
protocols=["TLSv1.2", "TLSv1.3"],
|
|
113
|
+
extensions=["server_name", "supported_groups", "ec_point_formats"],
|
|
114
|
+
alpn=["h2", "http/1.1"]
|
|
115
|
+
),
|
|
116
|
+
"safari": TLSProfile(
|
|
117
|
+
name="Safari",
|
|
118
|
+
ciphers=self.BROWSER_PROFILES["safari"],
|
|
119
|
+
protocols=["TLSv1.2", "TLSv1.3"],
|
|
120
|
+
extensions=["server_name", "ec_point_formats", "supported_groups"],
|
|
121
|
+
alpn=["h2", "http/1.1"]
|
|
122
|
+
),
|
|
123
|
+
"random": TLSProfile(
|
|
124
|
+
name="Random",
|
|
125
|
+
ciphers=self.CIPHER_SUITES.copy(),
|
|
126
|
+
protocols=["TLSv1.2", "TLSv1.3"],
|
|
127
|
+
extensions=["server_name"],
|
|
128
|
+
alpn=["h2", "http/1.1"]
|
|
129
|
+
),
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
def get_profile(self, name: str = "random") -> TLSProfile:
|
|
133
|
+
"""
|
|
134
|
+
Get TLS profile.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
name: Profile name (chrome, firefox, safari, random)
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
TLSProfile
|
|
141
|
+
"""
|
|
142
|
+
return self.profiles.get(name, self.profiles["random"])
|
|
143
|
+
|
|
144
|
+
def randomize_ciphers(self, base_ciphers: List[str] = None) -> List[str]:
|
|
145
|
+
"""
|
|
146
|
+
Randomize cipher suite order.
|
|
147
|
+
|
|
148
|
+
Args:
|
|
149
|
+
base_ciphers: Base cipher list
|
|
150
|
+
|
|
151
|
+
Returns:
|
|
152
|
+
Randomized cipher list
|
|
153
|
+
"""
|
|
154
|
+
ciphers = base_ciphers or self.CIPHER_SUITES.copy()
|
|
155
|
+
|
|
156
|
+
# Keep TLS 1.3 ciphers at top but shuffle others
|
|
157
|
+
tls13_ciphers = [c for c in ciphers if c.startswith("TLS_")]
|
|
158
|
+
other_ciphers = [c for c in ciphers if not c.startswith("TLS_")]
|
|
159
|
+
|
|
160
|
+
random.shuffle(other_ciphers)
|
|
161
|
+
|
|
162
|
+
return tls13_ciphers + other_ciphers
|
|
163
|
+
|
|
164
|
+
def randomize_alpn(self) -> List[str]:
|
|
165
|
+
"""
|
|
166
|
+
Randomize ALPN protocol order.
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
Randomized ALPN list
|
|
170
|
+
"""
|
|
171
|
+
alpn = self.ALPN_PROTOCOLS.copy()
|
|
172
|
+
if random.random() > 0.5:
|
|
173
|
+
alpn.reverse()
|
|
174
|
+
return alpn
|
|
175
|
+
|
|
176
|
+
def create_ssl_context(
|
|
177
|
+
self,
|
|
178
|
+
profile: str = "random",
|
|
179
|
+
verify: bool = True
|
|
180
|
+
) -> ssl.SSLContext:
|
|
181
|
+
"""
|
|
182
|
+
Create SSL context with randomized fingerprint.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
profile: TLS profile to use
|
|
186
|
+
verify: Verify certificates
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
Configured SSLContext
|
|
190
|
+
"""
|
|
191
|
+
context = ssl.create_default_context()
|
|
192
|
+
|
|
193
|
+
if not verify:
|
|
194
|
+
context.check_hostname = False
|
|
195
|
+
context.verify_mode = ssl.CERT_NONE
|
|
196
|
+
|
|
197
|
+
# Get profile
|
|
198
|
+
tls_profile = self.get_profile(profile)
|
|
199
|
+
|
|
200
|
+
# Set ciphers
|
|
201
|
+
if profile == "random":
|
|
202
|
+
ciphers = self.randomize_ciphers(tls_profile.ciphers)
|
|
203
|
+
else:
|
|
204
|
+
ciphers = tls_profile.ciphers
|
|
205
|
+
|
|
206
|
+
try:
|
|
207
|
+
cipher_string = ":".join(ciphers)
|
|
208
|
+
context.set_ciphers(cipher_string)
|
|
209
|
+
except ssl.SSLError:
|
|
210
|
+
# Fall back to default if cipher setting fails
|
|
211
|
+
pass
|
|
212
|
+
|
|
213
|
+
# Set minimum TLS version
|
|
214
|
+
context.minimum_version = ssl.TLSVersion.TLSv1_2
|
|
215
|
+
|
|
216
|
+
# Set ALPN protocols
|
|
217
|
+
try:
|
|
218
|
+
alpn = self.randomize_alpn() if profile == "random" else tls_profile.alpn
|
|
219
|
+
context.set_alpn_protocols(alpn)
|
|
220
|
+
except (ssl.SSLError, AttributeError):
|
|
221
|
+
pass
|
|
222
|
+
|
|
223
|
+
return context
|
|
224
|
+
|
|
225
|
+
def get_randomized_context(self, verify: bool = True) -> ssl.SSLContext:
|
|
226
|
+
"""
|
|
227
|
+
Get SSL context with randomized fingerprint.
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
verify: Verify certificates
|
|
231
|
+
|
|
232
|
+
Returns:
|
|
233
|
+
Randomized SSLContext
|
|
234
|
+
"""
|
|
235
|
+
# Randomly pick a browser profile
|
|
236
|
+
profile = random.choice(["chrome", "firefox", "safari", "random"])
|
|
237
|
+
return self.create_ssl_context(profile, verify)
|
|
238
|
+
|
|
239
|
+
def get_cipher_string(self, profile: str = "random") -> str:
|
|
240
|
+
"""
|
|
241
|
+
Get cipher string for requests/urllib3.
|
|
242
|
+
|
|
243
|
+
Args:
|
|
244
|
+
profile: TLS profile
|
|
245
|
+
|
|
246
|
+
Returns:
|
|
247
|
+
Cipher string
|
|
248
|
+
"""
|
|
249
|
+
tls_profile = self.get_profile(profile)
|
|
250
|
+
|
|
251
|
+
if profile == "random":
|
|
252
|
+
ciphers = self.randomize_ciphers(tls_profile.ciphers)
|
|
253
|
+
else:
|
|
254
|
+
ciphers = tls_profile.ciphers
|
|
255
|
+
|
|
256
|
+
return ":".join(ciphers)
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def randomize_tls(verify: bool = True) -> ssl.SSLContext:
|
|
260
|
+
"""
|
|
261
|
+
Get randomized TLS context.
|
|
262
|
+
|
|
263
|
+
Args:
|
|
264
|
+
verify: Verify certificates
|
|
265
|
+
|
|
266
|
+
Returns:
|
|
267
|
+
Randomized SSLContext
|
|
268
|
+
"""
|
|
269
|
+
tls = TLSFingerprint()
|
|
270
|
+
return tls.get_randomized_context(verify)
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def get_browser_tls(browser: str = "chrome", verify: bool = True) -> ssl.SSLContext:
|
|
274
|
+
"""
|
|
275
|
+
Get browser-like TLS context.
|
|
276
|
+
|
|
277
|
+
Args:
|
|
278
|
+
browser: Browser name (chrome, firefox, safari)
|
|
279
|
+
verify: Verify certificates
|
|
280
|
+
|
|
281
|
+
Returns:
|
|
282
|
+
Browser-like SSLContext
|
|
283
|
+
"""
|
|
284
|
+
tls = TLSFingerprint()
|
|
285
|
+
return tls.create_ssl_context(browser, verify)
|