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.
Files changed (187) hide show
  1. aipt_v2/__init__.py +110 -0
  2. aipt_v2/__main__.py +24 -0
  3. aipt_v2/agents/AIPTxAgent/__init__.py +10 -0
  4. aipt_v2/agents/AIPTxAgent/aiptx_agent.py +211 -0
  5. aipt_v2/agents/__init__.py +46 -0
  6. aipt_v2/agents/base.py +520 -0
  7. aipt_v2/agents/exploit_agent.py +688 -0
  8. aipt_v2/agents/ptt.py +406 -0
  9. aipt_v2/agents/state.py +168 -0
  10. aipt_v2/app.py +957 -0
  11. aipt_v2/browser/__init__.py +31 -0
  12. aipt_v2/browser/automation.py +458 -0
  13. aipt_v2/browser/crawler.py +453 -0
  14. aipt_v2/cli.py +2933 -0
  15. aipt_v2/compliance/__init__.py +71 -0
  16. aipt_v2/compliance/compliance_report.py +449 -0
  17. aipt_v2/compliance/framework_mapper.py +424 -0
  18. aipt_v2/compliance/nist_mapping.py +345 -0
  19. aipt_v2/compliance/owasp_mapping.py +330 -0
  20. aipt_v2/compliance/pci_mapping.py +297 -0
  21. aipt_v2/config.py +341 -0
  22. aipt_v2/core/__init__.py +43 -0
  23. aipt_v2/core/agent.py +630 -0
  24. aipt_v2/core/llm.py +395 -0
  25. aipt_v2/core/memory.py +305 -0
  26. aipt_v2/core/ptt.py +329 -0
  27. aipt_v2/database/__init__.py +14 -0
  28. aipt_v2/database/models.py +232 -0
  29. aipt_v2/database/repository.py +384 -0
  30. aipt_v2/docker/__init__.py +23 -0
  31. aipt_v2/docker/builder.py +260 -0
  32. aipt_v2/docker/manager.py +222 -0
  33. aipt_v2/docker/sandbox.py +371 -0
  34. aipt_v2/evasion/__init__.py +58 -0
  35. aipt_v2/evasion/request_obfuscator.py +272 -0
  36. aipt_v2/evasion/tls_fingerprint.py +285 -0
  37. aipt_v2/evasion/ua_rotator.py +301 -0
  38. aipt_v2/evasion/waf_bypass.py +439 -0
  39. aipt_v2/execution/__init__.py +23 -0
  40. aipt_v2/execution/executor.py +302 -0
  41. aipt_v2/execution/parser.py +544 -0
  42. aipt_v2/execution/terminal.py +337 -0
  43. aipt_v2/health.py +437 -0
  44. aipt_v2/intelligence/__init__.py +194 -0
  45. aipt_v2/intelligence/adaptation.py +474 -0
  46. aipt_v2/intelligence/auth.py +520 -0
  47. aipt_v2/intelligence/chaining.py +775 -0
  48. aipt_v2/intelligence/correlation.py +536 -0
  49. aipt_v2/intelligence/cve_aipt.py +334 -0
  50. aipt_v2/intelligence/cve_info.py +1111 -0
  51. aipt_v2/intelligence/knowledge_graph.py +590 -0
  52. aipt_v2/intelligence/learning.py +626 -0
  53. aipt_v2/intelligence/llm_analyzer.py +502 -0
  54. aipt_v2/intelligence/llm_tool_selector.py +518 -0
  55. aipt_v2/intelligence/payload_generator.py +562 -0
  56. aipt_v2/intelligence/rag.py +239 -0
  57. aipt_v2/intelligence/scope.py +442 -0
  58. aipt_v2/intelligence/searchers/__init__.py +5 -0
  59. aipt_v2/intelligence/searchers/exploitdb_searcher.py +523 -0
  60. aipt_v2/intelligence/searchers/github_searcher.py +467 -0
  61. aipt_v2/intelligence/searchers/google_searcher.py +281 -0
  62. aipt_v2/intelligence/tools.json +443 -0
  63. aipt_v2/intelligence/triage.py +670 -0
  64. aipt_v2/interactive_shell.py +559 -0
  65. aipt_v2/interface/__init__.py +5 -0
  66. aipt_v2/interface/cli.py +230 -0
  67. aipt_v2/interface/main.py +501 -0
  68. aipt_v2/interface/tui.py +1276 -0
  69. aipt_v2/interface/utils.py +583 -0
  70. aipt_v2/llm/__init__.py +39 -0
  71. aipt_v2/llm/config.py +26 -0
  72. aipt_v2/llm/llm.py +514 -0
  73. aipt_v2/llm/memory.py +214 -0
  74. aipt_v2/llm/request_queue.py +89 -0
  75. aipt_v2/llm/utils.py +89 -0
  76. aipt_v2/local_tool_installer.py +1467 -0
  77. aipt_v2/models/__init__.py +15 -0
  78. aipt_v2/models/findings.py +295 -0
  79. aipt_v2/models/phase_result.py +224 -0
  80. aipt_v2/models/scan_config.py +207 -0
  81. aipt_v2/monitoring/grafana/dashboards/aipt-dashboard.json +355 -0
  82. aipt_v2/monitoring/grafana/dashboards/default.yml +17 -0
  83. aipt_v2/monitoring/grafana/datasources/prometheus.yml +17 -0
  84. aipt_v2/monitoring/prometheus.yml +60 -0
  85. aipt_v2/orchestration/__init__.py +52 -0
  86. aipt_v2/orchestration/pipeline.py +398 -0
  87. aipt_v2/orchestration/progress.py +300 -0
  88. aipt_v2/orchestration/scheduler.py +296 -0
  89. aipt_v2/orchestrator.py +2427 -0
  90. aipt_v2/payloads/__init__.py +27 -0
  91. aipt_v2/payloads/cmdi.py +150 -0
  92. aipt_v2/payloads/sqli.py +263 -0
  93. aipt_v2/payloads/ssrf.py +204 -0
  94. aipt_v2/payloads/templates.py +222 -0
  95. aipt_v2/payloads/traversal.py +166 -0
  96. aipt_v2/payloads/xss.py +204 -0
  97. aipt_v2/prompts/__init__.py +60 -0
  98. aipt_v2/proxy/__init__.py +29 -0
  99. aipt_v2/proxy/history.py +352 -0
  100. aipt_v2/proxy/interceptor.py +452 -0
  101. aipt_v2/recon/__init__.py +44 -0
  102. aipt_v2/recon/dns.py +241 -0
  103. aipt_v2/recon/osint.py +367 -0
  104. aipt_v2/recon/subdomain.py +372 -0
  105. aipt_v2/recon/tech_detect.py +311 -0
  106. aipt_v2/reports/__init__.py +17 -0
  107. aipt_v2/reports/generator.py +313 -0
  108. aipt_v2/reports/html_report.py +378 -0
  109. aipt_v2/runtime/__init__.py +53 -0
  110. aipt_v2/runtime/base.py +30 -0
  111. aipt_v2/runtime/docker.py +401 -0
  112. aipt_v2/runtime/local.py +346 -0
  113. aipt_v2/runtime/tool_server.py +205 -0
  114. aipt_v2/runtime/vps.py +830 -0
  115. aipt_v2/scanners/__init__.py +28 -0
  116. aipt_v2/scanners/base.py +273 -0
  117. aipt_v2/scanners/nikto.py +244 -0
  118. aipt_v2/scanners/nmap.py +402 -0
  119. aipt_v2/scanners/nuclei.py +273 -0
  120. aipt_v2/scanners/web.py +454 -0
  121. aipt_v2/scripts/security_audit.py +366 -0
  122. aipt_v2/setup_wizard.py +941 -0
  123. aipt_v2/skills/__init__.py +80 -0
  124. aipt_v2/skills/agents/__init__.py +14 -0
  125. aipt_v2/skills/agents/api_tester.py +706 -0
  126. aipt_v2/skills/agents/base.py +477 -0
  127. aipt_v2/skills/agents/code_review.py +459 -0
  128. aipt_v2/skills/agents/security_agent.py +336 -0
  129. aipt_v2/skills/agents/web_pentest.py +818 -0
  130. aipt_v2/skills/prompts/__init__.py +647 -0
  131. aipt_v2/system_detector.py +539 -0
  132. aipt_v2/telemetry/__init__.py +7 -0
  133. aipt_v2/telemetry/tracer.py +347 -0
  134. aipt_v2/terminal/__init__.py +28 -0
  135. aipt_v2/terminal/executor.py +400 -0
  136. aipt_v2/terminal/sandbox.py +350 -0
  137. aipt_v2/tools/__init__.py +44 -0
  138. aipt_v2/tools/active_directory/__init__.py +78 -0
  139. aipt_v2/tools/active_directory/ad_config.py +238 -0
  140. aipt_v2/tools/active_directory/bloodhound_wrapper.py +447 -0
  141. aipt_v2/tools/active_directory/kerberos_attacks.py +430 -0
  142. aipt_v2/tools/active_directory/ldap_enum.py +533 -0
  143. aipt_v2/tools/active_directory/smb_attacks.py +505 -0
  144. aipt_v2/tools/agents_graph/__init__.py +19 -0
  145. aipt_v2/tools/agents_graph/agents_graph_actions.py +69 -0
  146. aipt_v2/tools/api_security/__init__.py +76 -0
  147. aipt_v2/tools/api_security/api_discovery.py +608 -0
  148. aipt_v2/tools/api_security/graphql_scanner.py +622 -0
  149. aipt_v2/tools/api_security/jwt_analyzer.py +577 -0
  150. aipt_v2/tools/api_security/openapi_fuzzer.py +761 -0
  151. aipt_v2/tools/browser/__init__.py +5 -0
  152. aipt_v2/tools/browser/browser_actions.py +238 -0
  153. aipt_v2/tools/browser/browser_instance.py +535 -0
  154. aipt_v2/tools/browser/tab_manager.py +344 -0
  155. aipt_v2/tools/cloud/__init__.py +70 -0
  156. aipt_v2/tools/cloud/cloud_config.py +273 -0
  157. aipt_v2/tools/cloud/cloud_scanner.py +639 -0
  158. aipt_v2/tools/cloud/prowler_tool.py +571 -0
  159. aipt_v2/tools/cloud/scoutsuite_tool.py +359 -0
  160. aipt_v2/tools/executor.py +307 -0
  161. aipt_v2/tools/parser.py +408 -0
  162. aipt_v2/tools/proxy/__init__.py +5 -0
  163. aipt_v2/tools/proxy/proxy_actions.py +103 -0
  164. aipt_v2/tools/proxy/proxy_manager.py +789 -0
  165. aipt_v2/tools/registry.py +196 -0
  166. aipt_v2/tools/scanners/__init__.py +343 -0
  167. aipt_v2/tools/scanners/acunetix_tool.py +712 -0
  168. aipt_v2/tools/scanners/burp_tool.py +631 -0
  169. aipt_v2/tools/scanners/config.py +156 -0
  170. aipt_v2/tools/scanners/nessus_tool.py +588 -0
  171. aipt_v2/tools/scanners/zap_tool.py +612 -0
  172. aipt_v2/tools/terminal/__init__.py +5 -0
  173. aipt_v2/tools/terminal/terminal_actions.py +37 -0
  174. aipt_v2/tools/terminal/terminal_manager.py +153 -0
  175. aipt_v2/tools/terminal/terminal_session.py +449 -0
  176. aipt_v2/tools/tool_processing.py +108 -0
  177. aipt_v2/utils/__init__.py +17 -0
  178. aipt_v2/utils/logging.py +202 -0
  179. aipt_v2/utils/model_manager.py +187 -0
  180. aipt_v2/utils/searchers/__init__.py +269 -0
  181. aipt_v2/verify_install.py +793 -0
  182. aiptx-2.0.7.dist-info/METADATA +345 -0
  183. aiptx-2.0.7.dist-info/RECORD +187 -0
  184. aiptx-2.0.7.dist-info/WHEEL +5 -0
  185. aiptx-2.0.7.dist-info/entry_points.txt +7 -0
  186. aiptx-2.0.7.dist-info/licenses/LICENSE +21 -0
  187. aiptx-2.0.7.dist-info/top_level.txt +1 -0
@@ -0,0 +1,533 @@
1
+ """
2
+ LDAP Enumeration for Active Directory
3
+
4
+ Comprehensive LDAP enumeration including:
5
+ - User enumeration
6
+ - Group enumeration
7
+ - Computer enumeration
8
+ - GPO enumeration
9
+ - Domain trust enumeration
10
+ - Service account detection
11
+ - Privileged account identification
12
+
13
+ Usage:
14
+ from aipt_v2.tools.active_directory import LDAPEnum
15
+
16
+ enum = LDAPEnum(config)
17
+ users = await enum.enumerate_users()
18
+ groups = await enum.enumerate_groups()
19
+ """
20
+
21
+ import asyncio
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
+ try:
29
+ import ldap3
30
+ from ldap3 import Server, Connection, ALL, NTLM, KERBEROS, SASL
31
+ HAS_LDAP3 = True
32
+ except ImportError:
33
+ HAS_LDAP3 = False
34
+
35
+
36
+ @dataclass
37
+ class LDAPFinding:
38
+ """LDAP enumeration finding."""
39
+ category: str # user, group, computer, gpo, trust, misc
40
+ severity: str # critical, high, medium, low, info
41
+ title: str
42
+ description: str
43
+ object_dn: str
44
+ attributes: Dict[str, Any] = field(default_factory=dict)
45
+ remediation: str = ""
46
+ timestamp: str = ""
47
+
48
+ def __post_init__(self):
49
+ if not self.timestamp:
50
+ self.timestamp = datetime.now(timezone.utc).isoformat()
51
+
52
+
53
+ @dataclass
54
+ class LDAPEnumResult:
55
+ """Result of LDAP enumeration."""
56
+ domain: str
57
+ status: str
58
+ started_at: str
59
+ finished_at: str
60
+ duration: float
61
+ users: List[Dict]
62
+ groups: List[Dict]
63
+ computers: List[Dict]
64
+ gpos: List[Dict]
65
+ trusts: List[Dict]
66
+ findings: List[LDAPFinding]
67
+ metadata: Dict[str, Any] = field(default_factory=dict)
68
+
69
+
70
+ class LDAPEnum:
71
+ """
72
+ LDAP Enumeration Tool.
73
+
74
+ Enumerates Active Directory objects via LDAP
75
+ and identifies security issues.
76
+ """
77
+
78
+ # Privileged groups to flag
79
+ PRIVILEGED_GROUPS = [
80
+ "Domain Admins",
81
+ "Enterprise Admins",
82
+ "Schema Admins",
83
+ "Administrators",
84
+ "Account Operators",
85
+ "Backup Operators",
86
+ "Server Operators",
87
+ "Print Operators",
88
+ "DnsAdmins",
89
+ "Domain Controllers",
90
+ "Group Policy Creator Owners"
91
+ ]
92
+
93
+ # Dangerous user attributes
94
+ DANGEROUS_FLAGS = {
95
+ 0x00010000: "PASSWORD_NEVER_EXPIRES",
96
+ 0x00020000: "MNS_LOGON_ACCOUNT",
97
+ 0x00000002: "ACCOUNT_DISABLED",
98
+ 0x00000010: "LOCKOUT",
99
+ 0x00000020: "PASSWORD_NOT_REQUIRED",
100
+ 0x00000040: "PASSWORD_CANT_CHANGE",
101
+ 0x00000080: "ENCRYPTED_TEXT_PASSWORD_ALLOWED",
102
+ 0x00200000: "TRUSTED_FOR_DELEGATION",
103
+ 0x00400000: "NOT_DELEGATED",
104
+ 0x00800000: "USE_DES_KEY_ONLY",
105
+ 0x01000000: "DONT_REQ_PREAUTH",
106
+ 0x04000000: "TRUSTED_TO_AUTH_FOR_DELEGATION"
107
+ }
108
+
109
+ def __init__(self, config: Optional[ADConfig] = None):
110
+ """
111
+ Initialize LDAP enumerator.
112
+
113
+ Args:
114
+ config: AD configuration
115
+ """
116
+ if not HAS_LDAP3:
117
+ raise ImportError("ldap3 is required. Install with: pip install ldap3")
118
+
119
+ self.config = config or ADConfig()
120
+ self.connection: Optional[Connection] = None
121
+ self.findings: List[LDAPFinding] = []
122
+
123
+ def _connect(self) -> bool:
124
+ """Establish LDAP connection."""
125
+ try:
126
+ server = Server(
127
+ self.config.dc_ip,
128
+ port=self.config.ldaps_port if self.config.use_ssl else self.config.ldap_port,
129
+ use_ssl=self.config.use_ssl,
130
+ get_info=ALL
131
+ )
132
+
133
+ # Determine authentication method
134
+ if self.config.credentials.use_kerberos:
135
+ self.connection = Connection(
136
+ server,
137
+ authentication=SASL,
138
+ sasl_mechanism=KERBEROS
139
+ )
140
+ else:
141
+ # NTLM authentication
142
+ user = self.config.credentials.get_auth_string()
143
+ password = self.config.credentials.get_password_or_hash()
144
+
145
+ self.connection = Connection(
146
+ server,
147
+ user=user,
148
+ password=password,
149
+ authentication=NTLM
150
+ )
151
+
152
+ return self.connection.bind()
153
+
154
+ except Exception as e:
155
+ print(f"[!] LDAP connection failed: {e}")
156
+ return False
157
+
158
+ def _search(
159
+ self,
160
+ search_filter: str,
161
+ attributes: List[str] = None,
162
+ search_base: str = None
163
+ ) -> List[Dict]:
164
+ """Execute LDAP search."""
165
+ if not self.connection:
166
+ return []
167
+
168
+ base = search_base or self.config.get_base_dn()
169
+ attrs = attributes or ["*"]
170
+
171
+ try:
172
+ self.connection.search(
173
+ search_base=base,
174
+ search_filter=search_filter,
175
+ attributes=attrs
176
+ )
177
+
178
+ results = []
179
+ for entry in self.connection.entries:
180
+ entry_dict = {
181
+ "dn": entry.entry_dn,
182
+ "attributes": dict(entry.entry_attributes_as_dict)
183
+ }
184
+ results.append(entry_dict)
185
+
186
+ return results
187
+
188
+ except Exception as e:
189
+ print(f"[!] LDAP search failed: {e}")
190
+ return []
191
+
192
+ def enumerate_users(self) -> List[Dict]:
193
+ """Enumerate domain users."""
194
+ users = self._search(
195
+ "(objectClass=user)",
196
+ attributes=[
197
+ "sAMAccountName", "userPrincipalName", "displayName",
198
+ "description", "memberOf", "userAccountControl",
199
+ "pwdLastSet", "lastLogonTimestamp", "adminCount",
200
+ "servicePrincipalName", "msDS-AllowedToDelegateTo"
201
+ ]
202
+ )
203
+
204
+ # Analyze users for security issues
205
+ for user in users:
206
+ attrs = user.get("attributes", {})
207
+ sam = attrs.get("sAMAccountName", [""])[0]
208
+ uac = attrs.get("userAccountControl", [0])[0]
209
+
210
+ # Check for dangerous flags
211
+ if isinstance(uac, int):
212
+ for flag_value, flag_name in self.DANGEROUS_FLAGS.items():
213
+ if uac & flag_value:
214
+ severity = "high" if flag_name in [
215
+ "DONT_REQ_PREAUTH", "TRUSTED_FOR_DELEGATION",
216
+ "PASSWORD_NOT_REQUIRED"
217
+ ] else "medium"
218
+
219
+ self.findings.append(LDAPFinding(
220
+ category="user",
221
+ severity=severity,
222
+ title=f"User has {flag_name} flag",
223
+ description=f"User {sam} has {flag_name} UAC flag set",
224
+ object_dn=user.get("dn", ""),
225
+ attributes={"flag": flag_name, "uac": uac},
226
+ remediation=f"Review and remove {flag_name} flag if not required"
227
+ ))
228
+
229
+ # Check for service accounts (Kerberoastable)
230
+ spns = attrs.get("servicePrincipalName", [])
231
+ if spns:
232
+ self.findings.append(LDAPFinding(
233
+ category="user",
234
+ severity="high",
235
+ title="Kerberoastable Account",
236
+ description=f"User {sam} has SPNs and may be Kerberoastable",
237
+ object_dn=user.get("dn", ""),
238
+ attributes={"spns": spns},
239
+ remediation="Use strong passwords for service accounts"
240
+ ))
241
+
242
+ # Check for delegation
243
+ delegation = attrs.get("msDS-AllowedToDelegateTo", [])
244
+ if delegation:
245
+ self.findings.append(LDAPFinding(
246
+ category="user",
247
+ severity="high",
248
+ title="Constrained Delegation",
249
+ description=f"User {sam} can delegate to specific services",
250
+ object_dn=user.get("dn", ""),
251
+ attributes={"delegation_targets": delegation},
252
+ remediation="Review delegation configuration"
253
+ ))
254
+
255
+ # Check admin count
256
+ if attrs.get("adminCount", [0])[0] == 1:
257
+ self.findings.append(LDAPFinding(
258
+ category="user",
259
+ severity="info",
260
+ title="Protected Admin Account",
261
+ description=f"User {sam} has adminCount=1 (was/is admin)",
262
+ object_dn=user.get("dn", ""),
263
+ attributes={},
264
+ remediation="Verify admin privileges are appropriate"
265
+ ))
266
+
267
+ return users
268
+
269
+ def enumerate_groups(self) -> List[Dict]:
270
+ """Enumerate domain groups."""
271
+ groups = self._search(
272
+ "(objectClass=group)",
273
+ attributes=[
274
+ "sAMAccountName", "description", "member",
275
+ "memberOf", "adminCount", "groupType"
276
+ ]
277
+ )
278
+
279
+ # Check for privileged groups
280
+ for group in groups:
281
+ attrs = group.get("attributes", {})
282
+ name = attrs.get("sAMAccountName", [""])[0]
283
+ members = attrs.get("member", [])
284
+
285
+ if name in self.PRIVILEGED_GROUPS:
286
+ self.findings.append(LDAPFinding(
287
+ category="group",
288
+ severity="info",
289
+ title=f"Privileged Group: {name}",
290
+ description=f"Group {name} has {len(members)} members",
291
+ object_dn=group.get("dn", ""),
292
+ attributes={"member_count": len(members)},
293
+ remediation="Review membership regularly"
294
+ ))
295
+
296
+ return groups
297
+
298
+ def enumerate_computers(self) -> List[Dict]:
299
+ """Enumerate domain computers."""
300
+ computers = self._search(
301
+ "(objectClass=computer)",
302
+ attributes=[
303
+ "sAMAccountName", "dNSHostName", "operatingSystem",
304
+ "operatingSystemVersion", "userAccountControl",
305
+ "lastLogonTimestamp", "servicePrincipalName",
306
+ "msDS-AllowedToDelegateTo"
307
+ ]
308
+ )
309
+
310
+ for computer in computers:
311
+ attrs = computer.get("attributes", {})
312
+ name = attrs.get("sAMAccountName", [""])[0]
313
+ os_name = attrs.get("operatingSystem", [""])[0]
314
+
315
+ # Check for legacy OS
316
+ if os_name and any(legacy in os_name.lower() for legacy in [
317
+ "2003", "2008", "xp", "vista", "windows 7"
318
+ ]):
319
+ self.findings.append(LDAPFinding(
320
+ category="computer",
321
+ severity="high",
322
+ title="Legacy Operating System",
323
+ description=f"Computer {name} runs {os_name}",
324
+ object_dn=computer.get("dn", ""),
325
+ attributes={"os": os_name},
326
+ remediation="Upgrade to supported operating system"
327
+ ))
328
+
329
+ # Check for unconstrained delegation
330
+ uac = attrs.get("userAccountControl", [0])[0]
331
+ if isinstance(uac, int) and (uac & 0x00080000): # TRUSTED_FOR_DELEGATION
332
+ self.findings.append(LDAPFinding(
333
+ category="computer",
334
+ severity="critical",
335
+ title="Unconstrained Delegation",
336
+ description=f"Computer {name} has unconstrained delegation",
337
+ object_dn=computer.get("dn", ""),
338
+ attributes={},
339
+ remediation="Remove unconstrained delegation or use constrained"
340
+ ))
341
+
342
+ return computers
343
+
344
+ def enumerate_gpos(self) -> List[Dict]:
345
+ """Enumerate Group Policy Objects."""
346
+ gpos = self._search(
347
+ "(objectClass=groupPolicyContainer)",
348
+ attributes=[
349
+ "displayName", "gPCFileSysPath", "versionNumber",
350
+ "gPCFunctionalityVersion"
351
+ ]
352
+ )
353
+
354
+ return gpos
355
+
356
+ def enumerate_trusts(self) -> List[Dict]:
357
+ """Enumerate domain trusts."""
358
+ trusts = self._search(
359
+ "(objectClass=trustedDomain)",
360
+ attributes=[
361
+ "name", "trustDirection", "trustType",
362
+ "trustAttributes", "securityIdentifier"
363
+ ]
364
+ )
365
+
366
+ for trust in trusts:
367
+ attrs = trust.get("attributes", {})
368
+ name = attrs.get("name", [""])[0]
369
+ direction = attrs.get("trustDirection", [0])[0]
370
+
371
+ direction_map = {
372
+ 0: "Disabled",
373
+ 1: "Inbound",
374
+ 2: "Outbound",
375
+ 3: "Bidirectional"
376
+ }
377
+
378
+ self.findings.append(LDAPFinding(
379
+ category="trust",
380
+ severity="info",
381
+ title=f"Domain Trust: {name}",
382
+ description=f"Trust direction: {direction_map.get(direction, 'Unknown')}",
383
+ object_dn=trust.get("dn", ""),
384
+ attributes={"direction": direction_map.get(direction)},
385
+ remediation="Review trust relationships regularly"
386
+ ))
387
+
388
+ return trusts
389
+
390
+ def enumerate_asreproastable(self) -> List[Dict]:
391
+ """Find users vulnerable to AS-REP roasting."""
392
+ # Users with DONT_REQ_PREAUTH flag
393
+ users = self._search(
394
+ "(&(objectClass=user)(userAccountControl:1.2.840.113556.1.4.803:=4194304))",
395
+ attributes=["sAMAccountName", "userPrincipalName", "description"]
396
+ )
397
+
398
+ for user in users:
399
+ attrs = user.get("attributes", {})
400
+ sam = attrs.get("sAMAccountName", [""])[0]
401
+
402
+ self.findings.append(LDAPFinding(
403
+ category="user",
404
+ severity="high",
405
+ title="AS-REP Roastable Account",
406
+ description=f"User {sam} does not require Kerberos pre-authentication",
407
+ object_dn=user.get("dn", ""),
408
+ attributes={},
409
+ remediation="Enable Kerberos pre-authentication"
410
+ ))
411
+
412
+ return users
413
+
414
+ def enumerate_laps(self) -> List[Dict]:
415
+ """Check for LAPS deployment."""
416
+ # Check if LAPS schema is present
417
+ computers = self._search(
418
+ "(&(objectClass=computer)(ms-Mcs-AdmPwd=*))",
419
+ attributes=["sAMAccountName", "ms-Mcs-AdmPwd", "ms-Mcs-AdmPwdExpirationTime"]
420
+ )
421
+
422
+ if computers:
423
+ self.findings.append(LDAPFinding(
424
+ category="misc",
425
+ severity="info",
426
+ title="LAPS Deployed",
427
+ description=f"LAPS is deployed on {len(computers)} computers",
428
+ object_dn="",
429
+ attributes={"computer_count": len(computers)},
430
+ remediation="Ensure LAPS passwords are retrieved securely"
431
+ ))
432
+ else:
433
+ self.findings.append(LDAPFinding(
434
+ category="misc",
435
+ severity="medium",
436
+ title="LAPS Not Detected",
437
+ description="LAPS does not appear to be deployed",
438
+ object_dn="",
439
+ attributes={},
440
+ remediation="Consider deploying LAPS for local admin password management"
441
+ ))
442
+
443
+ return computers
444
+
445
+ async def enumerate_all(self) -> LDAPEnumResult:
446
+ """Run full LDAP enumeration."""
447
+ started_at = datetime.now(timezone.utc).isoformat()
448
+ start_time = asyncio.get_event_loop().time()
449
+
450
+ if not self._connect():
451
+ return LDAPEnumResult(
452
+ domain=self.config.domain,
453
+ status="failed",
454
+ started_at=started_at,
455
+ finished_at=datetime.now(timezone.utc).isoformat(),
456
+ duration=0,
457
+ users=[],
458
+ groups=[],
459
+ computers=[],
460
+ gpos=[],
461
+ trusts=[],
462
+ findings=[],
463
+ metadata={"error": "LDAP connection failed"}
464
+ )
465
+
466
+ # Run enumeration
467
+ users = self.enumerate_users()
468
+ groups = self.enumerate_groups()
469
+ computers = self.enumerate_computers()
470
+ gpos = self.enumerate_gpos()
471
+ trusts = self.enumerate_trusts()
472
+ self.enumerate_asreproastable()
473
+ self.enumerate_laps()
474
+
475
+ # Close connection
476
+ if self.connection:
477
+ self.connection.unbind()
478
+
479
+ finished_at = datetime.now(timezone.utc).isoformat()
480
+ duration = asyncio.get_event_loop().time() - start_time
481
+
482
+ return LDAPEnumResult(
483
+ domain=self.config.domain,
484
+ status="completed",
485
+ started_at=started_at,
486
+ finished_at=finished_at,
487
+ duration=duration,
488
+ users=users,
489
+ groups=groups,
490
+ computers=computers,
491
+ gpos=gpos,
492
+ trusts=trusts,
493
+ findings=self.findings,
494
+ metadata={
495
+ "user_count": len(users),
496
+ "group_count": len(groups),
497
+ "computer_count": len(computers),
498
+ "finding_count": len(self.findings)
499
+ }
500
+ )
501
+
502
+
503
+ # Convenience function
504
+ async def enumerate_ldap(
505
+ domain: str,
506
+ dc_ip: str,
507
+ username: str,
508
+ password: str,
509
+ **kwargs
510
+ ) -> LDAPEnumResult:
511
+ """
512
+ Quick LDAP enumeration.
513
+
514
+ Args:
515
+ domain: AD domain
516
+ dc_ip: Domain Controller IP
517
+ username: Username
518
+ password: Password
519
+ **kwargs: Additional options
520
+
521
+ Returns:
522
+ LDAPEnumResult
523
+ """
524
+ config = get_ad_config(
525
+ domain=domain,
526
+ dc_ip=dc_ip,
527
+ username=username,
528
+ password=password,
529
+ **kwargs
530
+ )
531
+
532
+ enum = LDAPEnum(config)
533
+ return await enum.enumerate_all()