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.

Files changed (165) 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 +24 -0
  6. aipt_v2/agents/base.py +520 -0
  7. aipt_v2/agents/ptt.py +406 -0
  8. aipt_v2/agents/state.py +168 -0
  9. aipt_v2/app.py +960 -0
  10. aipt_v2/browser/__init__.py +31 -0
  11. aipt_v2/browser/automation.py +458 -0
  12. aipt_v2/browser/crawler.py +453 -0
  13. aipt_v2/cli.py +321 -0
  14. aipt_v2/compliance/__init__.py +71 -0
  15. aipt_v2/compliance/compliance_report.py +449 -0
  16. aipt_v2/compliance/framework_mapper.py +424 -0
  17. aipt_v2/compliance/nist_mapping.py +345 -0
  18. aipt_v2/compliance/owasp_mapping.py +330 -0
  19. aipt_v2/compliance/pci_mapping.py +297 -0
  20. aipt_v2/config.py +288 -0
  21. aipt_v2/core/__init__.py +43 -0
  22. aipt_v2/core/agent.py +630 -0
  23. aipt_v2/core/llm.py +395 -0
  24. aipt_v2/core/memory.py +305 -0
  25. aipt_v2/core/ptt.py +329 -0
  26. aipt_v2/database/__init__.py +14 -0
  27. aipt_v2/database/models.py +232 -0
  28. aipt_v2/database/repository.py +384 -0
  29. aipt_v2/docker/__init__.py +23 -0
  30. aipt_v2/docker/builder.py +260 -0
  31. aipt_v2/docker/manager.py +222 -0
  32. aipt_v2/docker/sandbox.py +371 -0
  33. aipt_v2/evasion/__init__.py +58 -0
  34. aipt_v2/evasion/request_obfuscator.py +272 -0
  35. aipt_v2/evasion/tls_fingerprint.py +285 -0
  36. aipt_v2/evasion/ua_rotator.py +301 -0
  37. aipt_v2/evasion/waf_bypass.py +439 -0
  38. aipt_v2/execution/__init__.py +23 -0
  39. aipt_v2/execution/executor.py +302 -0
  40. aipt_v2/execution/parser.py +544 -0
  41. aipt_v2/execution/terminal.py +337 -0
  42. aipt_v2/health.py +437 -0
  43. aipt_v2/intelligence/__init__.py +85 -0
  44. aipt_v2/intelligence/auth.py +520 -0
  45. aipt_v2/intelligence/chaining.py +775 -0
  46. aipt_v2/intelligence/cve_aipt.py +334 -0
  47. aipt_v2/intelligence/cve_info.py +1111 -0
  48. aipt_v2/intelligence/rag.py +239 -0
  49. aipt_v2/intelligence/scope.py +442 -0
  50. aipt_v2/intelligence/searchers/__init__.py +5 -0
  51. aipt_v2/intelligence/searchers/exploitdb_searcher.py +523 -0
  52. aipt_v2/intelligence/searchers/github_searcher.py +467 -0
  53. aipt_v2/intelligence/searchers/google_searcher.py +281 -0
  54. aipt_v2/intelligence/tools.json +443 -0
  55. aipt_v2/intelligence/triage.py +670 -0
  56. aipt_v2/interface/__init__.py +5 -0
  57. aipt_v2/interface/cli.py +230 -0
  58. aipt_v2/interface/main.py +501 -0
  59. aipt_v2/interface/tui.py +1276 -0
  60. aipt_v2/interface/utils.py +583 -0
  61. aipt_v2/llm/__init__.py +39 -0
  62. aipt_v2/llm/config.py +26 -0
  63. aipt_v2/llm/llm.py +514 -0
  64. aipt_v2/llm/memory.py +214 -0
  65. aipt_v2/llm/request_queue.py +89 -0
  66. aipt_v2/llm/utils.py +89 -0
  67. aipt_v2/models/__init__.py +15 -0
  68. aipt_v2/models/findings.py +295 -0
  69. aipt_v2/models/phase_result.py +224 -0
  70. aipt_v2/models/scan_config.py +207 -0
  71. aipt_v2/monitoring/grafana/dashboards/aipt-dashboard.json +355 -0
  72. aipt_v2/monitoring/grafana/dashboards/default.yml +17 -0
  73. aipt_v2/monitoring/grafana/datasources/prometheus.yml +17 -0
  74. aipt_v2/monitoring/prometheus.yml +60 -0
  75. aipt_v2/orchestration/__init__.py +52 -0
  76. aipt_v2/orchestration/pipeline.py +398 -0
  77. aipt_v2/orchestration/progress.py +300 -0
  78. aipt_v2/orchestration/scheduler.py +296 -0
  79. aipt_v2/orchestrator.py +2284 -0
  80. aipt_v2/payloads/__init__.py +27 -0
  81. aipt_v2/payloads/cmdi.py +150 -0
  82. aipt_v2/payloads/sqli.py +263 -0
  83. aipt_v2/payloads/ssrf.py +204 -0
  84. aipt_v2/payloads/templates.py +222 -0
  85. aipt_v2/payloads/traversal.py +166 -0
  86. aipt_v2/payloads/xss.py +204 -0
  87. aipt_v2/prompts/__init__.py +60 -0
  88. aipt_v2/proxy/__init__.py +29 -0
  89. aipt_v2/proxy/history.py +352 -0
  90. aipt_v2/proxy/interceptor.py +452 -0
  91. aipt_v2/recon/__init__.py +44 -0
  92. aipt_v2/recon/dns.py +241 -0
  93. aipt_v2/recon/osint.py +367 -0
  94. aipt_v2/recon/subdomain.py +372 -0
  95. aipt_v2/recon/tech_detect.py +311 -0
  96. aipt_v2/reports/__init__.py +17 -0
  97. aipt_v2/reports/generator.py +313 -0
  98. aipt_v2/reports/html_report.py +378 -0
  99. aipt_v2/runtime/__init__.py +44 -0
  100. aipt_v2/runtime/base.py +30 -0
  101. aipt_v2/runtime/docker.py +401 -0
  102. aipt_v2/runtime/local.py +346 -0
  103. aipt_v2/runtime/tool_server.py +205 -0
  104. aipt_v2/scanners/__init__.py +28 -0
  105. aipt_v2/scanners/base.py +273 -0
  106. aipt_v2/scanners/nikto.py +244 -0
  107. aipt_v2/scanners/nmap.py +402 -0
  108. aipt_v2/scanners/nuclei.py +273 -0
  109. aipt_v2/scanners/web.py +454 -0
  110. aipt_v2/scripts/security_audit.py +366 -0
  111. aipt_v2/telemetry/__init__.py +7 -0
  112. aipt_v2/telemetry/tracer.py +347 -0
  113. aipt_v2/terminal/__init__.py +28 -0
  114. aipt_v2/terminal/executor.py +400 -0
  115. aipt_v2/terminal/sandbox.py +350 -0
  116. aipt_v2/tools/__init__.py +44 -0
  117. aipt_v2/tools/active_directory/__init__.py +78 -0
  118. aipt_v2/tools/active_directory/ad_config.py +238 -0
  119. aipt_v2/tools/active_directory/bloodhound_wrapper.py +447 -0
  120. aipt_v2/tools/active_directory/kerberos_attacks.py +430 -0
  121. aipt_v2/tools/active_directory/ldap_enum.py +533 -0
  122. aipt_v2/tools/active_directory/smb_attacks.py +505 -0
  123. aipt_v2/tools/agents_graph/__init__.py +19 -0
  124. aipt_v2/tools/agents_graph/agents_graph_actions.py +69 -0
  125. aipt_v2/tools/api_security/__init__.py +76 -0
  126. aipt_v2/tools/api_security/api_discovery.py +608 -0
  127. aipt_v2/tools/api_security/graphql_scanner.py +622 -0
  128. aipt_v2/tools/api_security/jwt_analyzer.py +577 -0
  129. aipt_v2/tools/api_security/openapi_fuzzer.py +761 -0
  130. aipt_v2/tools/browser/__init__.py +5 -0
  131. aipt_v2/tools/browser/browser_actions.py +238 -0
  132. aipt_v2/tools/browser/browser_instance.py +535 -0
  133. aipt_v2/tools/browser/tab_manager.py +344 -0
  134. aipt_v2/tools/cloud/__init__.py +70 -0
  135. aipt_v2/tools/cloud/cloud_config.py +273 -0
  136. aipt_v2/tools/cloud/cloud_scanner.py +639 -0
  137. aipt_v2/tools/cloud/prowler_tool.py +571 -0
  138. aipt_v2/tools/cloud/scoutsuite_tool.py +359 -0
  139. aipt_v2/tools/executor.py +307 -0
  140. aipt_v2/tools/parser.py +408 -0
  141. aipt_v2/tools/proxy/__init__.py +5 -0
  142. aipt_v2/tools/proxy/proxy_actions.py +103 -0
  143. aipt_v2/tools/proxy/proxy_manager.py +789 -0
  144. aipt_v2/tools/registry.py +196 -0
  145. aipt_v2/tools/scanners/__init__.py +343 -0
  146. aipt_v2/tools/scanners/acunetix_tool.py +712 -0
  147. aipt_v2/tools/scanners/burp_tool.py +631 -0
  148. aipt_v2/tools/scanners/config.py +156 -0
  149. aipt_v2/tools/scanners/nessus_tool.py +588 -0
  150. aipt_v2/tools/scanners/zap_tool.py +612 -0
  151. aipt_v2/tools/terminal/__init__.py +5 -0
  152. aipt_v2/tools/terminal/terminal_actions.py +37 -0
  153. aipt_v2/tools/terminal/terminal_manager.py +153 -0
  154. aipt_v2/tools/terminal/terminal_session.py +449 -0
  155. aipt_v2/tools/tool_processing.py +108 -0
  156. aipt_v2/utils/__init__.py +17 -0
  157. aipt_v2/utils/logging.py +201 -0
  158. aipt_v2/utils/model_manager.py +187 -0
  159. aipt_v2/utils/searchers/__init__.py +269 -0
  160. aiptx-2.0.2.dist-info/METADATA +324 -0
  161. aiptx-2.0.2.dist-info/RECORD +165 -0
  162. aiptx-2.0.2.dist-info/WHEEL +5 -0
  163. aiptx-2.0.2.dist-info/entry_points.txt +7 -0
  164. aiptx-2.0.2.dist-info/licenses/LICENSE +21 -0
  165. aiptx-2.0.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,775 @@
1
+ """
2
+ AIPT Vulnerability Chaining Engine
3
+
4
+ Analyzes findings to identify attack chains - sequences of vulnerabilities
5
+ that can be combined for greater impact.
6
+
7
+ Example Chains:
8
+ - SSRF → Internal Service Access → Sensitive Data Exposure
9
+ - SQL Injection → Authentication Bypass → Privilege Escalation
10
+ - XSS → Session Hijacking → Account Takeover
11
+ - File Upload → RCE → Full Server Compromise
12
+
13
+ This module helps pentesters demonstrate real-world attack impact
14
+ by showing how multiple "medium" findings can combine into "critical" risks.
15
+ """
16
+ from __future__ import annotations
17
+
18
+ import logging
19
+ from dataclasses import dataclass, field
20
+ from datetime import datetime
21
+ from enum import Enum
22
+ from typing import Any
23
+ import hashlib
24
+
25
+ # Works with dict-based findings, not typed Finding class
26
+
27
+
28
+ logger = logging.getLogger(__name__)
29
+
30
+
31
+ # ============================================================================
32
+ # Vulnerability Types (for chain rules)
33
+ # ============================================================================
34
+
35
+ class VulnerabilityType(Enum):
36
+ """Types of vulnerabilities for chain detection"""
37
+ # Injection
38
+ SQL_INJECTION = "sql_injection"
39
+ XSS_STORED = "xss_stored"
40
+ XSS_REFLECTED = "xss_reflected"
41
+ COMMAND_INJECTION = "command_injection"
42
+ LDAP_INJECTION = "ldap_injection"
43
+ XPATH_INJECTION = "xpath_injection"
44
+
45
+ # Authentication/Authorization
46
+ AUTH_BYPASS = "auth_bypass"
47
+ BROKEN_AUTH = "broken_authentication"
48
+ PRIVILEGE_ESCALATION = "privilege_escalation"
49
+ IDOR = "idor"
50
+
51
+ # Information Disclosure
52
+ INFORMATION_DISCLOSURE = "information_disclosure"
53
+ PATH_TRAVERSAL = "path_traversal"
54
+ SOURCE_CODE_DISCLOSURE = "source_code_disclosure"
55
+
56
+ # Server-Side
57
+ SSRF = "ssrf"
58
+ RCE = "rce"
59
+ FILE_UPLOAD = "file_upload"
60
+ FILE_INCLUSION = "file_inclusion"
61
+ LFI = "lfi"
62
+ RFI = "rfi"
63
+ DESERIALIZATION = "deserialization"
64
+ INSECURE_DESERIALIZATION = "insecure_deserialization"
65
+ XXE = "xxe"
66
+
67
+ # Client-Side
68
+ CSRF = "csrf"
69
+ CLICKJACKING = "clickjacking"
70
+ OPEN_REDIRECT = "open_redirect"
71
+
72
+ # Cryptographic
73
+ WEAK_CRYPTO = "weak_cryptography"
74
+ HARDCODED_SECRETS = "hardcoded_secrets"
75
+
76
+ # Configuration
77
+ MISCONFIGURATION = "misconfiguration"
78
+ DEFAULT_CREDENTIALS = "default_credentials"
79
+ EXPOSED_ADMIN = "exposed_admin_panel"
80
+
81
+ # Other
82
+ DOS = "denial_of_service"
83
+ BUSINESS_LOGIC = "business_logic"
84
+ UNKNOWN = "unknown"
85
+
86
+
87
+ class ChainType(Enum):
88
+ """Types of vulnerability chains"""
89
+ DATA_EXFILTRATION = "data_exfiltration"
90
+ ACCOUNT_TAKEOVER = "account_takeover"
91
+ PRIVILEGE_ESCALATION = "privilege_escalation"
92
+ REMOTE_CODE_EXECUTION = "remote_code_execution"
93
+ INTERNAL_NETWORK_ACCESS = "internal_network_access"
94
+ DENIAL_OF_SERVICE = "denial_of_service"
95
+ SUPPLY_CHAIN = "supply_chain"
96
+ LATERAL_MOVEMENT = "lateral_movement"
97
+
98
+
99
+ class ChainImpact(Enum):
100
+ """Business impact levels"""
101
+ CATASTROPHIC = "catastrophic" # Full compromise, data breach
102
+ SEVERE = "severe" # Significant access, major data exposure
103
+ SIGNIFICANT = "significant" # Limited access, some data exposure
104
+ MODERATE = "moderate" # Potential for escalation
105
+ MINIMAL = "minimal" # Low impact chain
106
+
107
+
108
+ @dataclass
109
+ class ChainLink:
110
+ """A single step in an attack chain"""
111
+ finding: Finding
112
+ step_number: int
113
+ action: str # What the attacker does at this step
114
+ outcome: str # What they achieve
115
+ prerequisites: list[str] = field(default_factory=list)
116
+
117
+ def to_dict(self) -> dict[str, Any]:
118
+ return {
119
+ "step": self.step_number,
120
+ "vulnerability": self.finding.title,
121
+ "severity": self.finding.severity.value,
122
+ "url": self.finding.url,
123
+ "action": self.action,
124
+ "outcome": self.outcome,
125
+ "prerequisites": self.prerequisites,
126
+ }
127
+
128
+
129
+ @dataclass
130
+ class AttackChain:
131
+ """A complete attack chain combining multiple vulnerabilities"""
132
+ chain_id: str
133
+ name: str
134
+ chain_type: ChainType
135
+ links: list[ChainLink]
136
+
137
+ # Impact assessment
138
+ combined_severity: Severity
139
+ impact: ChainImpact
140
+ business_impact: str
141
+
142
+ # Metadata
143
+ confidence: float # 0.0 to 1.0
144
+ created_at: datetime = field(default_factory=datetime.utcnow)
145
+
146
+ # Narrative
147
+ attack_narrative: str = ""
148
+ remediation_priority: str = ""
149
+
150
+ @property
151
+ def length(self) -> int:
152
+ return len(self.links)
153
+
154
+ @property
155
+ def entry_point(self) -> Finding:
156
+ return self.links[0].finding if self.links else None
157
+
158
+ @property
159
+ def final_outcome(self) -> str:
160
+ return self.links[-1].outcome if self.links else ""
161
+
162
+ def get_cvss_amplification(self) -> float:
163
+ """
164
+ Calculate how much the chain amplifies individual CVSS scores.
165
+ Chains often have impact greater than sum of parts.
166
+ """
167
+ if not self.links:
168
+ return 1.0
169
+
170
+ individual_max = max(
171
+ (l.finding.cvss_score or 0) for l in self.links
172
+ )
173
+
174
+ # Chain severity bonus based on type
175
+ chain_bonus = {
176
+ ChainType.REMOTE_CODE_EXECUTION: 2.0,
177
+ ChainType.DATA_EXFILTRATION: 1.8,
178
+ ChainType.ACCOUNT_TAKEOVER: 1.7,
179
+ ChainType.PRIVILEGE_ESCALATION: 1.6,
180
+ ChainType.INTERNAL_NETWORK_ACCESS: 1.5,
181
+ ChainType.LATERAL_MOVEMENT: 1.4,
182
+ ChainType.SUPPLY_CHAIN: 1.9,
183
+ ChainType.DENIAL_OF_SERVICE: 1.2,
184
+ }
185
+
186
+ return chain_bonus.get(self.chain_type, 1.3)
187
+
188
+ def to_dict(self) -> dict[str, Any]:
189
+ return {
190
+ "chain_id": self.chain_id,
191
+ "name": self.name,
192
+ "chain_type": self.chain_type.value,
193
+ "length": self.length,
194
+ "combined_severity": self.combined_severity.value,
195
+ "impact": self.impact.value,
196
+ "business_impact": self.business_impact,
197
+ "confidence": self.confidence,
198
+ "cvss_amplification": self.get_cvss_amplification(),
199
+ "links": [link.to_dict() for link in self.links],
200
+ "attack_narrative": self.attack_narrative,
201
+ "remediation_priority": self.remediation_priority,
202
+ }
203
+
204
+
205
+ # ============================================================================
206
+ # Chain Detection Rules
207
+ # ============================================================================
208
+
209
+ # Define which vulnerability types can lead to others
210
+ CHAIN_RULES: dict[VulnerabilityType, list[tuple[VulnerabilityType, str, str]]] = {
211
+ # SSRF can lead to...
212
+ VulnerabilityType.SSRF: [
213
+ (VulnerabilityType.INFORMATION_DISCLOSURE,
214
+ "Use SSRF to access internal endpoints",
215
+ "Read internal service data"),
216
+ (VulnerabilityType.RCE,
217
+ "SSRF to internal admin panel with RCE",
218
+ "Execute commands on internal systems"),
219
+ (VulnerabilityType.SQL_INJECTION,
220
+ "SSRF to internal database interface",
221
+ "Query internal databases"),
222
+ ],
223
+
224
+ # SQL Injection can lead to...
225
+ VulnerabilityType.SQL_INJECTION: [
226
+ (VulnerabilityType.AUTH_BYPASS,
227
+ "Extract credentials from database",
228
+ "Authenticate as any user"),
229
+ (VulnerabilityType.INFORMATION_DISCLOSURE,
230
+ "Dump database tables",
231
+ "Access all stored data"),
232
+ (VulnerabilityType.RCE,
233
+ "SQL injection to xp_cmdshell or file write",
234
+ "Execute system commands"),
235
+ (VulnerabilityType.PRIVILEGE_ESCALATION,
236
+ "Modify user roles in database",
237
+ "Elevate to admin privileges"),
238
+ ],
239
+
240
+ # XSS can lead to...
241
+ VulnerabilityType.XSS_STORED: [
242
+ (VulnerabilityType.AUTH_BYPASS,
243
+ "Steal session cookies via XSS",
244
+ "Hijack user sessions"),
245
+ (VulnerabilityType.CSRF,
246
+ "Use XSS to perform actions as victim",
247
+ "Execute privileged actions"),
248
+ (VulnerabilityType.INFORMATION_DISCLOSURE,
249
+ "Exfiltrate page data via XSS",
250
+ "Steal sensitive displayed information"),
251
+ ],
252
+ VulnerabilityType.XSS_REFLECTED: [
253
+ (VulnerabilityType.AUTH_BYPASS,
254
+ "Phish credentials via reflected XSS",
255
+ "Capture user credentials"),
256
+ ],
257
+
258
+ # File Upload can lead to...
259
+ VulnerabilityType.FILE_UPLOAD: [
260
+ (VulnerabilityType.RCE,
261
+ "Upload web shell",
262
+ "Execute arbitrary code"),
263
+ (VulnerabilityType.FILE_INCLUSION,
264
+ "Upload malicious include file",
265
+ "Include and execute uploaded code"),
266
+ ],
267
+
268
+ # File Inclusion can lead to...
269
+ VulnerabilityType.FILE_INCLUSION: [
270
+ (VulnerabilityType.INFORMATION_DISCLOSURE,
271
+ "Read sensitive configuration files",
272
+ "Access credentials and secrets"),
273
+ (VulnerabilityType.RCE,
274
+ "Include remote file or log poisoning",
275
+ "Execute arbitrary code"),
276
+ ],
277
+
278
+ # Auth Bypass can lead to...
279
+ VulnerabilityType.AUTH_BYPASS: [
280
+ (VulnerabilityType.PRIVILEGE_ESCALATION,
281
+ "Access admin functionality",
282
+ "Perform administrative actions"),
283
+ (VulnerabilityType.INFORMATION_DISCLOSURE,
284
+ "Access protected user data",
285
+ "View all user information"),
286
+ (VulnerabilityType.IDOR,
287
+ "Access other users' resources",
288
+ "Modify or steal user data"),
289
+ ],
290
+
291
+ # IDOR can lead to...
292
+ VulnerabilityType.IDOR: [
293
+ (VulnerabilityType.INFORMATION_DISCLOSURE,
294
+ "Enumerate and access all records",
295
+ "Mass data extraction"),
296
+ (VulnerabilityType.PRIVILEGE_ESCALATION,
297
+ "Modify own role/permissions",
298
+ "Elevate account privileges"),
299
+ ],
300
+
301
+ # Open Redirect can lead to...
302
+ VulnerabilityType.OPEN_REDIRECT: [
303
+ (VulnerabilityType.AUTH_BYPASS,
304
+ "Redirect OAuth flow to attacker",
305
+ "Steal authentication tokens"),
306
+ (VulnerabilityType.XSS_REFLECTED,
307
+ "Chain with XSS via redirect",
308
+ "Execute JavaScript in context"),
309
+ ],
310
+
311
+ # XXE can lead to...
312
+ VulnerabilityType.XXE: [
313
+ (VulnerabilityType.INFORMATION_DISCLOSURE,
314
+ "Read local files via XXE",
315
+ "Access server files"),
316
+ (VulnerabilityType.SSRF,
317
+ "XXE to make server-side requests",
318
+ "Access internal network"),
319
+ (VulnerabilityType.RCE,
320
+ "XXE with expect:// wrapper",
321
+ "Execute system commands"),
322
+ ],
323
+
324
+ # Command Injection leads to RCE
325
+ VulnerabilityType.COMMAND_INJECTION: [
326
+ (VulnerabilityType.RCE,
327
+ "Execute arbitrary commands",
328
+ "Full system compromise"),
329
+ (VulnerabilityType.INFORMATION_DISCLOSURE,
330
+ "Read files and environment",
331
+ "Access secrets and configuration"),
332
+ ],
333
+
334
+ # Weak Crypto can lead to...
335
+ VulnerabilityType.WEAK_CRYPTO: [
336
+ (VulnerabilityType.AUTH_BYPASS,
337
+ "Crack or forge authentication tokens",
338
+ "Authenticate as any user"),
339
+ (VulnerabilityType.INFORMATION_DISCLOSURE,
340
+ "Decrypt sensitive data",
341
+ "Access protected information"),
342
+ ],
343
+
344
+ # Insecure Deserialization
345
+ VulnerabilityType.INSECURE_DESERIALIZATION: [
346
+ (VulnerabilityType.RCE,
347
+ "Craft malicious serialized object",
348
+ "Execute arbitrary code"),
349
+ ],
350
+ }
351
+
352
+
353
+ class VulnerabilityChainer:
354
+ """
355
+ Analyzes findings to identify attack chains.
356
+
357
+ This class takes a list of individual findings and identifies
358
+ how they can be combined into more impactful attack scenarios.
359
+
360
+ Example:
361
+ chainer = VulnerabilityChainer()
362
+ chains = chainer.find_chains(findings)
363
+ for chain in chains:
364
+ print(f"Attack Chain: {chain.name}")
365
+ print(f"Impact: {chain.impact.value}")
366
+ for link in chain.links:
367
+ print(f" Step {link.step_number}: {link.action}")
368
+ """
369
+
370
+ def __init__(self, max_chain_length: int = 5):
371
+ self.max_chain_length = max_chain_length
372
+ self._chain_counter = 0
373
+
374
+ def find_chains(
375
+ self,
376
+ findings: list[Finding],
377
+ min_confidence: float = 0.5,
378
+ ) -> list[AttackChain]:
379
+ """
380
+ Find all possible attack chains in the findings.
381
+
382
+ Args:
383
+ findings: List of vulnerability findings
384
+ min_confidence: Minimum confidence threshold for chains
385
+
386
+ Returns:
387
+ List of identified attack chains, sorted by impact
388
+ """
389
+ if not findings:
390
+ return []
391
+
392
+ chains = []
393
+
394
+ # Group findings by vulnerability type
395
+ by_type: dict[VulnerabilityType, list[Finding]] = {}
396
+ for f in findings:
397
+ if f.vuln_type not in by_type:
398
+ by_type[f.vuln_type] = []
399
+ by_type[f.vuln_type].append(f)
400
+
401
+ # Find chains starting from each finding
402
+ for finding in findings:
403
+ found_chains = self._find_chains_from(finding, by_type, [])
404
+ chains.extend(found_chains)
405
+
406
+ # Deduplicate and filter
407
+ unique_chains = self._deduplicate_chains(chains)
408
+ filtered_chains = [c for c in unique_chains if c.confidence >= min_confidence]
409
+
410
+ # Sort by impact (most severe first)
411
+ impact_order = [
412
+ ChainImpact.CATASTROPHIC,
413
+ ChainImpact.SEVERE,
414
+ ChainImpact.SIGNIFICANT,
415
+ ChainImpact.MODERATE,
416
+ ChainImpact.MINIMAL,
417
+ ]
418
+ filtered_chains.sort(key=lambda c: impact_order.index(c.impact))
419
+
420
+ logger.info(f"Found {len(filtered_chains)} attack chains")
421
+ return filtered_chains
422
+
423
+ def _find_chains_from(
424
+ self,
425
+ start: Finding,
426
+ by_type: dict[VulnerabilityType, list[Finding]],
427
+ current_path: list[Finding],
428
+ ) -> list[AttackChain]:
429
+ """Recursively find chains starting from a finding"""
430
+ chains = []
431
+
432
+ # Prevent cycles and limit depth
433
+ if start in current_path or len(current_path) >= self.max_chain_length:
434
+ return chains
435
+
436
+ current_path = current_path + [start]
437
+
438
+ # Get possible next steps from chain rules
439
+ next_types = CHAIN_RULES.get(start.vuln_type, [])
440
+
441
+ for next_type, action, outcome in next_types:
442
+ # Find findings that match the next type
443
+ matching = by_type.get(next_type, [])
444
+
445
+ for next_finding in matching:
446
+ # Check if findings are related (same host/path)
447
+ if self._findings_related(start, next_finding):
448
+ # Recurse to find longer chains
449
+ deeper_chains = self._find_chains_from(
450
+ next_finding, by_type, current_path
451
+ )
452
+ chains.extend(deeper_chains)
453
+
454
+ # Even without a matching finding, if we have 2+ steps, record the chain
455
+ if len(current_path) >= 2:
456
+ chain = self._build_chain(current_path)
457
+ if chain:
458
+ chains.append(chain)
459
+
460
+ return chains
461
+
462
+ def _findings_related(self, f1: Finding, f2: Finding) -> bool:
463
+ """Check if two findings are related (could be chained)"""
464
+ # Same host
465
+ try:
466
+ from urllib.parse import urlparse
467
+ host1 = urlparse(f1.url).netloc
468
+ host2 = urlparse(f2.url).netloc
469
+ if host1 and host2 and host1 == host2:
470
+ return True
471
+ except Exception:
472
+ pass
473
+
474
+ # Same parameter
475
+ if f1.parameter and f2.parameter and f1.parameter == f2.parameter:
476
+ return True
477
+
478
+ # If same source scanner found both, likely related
479
+ if f1.source == f2.source:
480
+ return True
481
+
482
+ return False
483
+
484
+ def _build_chain(self, findings: list[Finding]) -> AttackChain | None:
485
+ """Build an AttackChain from a list of findings"""
486
+ if len(findings) < 2:
487
+ return None
488
+
489
+ self._chain_counter += 1
490
+ chain_id = f"chain_{self._chain_counter:04d}"
491
+
492
+ # Build links
493
+ links = []
494
+ for i, finding in enumerate(findings):
495
+ # Get action/outcome from chain rules
496
+ action, outcome = self._get_step_details(
497
+ finding,
498
+ findings[i + 1] if i + 1 < len(findings) else None
499
+ )
500
+
501
+ link = ChainLink(
502
+ finding=finding,
503
+ step_number=i + 1,
504
+ action=action,
505
+ outcome=outcome,
506
+ prerequisites=[f"Step {j+1} completed" for j in range(i)],
507
+ )
508
+ links.append(link)
509
+
510
+ # Determine chain type and impact
511
+ chain_type = self._classify_chain_type(findings)
512
+ impact = self._assess_impact(findings, chain_type)
513
+ combined_severity = self._calculate_combined_severity(findings, chain_type)
514
+
515
+ # Generate narrative
516
+ narrative = self._generate_narrative(links, chain_type)
517
+
518
+ # Calculate confidence
519
+ confidence = self._calculate_confidence(findings)
520
+
521
+ return AttackChain(
522
+ chain_id=chain_id,
523
+ name=self._generate_chain_name(chain_type, findings),
524
+ chain_type=chain_type,
525
+ links=links,
526
+ combined_severity=combined_severity,
527
+ impact=impact,
528
+ business_impact=self._describe_business_impact(chain_type, impact),
529
+ confidence=confidence,
530
+ attack_narrative=narrative,
531
+ remediation_priority=self._suggest_remediation(links),
532
+ )
533
+
534
+ def _get_step_details(
535
+ self,
536
+ current: Finding,
537
+ next_finding: Finding | None,
538
+ ) -> tuple[str, str]:
539
+ """Get action and outcome for a chain step"""
540
+ rules = CHAIN_RULES.get(current.vuln_type, [])
541
+
542
+ if next_finding:
543
+ for next_type, action, outcome in rules:
544
+ if next_finding.vuln_type == next_type:
545
+ return action, outcome
546
+
547
+ # Default based on vulnerability type
548
+ defaults = {
549
+ VulnerabilityType.SQL_INJECTION: ("Execute SQL injection", "Access database"),
550
+ VulnerabilityType.XSS_STORED: ("Inject stored XSS payload", "Execute JavaScript in user browsers"),
551
+ VulnerabilityType.SSRF: ("Exploit SSRF vulnerability", "Make server-side requests"),
552
+ VulnerabilityType.RCE: ("Execute remote code", "Full system access"),
553
+ VulnerabilityType.AUTH_BYPASS: ("Bypass authentication", "Access as authenticated user"),
554
+ VulnerabilityType.IDOR: ("Access unauthorized resources", "View/modify other users' data"),
555
+ }
556
+
557
+ return defaults.get(
558
+ current.vuln_type,
559
+ (f"Exploit {current.vuln_type.value}", "Advance attack")
560
+ )
561
+
562
+ def _classify_chain_type(self, findings: list[Finding]) -> ChainType:
563
+ """Determine the type of attack chain"""
564
+ types = {f.vuln_type for f in findings}
565
+
566
+ # Check for RCE chain
567
+ if VulnerabilityType.RCE in types or VulnerabilityType.COMMAND_INJECTION in types:
568
+ return ChainType.REMOTE_CODE_EXECUTION
569
+
570
+ # Check for data exfiltration
571
+ if VulnerabilityType.SQL_INJECTION in types and VulnerabilityType.INFORMATION_DISCLOSURE in types:
572
+ return ChainType.DATA_EXFILTRATION
573
+
574
+ # Check for account takeover
575
+ if VulnerabilityType.AUTH_BYPASS in types or (
576
+ VulnerabilityType.XSS_STORED in types and VulnerabilityType.CSRF in types
577
+ ):
578
+ return ChainType.ACCOUNT_TAKEOVER
579
+
580
+ # Check for privilege escalation
581
+ if VulnerabilityType.PRIVILEGE_ESCALATION in types or (
582
+ VulnerabilityType.IDOR in types and VulnerabilityType.AUTH_BYPASS in types
583
+ ):
584
+ return ChainType.PRIVILEGE_ESCALATION
585
+
586
+ # Check for internal network access
587
+ if VulnerabilityType.SSRF in types:
588
+ return ChainType.INTERNAL_NETWORK_ACCESS
589
+
590
+ # Default based on most severe finding
591
+ return ChainType.DATA_EXFILTRATION
592
+
593
+ def _assess_impact(
594
+ self,
595
+ findings: list[Finding],
596
+ chain_type: ChainType,
597
+ ) -> ChainImpact:
598
+ """Assess the business impact of the chain"""
599
+ severities = [f.severity for f in findings]
600
+
601
+ # Catastrophic if RCE or multiple criticals
602
+ if chain_type == ChainType.REMOTE_CODE_EXECUTION:
603
+ return ChainImpact.CATASTROPHIC
604
+ if severities.count(Severity.CRITICAL) >= 2:
605
+ return ChainImpact.CATASTROPHIC
606
+
607
+ # Severe if data exfil or account takeover with high severity
608
+ if chain_type in [ChainType.DATA_EXFILTRATION, ChainType.ACCOUNT_TAKEOVER]:
609
+ if Severity.HIGH in severities or Severity.CRITICAL in severities:
610
+ return ChainImpact.SEVERE
611
+
612
+ # Significant if privilege escalation
613
+ if chain_type == ChainType.PRIVILEGE_ESCALATION:
614
+ return ChainImpact.SIGNIFICANT
615
+
616
+ # Based on highest severity in chain
617
+ if Severity.CRITICAL in severities:
618
+ return ChainImpact.SEVERE
619
+ if Severity.HIGH in severities:
620
+ return ChainImpact.SIGNIFICANT
621
+ if Severity.MEDIUM in severities:
622
+ return ChainImpact.MODERATE
623
+
624
+ return ChainImpact.MINIMAL
625
+
626
+ def _calculate_combined_severity(
627
+ self,
628
+ findings: list[Finding],
629
+ chain_type: ChainType,
630
+ ) -> Severity:
631
+ """Calculate the combined severity of the chain"""
632
+ # Chain severity is often higher than individual findings
633
+ max_severity = max(f.severity for f in findings)
634
+
635
+ # Escalate severity for high-impact chain types
636
+ escalation_types = [
637
+ ChainType.REMOTE_CODE_EXECUTION,
638
+ ChainType.DATA_EXFILTRATION,
639
+ ChainType.ACCOUNT_TAKEOVER,
640
+ ]
641
+
642
+ if chain_type in escalation_types:
643
+ severity_order = [Severity.INFO, Severity.LOW, Severity.MEDIUM, Severity.HIGH, Severity.CRITICAL]
644
+ current_idx = severity_order.index(max_severity)
645
+ escalated_idx = min(current_idx + 1, len(severity_order) - 1)
646
+ return severity_order[escalated_idx]
647
+
648
+ return max_severity
649
+
650
+ def _calculate_confidence(self, findings: list[Finding]) -> float:
651
+ """Calculate confidence in the chain validity"""
652
+ # Base confidence on finding confirmations
653
+ confirmed_count = sum(1 for f in findings if f.confirmed)
654
+ base_confidence = confirmed_count / len(findings)
655
+
656
+ # Boost for AI-validated findings
657
+ ai_findings = [f for f in findings if f.ai_confidence]
658
+ if ai_findings:
659
+ ai_boost = sum(f.ai_confidence for f in ai_findings) / len(ai_findings)
660
+ base_confidence = (base_confidence + ai_boost) / 2
661
+
662
+ # Reduce confidence for longer chains (more assumptions)
663
+ length_penalty = max(0, (len(findings) - 2) * 0.1)
664
+
665
+ return max(0.3, min(1.0, base_confidence - length_penalty))
666
+
667
+ def _generate_chain_name(
668
+ self,
669
+ chain_type: ChainType,
670
+ findings: list[Finding],
671
+ ) -> str:
672
+ """Generate a descriptive name for the chain"""
673
+ type_names = {
674
+ ChainType.REMOTE_CODE_EXECUTION: "Remote Code Execution Chain",
675
+ ChainType.DATA_EXFILTRATION: "Data Exfiltration Chain",
676
+ ChainType.ACCOUNT_TAKEOVER: "Account Takeover Chain",
677
+ ChainType.PRIVILEGE_ESCALATION: "Privilege Escalation Chain",
678
+ ChainType.INTERNAL_NETWORK_ACCESS: "Internal Network Access Chain",
679
+ ChainType.LATERAL_MOVEMENT: "Lateral Movement Chain",
680
+ ChainType.DENIAL_OF_SERVICE: "Denial of Service Chain",
681
+ ChainType.SUPPLY_CHAIN: "Supply Chain Attack",
682
+ }
683
+
684
+ base_name = type_names.get(chain_type, "Attack Chain")
685
+ entry = findings[0].vuln_type.value.replace("_", " ").title()
686
+
687
+ return f"{base_name} via {entry}"
688
+
689
+ def _generate_narrative(
690
+ self,
691
+ links: list[ChainLink],
692
+ chain_type: ChainType,
693
+ ) -> str:
694
+ """Generate a human-readable attack narrative"""
695
+ lines = [
696
+ "## Attack Narrative\n",
697
+ "An attacker could exploit this chain of vulnerabilities as follows:\n",
698
+ ]
699
+
700
+ for link in links:
701
+ lines.append(
702
+ f"**Step {link.step_number}:** {link.action}\n"
703
+ f"- Vulnerability: {link.finding.title}\n"
704
+ f"- URL: `{link.finding.url}`\n"
705
+ f"- Outcome: {link.outcome}\n"
706
+ )
707
+
708
+ lines.append(f"\n**Final Impact:** {links[-1].outcome}")
709
+
710
+ return "\n".join(lines)
711
+
712
+ def _describe_business_impact(
713
+ self,
714
+ chain_type: ChainType,
715
+ impact: ChainImpact,
716
+ ) -> str:
717
+ """Describe the business impact of the chain"""
718
+ descriptions = {
719
+ (ChainType.REMOTE_CODE_EXECUTION, ChainImpact.CATASTROPHIC):
720
+ "Complete server compromise. Attacker can access all data, install backdoors, "
721
+ "pivot to internal networks, and cause widespread damage.",
722
+
723
+ (ChainType.DATA_EXFILTRATION, ChainImpact.SEVERE):
724
+ "Mass data breach potential. Sensitive customer data, credentials, and "
725
+ "proprietary information could be extracted.",
726
+
727
+ (ChainType.ACCOUNT_TAKEOVER, ChainImpact.SEVERE):
728
+ "Any user account can be compromised. Attackers could access admin accounts, "
729
+ "steal user data, or perform actions as victims.",
730
+
731
+ (ChainType.PRIVILEGE_ESCALATION, ChainImpact.SIGNIFICANT):
732
+ "Unauthorized access to privileged functionality. Attackers could modify "
733
+ "configurations, access restricted data, or compromise other users.",
734
+
735
+ (ChainType.INTERNAL_NETWORK_ACCESS, ChainImpact.SEVERE):
736
+ "Access to internal network services. Attackers could reach databases, "
737
+ "admin panels, and other sensitive internal resources.",
738
+ }
739
+
740
+ return descriptions.get(
741
+ (chain_type, impact),
742
+ f"Potential for {impact.value} impact through {chain_type.value} attack."
743
+ )
744
+
745
+ def _suggest_remediation(self, links: list[ChainLink]) -> str:
746
+ """Suggest remediation priority based on chain analysis"""
747
+ # Breaking the first link breaks the chain
748
+ first = links[0].finding
749
+
750
+ return (
751
+ f"**Priority:** Fix the entry point first.\n"
752
+ f"Remediating '{first.title}' at {first.url} will break this entire attack chain.\n"
753
+ f"This should be addressed with {first.severity.value.upper()} priority."
754
+ )
755
+
756
+ def _deduplicate_chains(self, chains: list[AttackChain]) -> list[AttackChain]:
757
+ """Remove duplicate or subset chains"""
758
+ if not chains:
759
+ return []
760
+
761
+ # Create fingerprint for each chain
762
+ def chain_fingerprint(chain: AttackChain) -> str:
763
+ finding_ids = sorted(f.finding.fingerprint for f in chain.links)
764
+ return hashlib.md5("|".join(finding_ids).encode()).hexdigest()
765
+
766
+ seen = set()
767
+ unique = []
768
+
769
+ for chain in chains:
770
+ fp = chain_fingerprint(chain)
771
+ if fp not in seen:
772
+ seen.add(fp)
773
+ unique.append(chain)
774
+
775
+ return unique