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,350 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AIPT Docker Sandbox
|
|
3
|
+
|
|
4
|
+
Isolated command execution in Docker containers for safety.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import asyncio
|
|
9
|
+
import logging
|
|
10
|
+
import os
|
|
11
|
+
import tempfile
|
|
12
|
+
from dataclasses import dataclass, field
|
|
13
|
+
from datetime import datetime
|
|
14
|
+
from typing import Optional
|
|
15
|
+
|
|
16
|
+
from .executor import CommandResult, ExecutionStatus
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class SandboxConfig:
|
|
23
|
+
"""Docker sandbox configuration"""
|
|
24
|
+
image: str = "python:3.11-slim" # Base image
|
|
25
|
+
network_mode: str = "bridge" # none, bridge, host
|
|
26
|
+
memory_limit: str = "512m"
|
|
27
|
+
cpu_limit: float = 1.0
|
|
28
|
+
timeout: float = 300.0
|
|
29
|
+
|
|
30
|
+
# Volume mounts
|
|
31
|
+
mount_workspace: bool = True
|
|
32
|
+
workspace_path: str = "/workspace"
|
|
33
|
+
read_only_root: bool = True
|
|
34
|
+
|
|
35
|
+
# Security
|
|
36
|
+
no_new_privileges: bool = True
|
|
37
|
+
drop_capabilities: list[str] = field(default_factory=lambda: ["ALL"])
|
|
38
|
+
add_capabilities: list[str] = field(default_factory=list)
|
|
39
|
+
|
|
40
|
+
# Cleanup
|
|
41
|
+
auto_remove: bool = True
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class DockerSandbox:
|
|
45
|
+
"""
|
|
46
|
+
Docker-based sandbox for isolated command execution.
|
|
47
|
+
|
|
48
|
+
Provides secure execution environment with:
|
|
49
|
+
- Network isolation
|
|
50
|
+
- Resource limits
|
|
51
|
+
- Filesystem isolation
|
|
52
|
+
- Capability dropping
|
|
53
|
+
|
|
54
|
+
Example:
|
|
55
|
+
sandbox = DockerSandbox()
|
|
56
|
+
await sandbox.start()
|
|
57
|
+
result = await sandbox.execute("python exploit.py")
|
|
58
|
+
await sandbox.stop()
|
|
59
|
+
|
|
60
|
+
# Or use context manager
|
|
61
|
+
async with DockerSandbox() as sandbox:
|
|
62
|
+
result = await sandbox.execute("nmap -sV target.com")
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
def __init__(self, config: Optional[SandboxConfig] = None):
|
|
66
|
+
self.config = config or SandboxConfig()
|
|
67
|
+
self._container_id: Optional[str] = None
|
|
68
|
+
self._temp_dir: Optional[str] = None
|
|
69
|
+
self._is_running = False
|
|
70
|
+
|
|
71
|
+
async def __aenter__(self) -> "DockerSandbox":
|
|
72
|
+
await self.start()
|
|
73
|
+
return self
|
|
74
|
+
|
|
75
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
76
|
+
await self.stop()
|
|
77
|
+
|
|
78
|
+
async def start(self) -> bool:
|
|
79
|
+
"""Start the sandbox container"""
|
|
80
|
+
if self._is_running:
|
|
81
|
+
return True
|
|
82
|
+
|
|
83
|
+
# Check Docker availability
|
|
84
|
+
if not await self._check_docker():
|
|
85
|
+
logger.error("Docker is not available")
|
|
86
|
+
return False
|
|
87
|
+
|
|
88
|
+
# Create temp directory for workspace
|
|
89
|
+
self._temp_dir = tempfile.mkdtemp(prefix="aipt_sandbox_")
|
|
90
|
+
|
|
91
|
+
# Build docker run command
|
|
92
|
+
cmd_parts = ["docker", "run", "-d"]
|
|
93
|
+
|
|
94
|
+
# Resource limits
|
|
95
|
+
cmd_parts.extend(["--memory", self.config.memory_limit])
|
|
96
|
+
cmd_parts.extend(["--cpus", str(self.config.cpu_limit)])
|
|
97
|
+
|
|
98
|
+
# Network
|
|
99
|
+
cmd_parts.extend(["--network", self.config.network_mode])
|
|
100
|
+
|
|
101
|
+
# Security
|
|
102
|
+
if self.config.no_new_privileges:
|
|
103
|
+
cmd_parts.append("--security-opt=no-new-privileges")
|
|
104
|
+
|
|
105
|
+
for cap in self.config.drop_capabilities:
|
|
106
|
+
cmd_parts.extend(["--cap-drop", cap])
|
|
107
|
+
for cap in self.config.add_capabilities:
|
|
108
|
+
cmd_parts.extend(["--cap-add", cap])
|
|
109
|
+
|
|
110
|
+
if self.config.read_only_root:
|
|
111
|
+
cmd_parts.append("--read-only")
|
|
112
|
+
cmd_parts.extend(["--tmpfs", "/tmp:rw,noexec,nosuid,size=100m"])
|
|
113
|
+
|
|
114
|
+
# Workspace mount
|
|
115
|
+
if self.config.mount_workspace:
|
|
116
|
+
cmd_parts.extend([
|
|
117
|
+
"-v", f"{self._temp_dir}:{self.config.workspace_path}:rw"
|
|
118
|
+
])
|
|
119
|
+
cmd_parts.extend(["-w", self.config.workspace_path])
|
|
120
|
+
|
|
121
|
+
# Auto-remove
|
|
122
|
+
if self.config.auto_remove:
|
|
123
|
+
cmd_parts.append("--rm")
|
|
124
|
+
|
|
125
|
+
# Image and keep alive command
|
|
126
|
+
cmd_parts.extend([self.config.image, "tail", "-f", "/dev/null"])
|
|
127
|
+
|
|
128
|
+
# Start container
|
|
129
|
+
try:
|
|
130
|
+
process = await asyncio.create_subprocess_exec(
|
|
131
|
+
*cmd_parts,
|
|
132
|
+
stdout=asyncio.subprocess.PIPE,
|
|
133
|
+
stderr=asyncio.subprocess.PIPE,
|
|
134
|
+
)
|
|
135
|
+
stdout, stderr = await process.communicate()
|
|
136
|
+
|
|
137
|
+
if process.returncode != 0:
|
|
138
|
+
logger.error(f"Failed to start container: {stderr.decode()}")
|
|
139
|
+
return False
|
|
140
|
+
|
|
141
|
+
self._container_id = stdout.decode().strip()[:12]
|
|
142
|
+
self._is_running = True
|
|
143
|
+
logger.info(f"Sandbox started: {self._container_id}")
|
|
144
|
+
return True
|
|
145
|
+
|
|
146
|
+
except Exception as e:
|
|
147
|
+
logger.error(f"Error starting sandbox: {e}")
|
|
148
|
+
return False
|
|
149
|
+
|
|
150
|
+
async def stop(self) -> bool:
|
|
151
|
+
"""Stop and cleanup the sandbox"""
|
|
152
|
+
if not self._is_running or not self._container_id:
|
|
153
|
+
return True
|
|
154
|
+
|
|
155
|
+
try:
|
|
156
|
+
process = await asyncio.create_subprocess_exec(
|
|
157
|
+
"docker", "stop", "-t", "5", self._container_id,
|
|
158
|
+
stdout=asyncio.subprocess.PIPE,
|
|
159
|
+
stderr=asyncio.subprocess.PIPE,
|
|
160
|
+
)
|
|
161
|
+
await process.communicate()
|
|
162
|
+
|
|
163
|
+
self._is_running = False
|
|
164
|
+
self._container_id = None
|
|
165
|
+
|
|
166
|
+
# Cleanup temp dir
|
|
167
|
+
if self._temp_dir and os.path.exists(self._temp_dir):
|
|
168
|
+
import shutil
|
|
169
|
+
shutil.rmtree(self._temp_dir, ignore_errors=True)
|
|
170
|
+
self._temp_dir = None
|
|
171
|
+
|
|
172
|
+
logger.info("Sandbox stopped")
|
|
173
|
+
return True
|
|
174
|
+
|
|
175
|
+
except Exception as e:
|
|
176
|
+
logger.error(f"Error stopping sandbox: {e}")
|
|
177
|
+
return False
|
|
178
|
+
|
|
179
|
+
async def execute(
|
|
180
|
+
self,
|
|
181
|
+
command: str,
|
|
182
|
+
timeout: Optional[float] = None,
|
|
183
|
+
user: str = "root",
|
|
184
|
+
) -> CommandResult:
|
|
185
|
+
"""
|
|
186
|
+
Execute command inside the sandbox.
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
command: Command to execute
|
|
190
|
+
timeout: Override timeout
|
|
191
|
+
user: User to run as
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
CommandResult
|
|
195
|
+
"""
|
|
196
|
+
if not self._is_running:
|
|
197
|
+
return CommandResult(
|
|
198
|
+
command=command,
|
|
199
|
+
status=ExecutionStatus.FAILED,
|
|
200
|
+
stderr="Sandbox not running",
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
timeout = timeout or self.config.timeout
|
|
204
|
+
result = CommandResult(
|
|
205
|
+
command=command,
|
|
206
|
+
status=ExecutionStatus.RUNNING,
|
|
207
|
+
start_time=datetime.utcnow(),
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
try:
|
|
211
|
+
# Docker exec command
|
|
212
|
+
exec_cmd = [
|
|
213
|
+
"docker", "exec",
|
|
214
|
+
"-u", user,
|
|
215
|
+
self._container_id,
|
|
216
|
+
"sh", "-c", command,
|
|
217
|
+
]
|
|
218
|
+
|
|
219
|
+
process = await asyncio.create_subprocess_exec(
|
|
220
|
+
*exec_cmd,
|
|
221
|
+
stdout=asyncio.subprocess.PIPE,
|
|
222
|
+
stderr=asyncio.subprocess.PIPE,
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
try:
|
|
226
|
+
stdout, stderr = await asyncio.wait_for(
|
|
227
|
+
process.communicate(),
|
|
228
|
+
timeout=timeout,
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
result.exit_code = process.returncode
|
|
232
|
+
result.status = (
|
|
233
|
+
ExecutionStatus.SUCCESS
|
|
234
|
+
if process.returncode == 0
|
|
235
|
+
else ExecutionStatus.FAILED
|
|
236
|
+
)
|
|
237
|
+
result.stdout = stdout.decode("utf-8", errors="replace")
|
|
238
|
+
result.stderr = stderr.decode("utf-8", errors="replace")
|
|
239
|
+
|
|
240
|
+
except asyncio.TimeoutError:
|
|
241
|
+
process.kill()
|
|
242
|
+
result.status = ExecutionStatus.TIMEOUT
|
|
243
|
+
result.stderr = f"Command timed out after {timeout}s"
|
|
244
|
+
|
|
245
|
+
except Exception as e:
|
|
246
|
+
result.status = ExecutionStatus.FAILED
|
|
247
|
+
result.stderr = str(e)
|
|
248
|
+
finally:
|
|
249
|
+
result.end_time = datetime.utcnow()
|
|
250
|
+
if result.start_time:
|
|
251
|
+
result.duration_seconds = (result.end_time - result.start_time).total_seconds()
|
|
252
|
+
|
|
253
|
+
return result
|
|
254
|
+
|
|
255
|
+
async def copy_to(self, local_path: str, container_path: str) -> bool:
|
|
256
|
+
"""Copy file into sandbox"""
|
|
257
|
+
if not self._is_running:
|
|
258
|
+
return False
|
|
259
|
+
|
|
260
|
+
try:
|
|
261
|
+
process = await asyncio.create_subprocess_exec(
|
|
262
|
+
"docker", "cp", local_path, f"{self._container_id}:{container_path}",
|
|
263
|
+
stdout=asyncio.subprocess.PIPE,
|
|
264
|
+
stderr=asyncio.subprocess.PIPE,
|
|
265
|
+
)
|
|
266
|
+
_, stderr = await process.communicate()
|
|
267
|
+
return process.returncode == 0
|
|
268
|
+
except Exception:
|
|
269
|
+
return False
|
|
270
|
+
|
|
271
|
+
async def copy_from(self, container_path: str, local_path: str) -> bool:
|
|
272
|
+
"""Copy file from sandbox"""
|
|
273
|
+
if not self._is_running:
|
|
274
|
+
return False
|
|
275
|
+
|
|
276
|
+
try:
|
|
277
|
+
process = await asyncio.create_subprocess_exec(
|
|
278
|
+
"docker", "cp", f"{self._container_id}:{container_path}", local_path,
|
|
279
|
+
stdout=asyncio.subprocess.PIPE,
|
|
280
|
+
stderr=asyncio.subprocess.PIPE,
|
|
281
|
+
)
|
|
282
|
+
_, stderr = await process.communicate()
|
|
283
|
+
return process.returncode == 0
|
|
284
|
+
except Exception:
|
|
285
|
+
return False
|
|
286
|
+
|
|
287
|
+
def write_to_workspace(self, filename: str, content: str) -> Optional[str]:
|
|
288
|
+
"""Write file to workspace directory"""
|
|
289
|
+
if not self._temp_dir:
|
|
290
|
+
return None
|
|
291
|
+
filepath = os.path.join(self._temp_dir, filename)
|
|
292
|
+
with open(filepath, "w") as f:
|
|
293
|
+
f.write(content)
|
|
294
|
+
return os.path.join(self.config.workspace_path, filename)
|
|
295
|
+
|
|
296
|
+
async def _check_docker(self) -> bool:
|
|
297
|
+
"""Check if Docker is available"""
|
|
298
|
+
try:
|
|
299
|
+
process = await asyncio.create_subprocess_exec(
|
|
300
|
+
"docker", "info",
|
|
301
|
+
stdout=asyncio.subprocess.PIPE,
|
|
302
|
+
stderr=asyncio.subprocess.PIPE,
|
|
303
|
+
)
|
|
304
|
+
await process.communicate()
|
|
305
|
+
return process.returncode == 0
|
|
306
|
+
except FileNotFoundError:
|
|
307
|
+
return False
|
|
308
|
+
|
|
309
|
+
@property
|
|
310
|
+
def is_running(self) -> bool:
|
|
311
|
+
return self._is_running
|
|
312
|
+
|
|
313
|
+
@property
|
|
314
|
+
def container_id(self) -> Optional[str]:
|
|
315
|
+
return self._container_id
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
# Security-focused sandbox presets
|
|
319
|
+
def create_network_scanner_sandbox() -> DockerSandbox:
|
|
320
|
+
"""Sandbox configured for network scanning tools"""
|
|
321
|
+
return DockerSandbox(SandboxConfig(
|
|
322
|
+
image="instrumentisto/nmap:latest",
|
|
323
|
+
network_mode="host", # Need host networking for scanning
|
|
324
|
+
memory_limit="1g",
|
|
325
|
+
timeout=600.0,
|
|
326
|
+
add_capabilities=["NET_RAW", "NET_ADMIN"],
|
|
327
|
+
))
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
def create_web_scanner_sandbox() -> DockerSandbox:
|
|
331
|
+
"""Sandbox configured for web scanning"""
|
|
332
|
+
return DockerSandbox(SandboxConfig(
|
|
333
|
+
image="python:3.11-slim",
|
|
334
|
+
network_mode="bridge",
|
|
335
|
+
memory_limit="512m",
|
|
336
|
+
timeout=300.0,
|
|
337
|
+
))
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def create_exploit_sandbox() -> DockerSandbox:
|
|
341
|
+
"""Highly isolated sandbox for running exploits"""
|
|
342
|
+
return DockerSandbox(SandboxConfig(
|
|
343
|
+
image="python:3.11-slim",
|
|
344
|
+
network_mode="none", # No network access
|
|
345
|
+
memory_limit="256m",
|
|
346
|
+
cpu_limit=0.5,
|
|
347
|
+
timeout=60.0,
|
|
348
|
+
read_only_root=True,
|
|
349
|
+
drop_capabilities=["ALL"],
|
|
350
|
+
))
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AIPT Tools Module - Terminal, Browser, Proxy, and Output Parsing
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from aipt_v2.tools.parser import OutputParser, Finding
|
|
6
|
+
from aipt_v2.tools.tool_processing import process_tool_invocations
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def get_tools_prompt() -> str:
|
|
10
|
+
"""Get the tools prompt for the agent."""
|
|
11
|
+
return """
|
|
12
|
+
You have access to the following security tools:
|
|
13
|
+
|
|
14
|
+
## Terminal Tools
|
|
15
|
+
- execute_command: Run shell commands in isolated Docker sandbox
|
|
16
|
+
- terminal_session: Manage persistent terminal sessions
|
|
17
|
+
|
|
18
|
+
## Browser Tools
|
|
19
|
+
- browser_navigate: Navigate to URLs
|
|
20
|
+
- browser_click: Click elements
|
|
21
|
+
- browser_type: Type text into inputs
|
|
22
|
+
- browser_screenshot: Take screenshots
|
|
23
|
+
|
|
24
|
+
## Proxy Tools
|
|
25
|
+
- proxy_intercept: Intercept HTTP traffic
|
|
26
|
+
- proxy_modify: Modify requests/responses
|
|
27
|
+
|
|
28
|
+
## Security Tools
|
|
29
|
+
- nmap: Port scanning and service detection
|
|
30
|
+
- gobuster: Directory brute-forcing
|
|
31
|
+
- nuclei: Vulnerability scanning
|
|
32
|
+
- hydra: Credential brute-forcing
|
|
33
|
+
- sqlmap: SQL injection testing
|
|
34
|
+
|
|
35
|
+
Use these tools to accomplish your penetration testing objectives.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
__all__ = [
|
|
40
|
+
"OutputParser",
|
|
41
|
+
"Finding",
|
|
42
|
+
"process_tool_invocations",
|
|
43
|
+
"get_tools_prompt",
|
|
44
|
+
]
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AIPT Active Directory Security Module
|
|
3
|
+
|
|
4
|
+
Comprehensive Active Directory penetration testing:
|
|
5
|
+
- BloodHound integration for attack path mapping
|
|
6
|
+
- Kerberos attacks (Kerberoasting, AS-REP roasting)
|
|
7
|
+
- LDAP enumeration (users, groups, computers, GPOs)
|
|
8
|
+
- SMB attacks (relay, pass-the-hash, PsExec)
|
|
9
|
+
- Domain trust enumeration and abuse
|
|
10
|
+
|
|
11
|
+
Usage:
|
|
12
|
+
from aipt_v2.tools.active_directory import (
|
|
13
|
+
ADScanner,
|
|
14
|
+
BloodHoundWrapper,
|
|
15
|
+
KerberosAttacks,
|
|
16
|
+
LDAPEnum,
|
|
17
|
+
SMBAttacks
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
# Run full AD assessment
|
|
21
|
+
scanner = ADScanner(domain="corp.local", dc_ip="10.0.0.1")
|
|
22
|
+
findings = await scanner.scan(username="user", password="pass")
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from aipt_v2.tools.active_directory.ad_config import (
|
|
26
|
+
ADConfig,
|
|
27
|
+
ADCredentials,
|
|
28
|
+
get_ad_config,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
from aipt_v2.tools.active_directory.ldap_enum import (
|
|
32
|
+
LDAPEnum,
|
|
33
|
+
LDAPFinding,
|
|
34
|
+
enumerate_ldap,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
from aipt_v2.tools.active_directory.kerberos_attacks import (
|
|
38
|
+
KerberosAttacks,
|
|
39
|
+
KerberosFinding,
|
|
40
|
+
run_kerberoast,
|
|
41
|
+
run_asreproast,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
from aipt_v2.tools.active_directory.smb_attacks import (
|
|
45
|
+
SMBAttacks,
|
|
46
|
+
SMBFinding,
|
|
47
|
+
enumerate_smb,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
from aipt_v2.tools.active_directory.bloodhound_wrapper import (
|
|
51
|
+
BloodHoundWrapper,
|
|
52
|
+
BloodHoundResult,
|
|
53
|
+
run_bloodhound,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
__all__ = [
|
|
57
|
+
# Config
|
|
58
|
+
"ADConfig",
|
|
59
|
+
"ADCredentials",
|
|
60
|
+
"get_ad_config",
|
|
61
|
+
# LDAP
|
|
62
|
+
"LDAPEnum",
|
|
63
|
+
"LDAPFinding",
|
|
64
|
+
"enumerate_ldap",
|
|
65
|
+
# Kerberos
|
|
66
|
+
"KerberosAttacks",
|
|
67
|
+
"KerberosFinding",
|
|
68
|
+
"run_kerberoast",
|
|
69
|
+
"run_asreproast",
|
|
70
|
+
# SMB
|
|
71
|
+
"SMBAttacks",
|
|
72
|
+
"SMBFinding",
|
|
73
|
+
"enumerate_smb",
|
|
74
|
+
# BloodHound
|
|
75
|
+
"BloodHoundWrapper",
|
|
76
|
+
"BloodHoundResult",
|
|
77
|
+
"run_bloodhound",
|
|
78
|
+
]
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Active Directory Configuration
|
|
3
|
+
|
|
4
|
+
Handles credentials and configuration for AD penetration testing.
|
|
5
|
+
Supports multiple authentication methods:
|
|
6
|
+
- Password authentication
|
|
7
|
+
- NTLM hash authentication (pass-the-hash)
|
|
8
|
+
- Kerberos ticket authentication
|
|
9
|
+
|
|
10
|
+
Usage:
|
|
11
|
+
from aipt_v2.tools.active_directory import ADConfig, get_ad_config
|
|
12
|
+
|
|
13
|
+
config = get_ad_config(
|
|
14
|
+
domain="corp.local",
|
|
15
|
+
dc_ip="10.0.0.1",
|
|
16
|
+
username="admin",
|
|
17
|
+
password="secret"
|
|
18
|
+
)
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
import os
|
|
22
|
+
from dataclasses import dataclass, field
|
|
23
|
+
from typing import Optional, List
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass
|
|
27
|
+
class ADCredentials:
|
|
28
|
+
"""Active Directory credentials."""
|
|
29
|
+
username: str = ""
|
|
30
|
+
password: str = ""
|
|
31
|
+
domain: str = ""
|
|
32
|
+
|
|
33
|
+
# Alternative auth methods
|
|
34
|
+
ntlm_hash: str = "" # Format: LMHASH:NTHASH or just NTHASH
|
|
35
|
+
aes_key: str = "" # Kerberos AES key
|
|
36
|
+
ccache_file: str = "" # Kerberos ticket cache
|
|
37
|
+
|
|
38
|
+
# Kerberos options
|
|
39
|
+
use_kerberos: bool = False
|
|
40
|
+
kdcHost: str = ""
|
|
41
|
+
|
|
42
|
+
def __post_init__(self):
|
|
43
|
+
"""Load from environment if not provided."""
|
|
44
|
+
if not self.username:
|
|
45
|
+
self.username = os.getenv("AD_USERNAME", "")
|
|
46
|
+
if not self.password:
|
|
47
|
+
self.password = os.getenv("AD_PASSWORD", "")
|
|
48
|
+
if not self.domain:
|
|
49
|
+
self.domain = os.getenv("AD_DOMAIN", "")
|
|
50
|
+
if not self.ntlm_hash:
|
|
51
|
+
self.ntlm_hash = os.getenv("AD_NTLM_HASH", "")
|
|
52
|
+
if not self.ccache_file:
|
|
53
|
+
self.ccache_file = os.getenv("KRB5CCNAME", "")
|
|
54
|
+
|
|
55
|
+
def get_auth_string(self) -> str:
|
|
56
|
+
"""Get authentication string for tools."""
|
|
57
|
+
if self.domain and self.username:
|
|
58
|
+
return f"{self.domain}\\{self.username}"
|
|
59
|
+
return self.username
|
|
60
|
+
|
|
61
|
+
def has_credentials(self) -> bool:
|
|
62
|
+
"""Check if valid credentials are available."""
|
|
63
|
+
return bool(
|
|
64
|
+
(self.username and self.password) or
|
|
65
|
+
(self.username and self.ntlm_hash) or
|
|
66
|
+
self.ccache_file
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
def is_hash_auth(self) -> bool:
|
|
70
|
+
"""Check if using hash-based authentication."""
|
|
71
|
+
return bool(self.ntlm_hash and not self.password)
|
|
72
|
+
|
|
73
|
+
def get_password_or_hash(self) -> str:
|
|
74
|
+
"""Get password or hash for authentication."""
|
|
75
|
+
return self.password if self.password else self.ntlm_hash
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@dataclass
|
|
79
|
+
class ADConfig:
|
|
80
|
+
"""Active Directory scanning configuration."""
|
|
81
|
+
# Domain settings
|
|
82
|
+
domain: str = ""
|
|
83
|
+
dc_ip: str = ""
|
|
84
|
+
dc_hostname: str = ""
|
|
85
|
+
|
|
86
|
+
# Credentials
|
|
87
|
+
credentials: ADCredentials = field(default_factory=ADCredentials)
|
|
88
|
+
|
|
89
|
+
# Target settings
|
|
90
|
+
target_users: List[str] = field(default_factory=list)
|
|
91
|
+
target_groups: List[str] = field(default_factory=list)
|
|
92
|
+
target_computers: List[str] = field(default_factory=list)
|
|
93
|
+
|
|
94
|
+
# Scanning options
|
|
95
|
+
enum_users: bool = True
|
|
96
|
+
enum_groups: bool = True
|
|
97
|
+
enum_computers: bool = True
|
|
98
|
+
enum_gpos: bool = True
|
|
99
|
+
enum_trusts: bool = True
|
|
100
|
+
|
|
101
|
+
# Attack options
|
|
102
|
+
run_kerberoast: bool = False
|
|
103
|
+
run_asreproast: bool = False
|
|
104
|
+
run_bloodhound: bool = False
|
|
105
|
+
|
|
106
|
+
# Output
|
|
107
|
+
output_dir: str = "./ad_results"
|
|
108
|
+
|
|
109
|
+
# LDAP settings
|
|
110
|
+
ldap_port: int = 389
|
|
111
|
+
ldaps_port: int = 636
|
|
112
|
+
use_ssl: bool = False
|
|
113
|
+
use_gc: bool = False # Global Catalog (port 3268/3269)
|
|
114
|
+
|
|
115
|
+
# Timeouts
|
|
116
|
+
timeout: int = 30
|
|
117
|
+
|
|
118
|
+
def __post_init__(self):
|
|
119
|
+
"""Initialize from environment."""
|
|
120
|
+
if not self.domain:
|
|
121
|
+
self.domain = os.getenv("AD_DOMAIN", "")
|
|
122
|
+
if not self.dc_ip:
|
|
123
|
+
self.dc_ip = os.getenv("AD_DC_IP", os.getenv("DC_IP", ""))
|
|
124
|
+
|
|
125
|
+
# Ensure credentials have domain
|
|
126
|
+
if self.domain and not self.credentials.domain:
|
|
127
|
+
self.credentials.domain = self.domain
|
|
128
|
+
|
|
129
|
+
def get_ldap_uri(self) -> str:
|
|
130
|
+
"""Get LDAP connection URI."""
|
|
131
|
+
protocol = "ldaps" if self.use_ssl else "ldap"
|
|
132
|
+
port = self.ldaps_port if self.use_ssl else self.ldap_port
|
|
133
|
+
|
|
134
|
+
if self.use_gc:
|
|
135
|
+
port = 3269 if self.use_ssl else 3268
|
|
136
|
+
|
|
137
|
+
host = self.dc_ip or self.dc_hostname
|
|
138
|
+
return f"{protocol}://{host}:{port}"
|
|
139
|
+
|
|
140
|
+
def get_base_dn(self) -> str:
|
|
141
|
+
"""Get LDAP base DN from domain."""
|
|
142
|
+
if not self.domain:
|
|
143
|
+
return ""
|
|
144
|
+
parts = self.domain.split(".")
|
|
145
|
+
return ",".join([f"DC={part}" for part in parts])
|
|
146
|
+
|
|
147
|
+
def is_configured(self) -> bool:
|
|
148
|
+
"""Check if AD is properly configured."""
|
|
149
|
+
return bool(
|
|
150
|
+
self.domain and
|
|
151
|
+
(self.dc_ip or self.dc_hostname) and
|
|
152
|
+
self.credentials.has_credentials()
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def get_ad_config(
|
|
157
|
+
domain: Optional[str] = None,
|
|
158
|
+
dc_ip: Optional[str] = None,
|
|
159
|
+
username: Optional[str] = None,
|
|
160
|
+
password: Optional[str] = None,
|
|
161
|
+
ntlm_hash: Optional[str] = None,
|
|
162
|
+
**kwargs
|
|
163
|
+
) -> ADConfig:
|
|
164
|
+
"""
|
|
165
|
+
Create ADConfig from parameters and environment.
|
|
166
|
+
|
|
167
|
+
Args:
|
|
168
|
+
domain: AD domain name (e.g., "corp.local")
|
|
169
|
+
dc_ip: Domain Controller IP address
|
|
170
|
+
username: AD username
|
|
171
|
+
password: AD password
|
|
172
|
+
ntlm_hash: NTLM hash for pass-the-hash
|
|
173
|
+
**kwargs: Additional configuration options
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
ADConfig instance
|
|
177
|
+
"""
|
|
178
|
+
credentials = ADCredentials(
|
|
179
|
+
username=username or os.getenv("AD_USERNAME", ""),
|
|
180
|
+
password=password or os.getenv("AD_PASSWORD", ""),
|
|
181
|
+
domain=domain or os.getenv("AD_DOMAIN", ""),
|
|
182
|
+
ntlm_hash=ntlm_hash or os.getenv("AD_NTLM_HASH", "")
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
return ADConfig(
|
|
186
|
+
domain=domain or os.getenv("AD_DOMAIN", ""),
|
|
187
|
+
dc_ip=dc_ip or os.getenv("AD_DC_IP", ""),
|
|
188
|
+
credentials=credentials,
|
|
189
|
+
output_dir=kwargs.get("output_dir", "./ad_results"),
|
|
190
|
+
use_ssl=kwargs.get("use_ssl", False),
|
|
191
|
+
timeout=kwargs.get("timeout", 30),
|
|
192
|
+
run_kerberoast=kwargs.get("run_kerberoast", False),
|
|
193
|
+
run_asreproast=kwargs.get("run_asreproast", False),
|
|
194
|
+
run_bloodhound=kwargs.get("run_bloodhound", False)
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def validate_ad_config(config: ADConfig) -> dict:
|
|
199
|
+
"""
|
|
200
|
+
Validate AD configuration.
|
|
201
|
+
|
|
202
|
+
Args:
|
|
203
|
+
config: ADConfig to validate
|
|
204
|
+
|
|
205
|
+
Returns:
|
|
206
|
+
Dict with validation results
|
|
207
|
+
"""
|
|
208
|
+
results = {
|
|
209
|
+
"valid": False,
|
|
210
|
+
"errors": [],
|
|
211
|
+
"warnings": []
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
# Check required fields
|
|
215
|
+
if not config.domain:
|
|
216
|
+
results["errors"].append("Domain not specified")
|
|
217
|
+
|
|
218
|
+
if not config.dc_ip and not config.dc_hostname:
|
|
219
|
+
results["errors"].append("Domain Controller not specified")
|
|
220
|
+
|
|
221
|
+
if not config.credentials.has_credentials():
|
|
222
|
+
results["errors"].append("No valid credentials provided")
|
|
223
|
+
|
|
224
|
+
# Check credential format
|
|
225
|
+
if config.credentials.ntlm_hash:
|
|
226
|
+
hash_parts = config.credentials.ntlm_hash.split(":")
|
|
227
|
+
if len(hash_parts) == 2:
|
|
228
|
+
if len(hash_parts[0]) != 32 or len(hash_parts[1]) != 32:
|
|
229
|
+
results["warnings"].append("NTLM hash format may be incorrect")
|
|
230
|
+
elif len(hash_parts) == 1:
|
|
231
|
+
if len(hash_parts[0]) != 32:
|
|
232
|
+
results["warnings"].append("NT hash length should be 32 characters")
|
|
233
|
+
|
|
234
|
+
# All good
|
|
235
|
+
if not results["errors"]:
|
|
236
|
+
results["valid"] = True
|
|
237
|
+
|
|
238
|
+
return results
|