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,505 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SMB Attack Module
|
|
3
|
+
|
|
4
|
+
Implements SMB-based enumeration and attacks:
|
|
5
|
+
- Share enumeration
|
|
6
|
+
- User enumeration via SMB
|
|
7
|
+
- Password spraying
|
|
8
|
+
- Pass-the-hash authentication
|
|
9
|
+
- SMB signing checks
|
|
10
|
+
|
|
11
|
+
Uses Impacket and CrackMapExec for SMB operations.
|
|
12
|
+
|
|
13
|
+
Usage:
|
|
14
|
+
from aipt_v2.tools.active_directory import SMBAttacks
|
|
15
|
+
|
|
16
|
+
smb = SMBAttacks(config)
|
|
17
|
+
shares = await smb.enumerate_shares()
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
import asyncio
|
|
21
|
+
import re
|
|
22
|
+
from dataclasses import dataclass, field
|
|
23
|
+
from datetime import datetime, timezone
|
|
24
|
+
from typing import List, Dict, Any, Optional
|
|
25
|
+
|
|
26
|
+
from aipt_v2.tools.active_directory.ad_config import ADConfig, get_ad_config
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class SMBFinding:
|
|
31
|
+
"""SMB security finding."""
|
|
32
|
+
category: str # share, signing, auth, misc
|
|
33
|
+
severity: str
|
|
34
|
+
title: str
|
|
35
|
+
description: str
|
|
36
|
+
target: str
|
|
37
|
+
evidence: str = ""
|
|
38
|
+
remediation: str = ""
|
|
39
|
+
timestamp: str = ""
|
|
40
|
+
|
|
41
|
+
def __post_init__(self):
|
|
42
|
+
if not self.timestamp:
|
|
43
|
+
self.timestamp = datetime.now(timezone.utc).isoformat()
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@dataclass
|
|
47
|
+
class SMBShare:
|
|
48
|
+
"""SMB share information."""
|
|
49
|
+
name: str
|
|
50
|
+
share_type: str
|
|
51
|
+
remark: str
|
|
52
|
+
readable: bool
|
|
53
|
+
writable: bool
|
|
54
|
+
permissions: List[str] = field(default_factory=list)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dataclass
|
|
58
|
+
class SMBResult:
|
|
59
|
+
"""Result of SMB enumeration."""
|
|
60
|
+
target: str
|
|
61
|
+
status: str
|
|
62
|
+
started_at: str
|
|
63
|
+
finished_at: str
|
|
64
|
+
duration: float
|
|
65
|
+
shares: List[SMBShare]
|
|
66
|
+
os_info: Dict[str, str]
|
|
67
|
+
signing_required: bool
|
|
68
|
+
findings: List[SMBFinding]
|
|
69
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class SMBAttacks:
|
|
73
|
+
"""
|
|
74
|
+
SMB Attack and Enumeration Tool.
|
|
75
|
+
|
|
76
|
+
Performs SMB-based reconnaissance and attacks
|
|
77
|
+
against Windows systems.
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
# Sensitive share names
|
|
81
|
+
SENSITIVE_SHARES = [
|
|
82
|
+
"ADMIN$", "C$", "IPC$", "SYSVOL", "NETLOGON",
|
|
83
|
+
"backup", "Backup", "IT", "Admin", "HR", "Finance",
|
|
84
|
+
"Confidential", "Private", "Secure"
|
|
85
|
+
]
|
|
86
|
+
|
|
87
|
+
def __init__(self, config: Optional[ADConfig] = None):
|
|
88
|
+
"""
|
|
89
|
+
Initialize SMB attacker.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
config: AD configuration
|
|
93
|
+
"""
|
|
94
|
+
self.config = config or ADConfig()
|
|
95
|
+
self.findings: List[SMBFinding] = []
|
|
96
|
+
self.shares: List[SMBShare] = []
|
|
97
|
+
self.os_info: Dict[str, str] = {}
|
|
98
|
+
self.signing_required: bool = True
|
|
99
|
+
|
|
100
|
+
async def _run_command(self, cmd: List[str]) -> str:
|
|
101
|
+
"""Run command and return output."""
|
|
102
|
+
try:
|
|
103
|
+
process = await asyncio.create_subprocess_exec(
|
|
104
|
+
*cmd,
|
|
105
|
+
stdout=asyncio.subprocess.PIPE,
|
|
106
|
+
stderr=asyncio.subprocess.PIPE
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
stdout, stderr = await asyncio.wait_for(
|
|
110
|
+
process.communicate(),
|
|
111
|
+
timeout=self.config.timeout
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
return stdout.decode() + stderr.decode()
|
|
115
|
+
|
|
116
|
+
except asyncio.TimeoutError:
|
|
117
|
+
return ""
|
|
118
|
+
except FileNotFoundError:
|
|
119
|
+
return f"[!] Command not found: {cmd[0]}"
|
|
120
|
+
except Exception as e:
|
|
121
|
+
return f"[!] Error: {e}"
|
|
122
|
+
|
|
123
|
+
async def check_smb_signing(self, target: str = None) -> bool:
|
|
124
|
+
"""
|
|
125
|
+
Check if SMB signing is required.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
target: Target IP (uses config.dc_ip if not provided)
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
True if signing is required
|
|
132
|
+
"""
|
|
133
|
+
target = target or self.config.dc_ip
|
|
134
|
+
|
|
135
|
+
# Use nmap for signing check
|
|
136
|
+
output = await self._run_command([
|
|
137
|
+
"nmap", "-p", "445",
|
|
138
|
+
"--script", "smb2-security-mode",
|
|
139
|
+
target
|
|
140
|
+
])
|
|
141
|
+
|
|
142
|
+
if "Message signing enabled but not required" in output:
|
|
143
|
+
self.signing_required = False
|
|
144
|
+
self.findings.append(SMBFinding(
|
|
145
|
+
category="signing",
|
|
146
|
+
severity="high",
|
|
147
|
+
title="SMB Signing Not Required",
|
|
148
|
+
description="SMB signing is enabled but not required",
|
|
149
|
+
target=target,
|
|
150
|
+
evidence="Message signing enabled but not required",
|
|
151
|
+
remediation="Enable mandatory SMB signing via GPO"
|
|
152
|
+
))
|
|
153
|
+
elif "Message signing enabled and required" in output:
|
|
154
|
+
self.signing_required = True
|
|
155
|
+
elif "not required" in output.lower():
|
|
156
|
+
self.signing_required = False
|
|
157
|
+
self.findings.append(SMBFinding(
|
|
158
|
+
category="signing",
|
|
159
|
+
severity="high",
|
|
160
|
+
title="SMB Signing Not Required",
|
|
161
|
+
description="SMB signing is not enforced",
|
|
162
|
+
target=target,
|
|
163
|
+
evidence=output[:200],
|
|
164
|
+
remediation="Enable mandatory SMB signing"
|
|
165
|
+
))
|
|
166
|
+
|
|
167
|
+
return self.signing_required
|
|
168
|
+
|
|
169
|
+
async def enumerate_shares(self, target: str = None) -> List[SMBShare]:
|
|
170
|
+
"""
|
|
171
|
+
Enumerate SMB shares.
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
target: Target IP
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
List of discovered shares
|
|
178
|
+
"""
|
|
179
|
+
target = target or self.config.dc_ip
|
|
180
|
+
shares = []
|
|
181
|
+
|
|
182
|
+
# Build smbclient command
|
|
183
|
+
cmd = ["smbclient", "-L", target, "-N"] # Null session first
|
|
184
|
+
|
|
185
|
+
if self.config.credentials.has_credentials():
|
|
186
|
+
user = self.config.credentials.get_auth_string()
|
|
187
|
+
cmd = ["smbclient", "-L", target, "-U", user]
|
|
188
|
+
if self.config.credentials.password:
|
|
189
|
+
cmd.extend(["-p", self.config.credentials.password])
|
|
190
|
+
|
|
191
|
+
output = await self._run_command(cmd)
|
|
192
|
+
|
|
193
|
+
# Parse share listing
|
|
194
|
+
share_pattern = r"^\s+(\S+)\s+(Disk|IPC|Printer)\s*(.*?)$"
|
|
195
|
+
for line in output.split("\n"):
|
|
196
|
+
match = re.match(share_pattern, line)
|
|
197
|
+
if match:
|
|
198
|
+
share_name = match.group(1)
|
|
199
|
+
share_type = match.group(2)
|
|
200
|
+
remark = match.group(3).strip()
|
|
201
|
+
|
|
202
|
+
share = SMBShare(
|
|
203
|
+
name=share_name,
|
|
204
|
+
share_type=share_type,
|
|
205
|
+
remark=remark,
|
|
206
|
+
readable=False,
|
|
207
|
+
writable=False
|
|
208
|
+
)
|
|
209
|
+
shares.append(share)
|
|
210
|
+
|
|
211
|
+
# Check for sensitive shares
|
|
212
|
+
if any(sens.lower() in share_name.lower() for sens in self.SENSITIVE_SHARES):
|
|
213
|
+
self.findings.append(SMBFinding(
|
|
214
|
+
category="share",
|
|
215
|
+
severity="medium",
|
|
216
|
+
title=f"Sensitive Share: {share_name}",
|
|
217
|
+
description=f"Potentially sensitive share discovered: {share_name}",
|
|
218
|
+
target=target,
|
|
219
|
+
evidence=f"Share: {share_name} ({share_type})",
|
|
220
|
+
remediation="Review share permissions and access"
|
|
221
|
+
))
|
|
222
|
+
|
|
223
|
+
# Try to access each share
|
|
224
|
+
for share in shares:
|
|
225
|
+
access = await self._check_share_access(target, share.name)
|
|
226
|
+
share.readable = access.get("readable", False)
|
|
227
|
+
share.writable = access.get("writable", False)
|
|
228
|
+
|
|
229
|
+
if share.readable and share.name not in ["IPC$"]:
|
|
230
|
+
self.findings.append(SMBFinding(
|
|
231
|
+
category="share",
|
|
232
|
+
severity="low" if share.name in ["SYSVOL", "NETLOGON"] else "medium",
|
|
233
|
+
title=f"Readable Share: {share.name}",
|
|
234
|
+
description=f"Share {share.name} is readable",
|
|
235
|
+
target=target,
|
|
236
|
+
evidence="Share access confirmed",
|
|
237
|
+
remediation="Review if read access is necessary"
|
|
238
|
+
))
|
|
239
|
+
|
|
240
|
+
if share.writable:
|
|
241
|
+
self.findings.append(SMBFinding(
|
|
242
|
+
category="share",
|
|
243
|
+
severity="high",
|
|
244
|
+
title=f"Writable Share: {share.name}",
|
|
245
|
+
description=f"Share {share.name} is writable",
|
|
246
|
+
target=target,
|
|
247
|
+
evidence="Write access confirmed",
|
|
248
|
+
remediation="Restrict write access to authorized users only"
|
|
249
|
+
))
|
|
250
|
+
|
|
251
|
+
self.shares = shares
|
|
252
|
+
return shares
|
|
253
|
+
|
|
254
|
+
async def _check_share_access(self, target: str, share: str) -> Dict[str, bool]:
|
|
255
|
+
"""Check read/write access to a share."""
|
|
256
|
+
access = {"readable": False, "writable": False}
|
|
257
|
+
|
|
258
|
+
# Build connection command
|
|
259
|
+
if self.config.credentials.has_credentials():
|
|
260
|
+
user = self.config.credentials.get_auth_string()
|
|
261
|
+
cmd = [
|
|
262
|
+
"smbclient",
|
|
263
|
+
f"//{target}/{share}",
|
|
264
|
+
"-U", user,
|
|
265
|
+
"-c", "dir"
|
|
266
|
+
]
|
|
267
|
+
if self.config.credentials.password:
|
|
268
|
+
cmd.insert(4, "-p")
|
|
269
|
+
cmd.insert(5, self.config.credentials.password)
|
|
270
|
+
else:
|
|
271
|
+
cmd = ["smbclient", f"//{target}/{share}", "-N", "-c", "dir"]
|
|
272
|
+
|
|
273
|
+
output = await self._run_command(cmd)
|
|
274
|
+
|
|
275
|
+
# Check if we could list contents
|
|
276
|
+
if "NT_STATUS_ACCESS_DENIED" not in output and "Error" not in output:
|
|
277
|
+
if any(x in output for x in ["blocks", "bytes", "Directory"]):
|
|
278
|
+
access["readable"] = True
|
|
279
|
+
|
|
280
|
+
# TODO: Add write check with careful file creation/deletion
|
|
281
|
+
|
|
282
|
+
return access
|
|
283
|
+
|
|
284
|
+
async def get_os_info(self, target: str = None) -> Dict[str, str]:
|
|
285
|
+
"""
|
|
286
|
+
Get OS information via SMB.
|
|
287
|
+
|
|
288
|
+
Args:
|
|
289
|
+
target: Target IP
|
|
290
|
+
|
|
291
|
+
Returns:
|
|
292
|
+
OS information dict
|
|
293
|
+
"""
|
|
294
|
+
target = target or self.config.dc_ip
|
|
295
|
+
os_info = {}
|
|
296
|
+
|
|
297
|
+
# Use nmap for OS detection
|
|
298
|
+
output = await self._run_command([
|
|
299
|
+
"nmap", "-p", "445",
|
|
300
|
+
"--script", "smb-os-discovery",
|
|
301
|
+
target
|
|
302
|
+
])
|
|
303
|
+
|
|
304
|
+
# Parse OS info
|
|
305
|
+
os_match = re.search(r"OS:\s*(.+?)$", output, re.MULTILINE)
|
|
306
|
+
if os_match:
|
|
307
|
+
os_info["os"] = os_match.group(1).strip()
|
|
308
|
+
|
|
309
|
+
computer_match = re.search(r"Computer name:\s*(.+?)$", output, re.MULTILINE)
|
|
310
|
+
if computer_match:
|
|
311
|
+
os_info["computer_name"] = computer_match.group(1).strip()
|
|
312
|
+
|
|
313
|
+
domain_match = re.search(r"Domain name:\s*(.+?)$", output, re.MULTILINE)
|
|
314
|
+
if domain_match:
|
|
315
|
+
os_info["domain"] = domain_match.group(1).strip()
|
|
316
|
+
|
|
317
|
+
self.os_info = os_info
|
|
318
|
+
return os_info
|
|
319
|
+
|
|
320
|
+
async def enumerate_users_rpc(self, target: str = None) -> List[str]:
|
|
321
|
+
"""
|
|
322
|
+
Enumerate users via RPC.
|
|
323
|
+
|
|
324
|
+
Args:
|
|
325
|
+
target: Target IP
|
|
326
|
+
|
|
327
|
+
Returns:
|
|
328
|
+
List of usernames
|
|
329
|
+
"""
|
|
330
|
+
target = target or self.config.dc_ip
|
|
331
|
+
users = []
|
|
332
|
+
|
|
333
|
+
# Use rpcclient for enumeration
|
|
334
|
+
cmd = ["rpcclient", "-U", "", target, "-N", "-c", "enumdomusers"]
|
|
335
|
+
|
|
336
|
+
if self.config.credentials.has_credentials():
|
|
337
|
+
user = self.config.credentials.get_auth_string()
|
|
338
|
+
password = self.config.credentials.get_password_or_hash()
|
|
339
|
+
cmd = [
|
|
340
|
+
"rpcclient", "-U", f"{user}%{password}",
|
|
341
|
+
target, "-c", "enumdomusers"
|
|
342
|
+
]
|
|
343
|
+
|
|
344
|
+
output = await self._run_command(cmd)
|
|
345
|
+
|
|
346
|
+
# Parse users
|
|
347
|
+
user_pattern = r"user:\[([^\]]+)\]"
|
|
348
|
+
matches = re.findall(user_pattern, output)
|
|
349
|
+
users.extend(matches)
|
|
350
|
+
|
|
351
|
+
return users
|
|
352
|
+
|
|
353
|
+
async def password_spray(
|
|
354
|
+
self,
|
|
355
|
+
users: List[str],
|
|
356
|
+
password: str,
|
|
357
|
+
target: str = None
|
|
358
|
+
) -> List[Dict]:
|
|
359
|
+
"""
|
|
360
|
+
Perform password spray attack.
|
|
361
|
+
|
|
362
|
+
Args:
|
|
363
|
+
users: List of usernames
|
|
364
|
+
password: Password to try
|
|
365
|
+
target: Target IP
|
|
366
|
+
|
|
367
|
+
Returns:
|
|
368
|
+
List of valid credentials
|
|
369
|
+
"""
|
|
370
|
+
target = target or self.config.dc_ip
|
|
371
|
+
valid_creds = []
|
|
372
|
+
|
|
373
|
+
for user in users:
|
|
374
|
+
# Use crackmapexec or smbclient for auth test
|
|
375
|
+
cmd = [
|
|
376
|
+
"smbclient",
|
|
377
|
+
f"//{target}/IPC$",
|
|
378
|
+
"-U", f"{self.config.domain}\\{user}%{password}",
|
|
379
|
+
"-c", "exit"
|
|
380
|
+
]
|
|
381
|
+
|
|
382
|
+
output = await self._run_command(cmd)
|
|
383
|
+
|
|
384
|
+
if "NT_STATUS_LOGON_FAILURE" not in output and "Error" not in output:
|
|
385
|
+
valid_creds.append({
|
|
386
|
+
"username": user,
|
|
387
|
+
"password": password
|
|
388
|
+
})
|
|
389
|
+
|
|
390
|
+
self.findings.append(SMBFinding(
|
|
391
|
+
category="auth",
|
|
392
|
+
severity="critical",
|
|
393
|
+
title=f"Valid Credentials: {user}",
|
|
394
|
+
description=f"Password spray found valid credentials for {user}",
|
|
395
|
+
target=target,
|
|
396
|
+
evidence=f"User: {user}, Password: {password[:3]}***",
|
|
397
|
+
remediation="Enforce strong password policies"
|
|
398
|
+
))
|
|
399
|
+
|
|
400
|
+
# Add delay to avoid lockout
|
|
401
|
+
await asyncio.sleep(0.5)
|
|
402
|
+
|
|
403
|
+
return valid_creds
|
|
404
|
+
|
|
405
|
+
async def test_null_session(self, target: str = None) -> bool:
|
|
406
|
+
"""
|
|
407
|
+
Test for null session access.
|
|
408
|
+
|
|
409
|
+
Args:
|
|
410
|
+
target: Target IP
|
|
411
|
+
|
|
412
|
+
Returns:
|
|
413
|
+
True if null session works
|
|
414
|
+
"""
|
|
415
|
+
target = target or self.config.dc_ip
|
|
416
|
+
|
|
417
|
+
cmd = ["smbclient", f"//{target}/IPC$", "-N", "-c", "exit"]
|
|
418
|
+
output = await self._run_command(cmd)
|
|
419
|
+
|
|
420
|
+
null_session = "NT_STATUS_ACCESS_DENIED" not in output
|
|
421
|
+
|
|
422
|
+
if null_session:
|
|
423
|
+
self.findings.append(SMBFinding(
|
|
424
|
+
category="auth",
|
|
425
|
+
severity="high",
|
|
426
|
+
title="Null Session Allowed",
|
|
427
|
+
description="Anonymous/null session authentication is allowed",
|
|
428
|
+
target=target,
|
|
429
|
+
evidence="IPC$ accessible without credentials",
|
|
430
|
+
remediation="Disable null session access via registry or GPO"
|
|
431
|
+
))
|
|
432
|
+
|
|
433
|
+
return null_session
|
|
434
|
+
|
|
435
|
+
async def enumerate(self, target: str = None) -> SMBResult:
|
|
436
|
+
"""
|
|
437
|
+
Run full SMB enumeration.
|
|
438
|
+
|
|
439
|
+
Args:
|
|
440
|
+
target: Target IP
|
|
441
|
+
|
|
442
|
+
Returns:
|
|
443
|
+
SMBResult with findings
|
|
444
|
+
"""
|
|
445
|
+
target = target or self.config.dc_ip
|
|
446
|
+
|
|
447
|
+
started_at = datetime.now(timezone.utc).isoformat()
|
|
448
|
+
start_time = asyncio.get_event_loop().time()
|
|
449
|
+
|
|
450
|
+
# Run enumeration
|
|
451
|
+
await self.check_smb_signing(target)
|
|
452
|
+
await self.test_null_session(target)
|
|
453
|
+
await self.get_os_info(target)
|
|
454
|
+
await self.enumerate_shares(target)
|
|
455
|
+
|
|
456
|
+
finished_at = datetime.now(timezone.utc).isoformat()
|
|
457
|
+
duration = asyncio.get_event_loop().time() - start_time
|
|
458
|
+
|
|
459
|
+
return SMBResult(
|
|
460
|
+
target=target,
|
|
461
|
+
status="completed",
|
|
462
|
+
started_at=started_at,
|
|
463
|
+
finished_at=finished_at,
|
|
464
|
+
duration=duration,
|
|
465
|
+
shares=self.shares,
|
|
466
|
+
os_info=self.os_info,
|
|
467
|
+
signing_required=self.signing_required,
|
|
468
|
+
findings=self.findings,
|
|
469
|
+
metadata={
|
|
470
|
+
"share_count": len(self.shares),
|
|
471
|
+
"finding_count": len(self.findings)
|
|
472
|
+
}
|
|
473
|
+
)
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
# Convenience function
|
|
477
|
+
async def enumerate_smb(
|
|
478
|
+
target: str,
|
|
479
|
+
domain: str = None,
|
|
480
|
+
username: str = None,
|
|
481
|
+
password: str = None,
|
|
482
|
+
**kwargs
|
|
483
|
+
) -> SMBResult:
|
|
484
|
+
"""
|
|
485
|
+
Quick SMB enumeration.
|
|
486
|
+
|
|
487
|
+
Args:
|
|
488
|
+
target: Target IP or hostname
|
|
489
|
+
domain: AD domain
|
|
490
|
+
username: Username
|
|
491
|
+
password: Password
|
|
492
|
+
|
|
493
|
+
Returns:
|
|
494
|
+
SMBResult
|
|
495
|
+
"""
|
|
496
|
+
config = get_ad_config(
|
|
497
|
+
domain=domain or "",
|
|
498
|
+
dc_ip=target,
|
|
499
|
+
username=username or "",
|
|
500
|
+
password=password or "",
|
|
501
|
+
**kwargs
|
|
502
|
+
)
|
|
503
|
+
|
|
504
|
+
smb = SMBAttacks(config)
|
|
505
|
+
return await smb.enumerate(target)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AIPT Agents Graph - Agent communication and tracking
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from aipt_v2.tools.agents_graph.agents_graph_actions import (
|
|
6
|
+
_agent_graph,
|
|
7
|
+
_agent_instances,
|
|
8
|
+
_agent_states,
|
|
9
|
+
_agent_messages,
|
|
10
|
+
_root_agent_id,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"_agent_graph",
|
|
15
|
+
"_agent_instances",
|
|
16
|
+
"_agent_states",
|
|
17
|
+
"_agent_messages",
|
|
18
|
+
"_root_agent_id",
|
|
19
|
+
]
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AIPT Agents Graph Actions - State management for agent graph
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Any, Optional
|
|
6
|
+
|
|
7
|
+
# Global state for agent graph
|
|
8
|
+
_agent_graph: dict[str, Any] = {
|
|
9
|
+
"nodes": {},
|
|
10
|
+
"edges": [],
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
_agent_instances: dict[str, Any] = {}
|
|
14
|
+
_agent_states: dict[str, Any] = {}
|
|
15
|
+
_agent_messages: dict[str, list[dict]] = {}
|
|
16
|
+
_root_agent_id: Optional[str] = None
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def reset_graph() -> None:
|
|
20
|
+
"""Reset the agent graph to initial state"""
|
|
21
|
+
global _agent_graph, _agent_instances, _agent_states, _agent_messages, _root_agent_id
|
|
22
|
+
_agent_graph = {"nodes": {}, "edges": []}
|
|
23
|
+
_agent_instances = {}
|
|
24
|
+
_agent_states = {}
|
|
25
|
+
_agent_messages = {}
|
|
26
|
+
_root_agent_id = None
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def send_message(
|
|
30
|
+
from_agent_id: str,
|
|
31
|
+
to_agent_id: str,
|
|
32
|
+
content: str,
|
|
33
|
+
message_type: str = "information",
|
|
34
|
+
priority: str = "normal",
|
|
35
|
+
) -> bool:
|
|
36
|
+
"""Send a message from one agent to another"""
|
|
37
|
+
from datetime import datetime
|
|
38
|
+
|
|
39
|
+
if to_agent_id not in _agent_messages:
|
|
40
|
+
_agent_messages[to_agent_id] = []
|
|
41
|
+
|
|
42
|
+
_agent_messages[to_agent_id].append({
|
|
43
|
+
"from": from_agent_id,
|
|
44
|
+
"content": content,
|
|
45
|
+
"message_type": message_type,
|
|
46
|
+
"priority": priority,
|
|
47
|
+
"timestamp": datetime.now().isoformat(),
|
|
48
|
+
"read": False,
|
|
49
|
+
})
|
|
50
|
+
return True
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def get_agent_status(agent_id: str) -> Optional[str]:
|
|
54
|
+
"""Get the current status of an agent"""
|
|
55
|
+
if agent_id in _agent_graph["nodes"]:
|
|
56
|
+
return _agent_graph["nodes"][agent_id].get("status")
|
|
57
|
+
return None
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
__all__ = [
|
|
61
|
+
"_agent_graph",
|
|
62
|
+
"_agent_instances",
|
|
63
|
+
"_agent_states",
|
|
64
|
+
"_agent_messages",
|
|
65
|
+
"_root_agent_id",
|
|
66
|
+
"reset_graph",
|
|
67
|
+
"send_message",
|
|
68
|
+
"get_agent_status",
|
|
69
|
+
]
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AIPT API Security Module - Comprehensive API Vulnerability Scanning
|
|
3
|
+
|
|
4
|
+
Provides security testing for modern API architectures:
|
|
5
|
+
- REST APIs (OpenAPI/Swagger fuzzing)
|
|
6
|
+
- GraphQL APIs (Introspection, DoS, Injection)
|
|
7
|
+
- JWT Tokens (Algorithm confusion, signature bypass)
|
|
8
|
+
- API Discovery (Endpoint enumeration, documentation detection)
|
|
9
|
+
|
|
10
|
+
Usage:
|
|
11
|
+
from aipt_v2.tools.api_security import (
|
|
12
|
+
GraphQLScanner,
|
|
13
|
+
OpenAPIFuzzer,
|
|
14
|
+
JWTAnalyzer,
|
|
15
|
+
APIDiscovery
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
# Scan GraphQL endpoint
|
|
19
|
+
scanner = GraphQLScanner("https://api.target.com/graphql")
|
|
20
|
+
findings = await scanner.scan()
|
|
21
|
+
|
|
22
|
+
# Fuzz REST API from OpenAPI spec
|
|
23
|
+
fuzzer = OpenAPIFuzzer("https://api.target.com", spec_path="openapi.yaml")
|
|
24
|
+
findings = await fuzzer.fuzz()
|
|
25
|
+
|
|
26
|
+
# Analyze JWT token
|
|
27
|
+
analyzer = JWTAnalyzer()
|
|
28
|
+
findings = analyzer.analyze(token)
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
from aipt_v2.tools.api_security.graphql_scanner import (
|
|
32
|
+
GraphQLScanner,
|
|
33
|
+
GraphQLConfig,
|
|
34
|
+
GraphQLFinding,
|
|
35
|
+
scan_graphql,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
from aipt_v2.tools.api_security.openapi_fuzzer import (
|
|
39
|
+
OpenAPIFuzzer,
|
|
40
|
+
OpenAPIConfig,
|
|
41
|
+
OpenAPIFinding,
|
|
42
|
+
fuzz_openapi,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
from aipt_v2.tools.api_security.jwt_analyzer import (
|
|
46
|
+
JWTAnalyzer,
|
|
47
|
+
JWTFinding,
|
|
48
|
+
analyze_jwt,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
from aipt_v2.tools.api_security.api_discovery import (
|
|
52
|
+
APIDiscovery,
|
|
53
|
+
DiscoveredEndpoint,
|
|
54
|
+
discover_api,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
__all__ = [
|
|
58
|
+
# GraphQL
|
|
59
|
+
"GraphQLScanner",
|
|
60
|
+
"GraphQLConfig",
|
|
61
|
+
"GraphQLFinding",
|
|
62
|
+
"scan_graphql",
|
|
63
|
+
# OpenAPI
|
|
64
|
+
"OpenAPIFuzzer",
|
|
65
|
+
"OpenAPIConfig",
|
|
66
|
+
"OpenAPIFinding",
|
|
67
|
+
"fuzz_openapi",
|
|
68
|
+
# JWT
|
|
69
|
+
"JWTAnalyzer",
|
|
70
|
+
"JWTFinding",
|
|
71
|
+
"analyze_jwt",
|
|
72
|
+
# Discovery
|
|
73
|
+
"APIDiscovery",
|
|
74
|
+
"DiscoveredEndpoint",
|
|
75
|
+
"discover_api",
|
|
76
|
+
]
|