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,523 @@
1
+ import os
2
+ import re
3
+ import dotenv
4
+ import requests
5
+ import subprocess
6
+ import urllib
7
+ import shutil
8
+ from pathlib import Path
9
+ import json
10
+ from aipt_v2.utils.searchers.util import remove_empty_directories
11
+ from aipt_v2.utils.model_manager import get_model
12
+ from langchain_core.messages import SystemMessage, HumanMessage
13
+ import yaml
14
+ import logging
15
+ # from util import remove_empty_directories
16
+
17
+ dotenv.load_dotenv()
18
+
19
+ # Security: CVE ID validation pattern (CWE-78 prevention)
20
+ CVE_PATTERN = re.compile(r"^CVE-\d{4}-\d{4,7}$", re.IGNORECASE)
21
+
22
+
23
+ def _validate_cve_id(cve: str) -> str:
24
+ """
25
+ Validate CVE ID format to prevent command injection.
26
+
27
+ Args:
28
+ cve: CVE identifier string
29
+
30
+ Returns:
31
+ Validated CVE ID
32
+
33
+ Raises:
34
+ ValueError: If CVE ID format is invalid
35
+ """
36
+ cve = cve.strip()
37
+ if not CVE_PATTERN.match(cve):
38
+ raise ValueError(f"Invalid CVE ID format: {cve}. Expected format: CVE-YYYY-NNNNN")
39
+ return cve.upper()
40
+
41
+
42
+ def _sanitize_keyword(keyword: str) -> str:
43
+ """
44
+ Sanitize search keyword to prevent command injection.
45
+
46
+ Args:
47
+ keyword: Search keyword string
48
+
49
+ Returns:
50
+ Sanitized keyword
51
+ """
52
+ # Remove dangerous characters that could enable shell injection
53
+ dangerous_chars = [";", "&", "|", "$", "`", "\n", "\r", "\\", "'", '"', "(", ")", "{", "}", "[", "]", "<", ">"]
54
+ sanitized = keyword
55
+ for char in dangerous_chars:
56
+ sanitized = sanitized.replace(char, "")
57
+ return sanitized.strip()[:200] # Limit length
58
+
59
+ config_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'configs', 'config.yaml')
60
+ with open(config_path, 'r', encoding='utf-8') as _f:
61
+ _config = yaml.safe_load(_f)
62
+ _planning_model_name = _config.get('runtime', {}).get('planning', {}).get('model', 'openai')
63
+ source_dir = _config.get('runtime', {}).get('exploitdb', {}).get('exploitdb_dir', '/usr/share/exploitdb/')
64
+
65
+ class ExploitDBSearcher:
66
+ ''' Just as GitHub searcher, ExploitDB searcher searches for exploits on ExploitDB and downloads them
67
+ Can search for exploits using the searchsploit tool'''
68
+
69
+ def __init__(self) -> None:
70
+ pass
71
+
72
+ def create_directories(self, output_dir: str):
73
+ directory_path = Path(output_dir)
74
+ for file in directory_path.iterdir():
75
+ if file.is_file():
76
+ if file.suffix == ".txt" or file.suffix == "":
77
+ self.analyze_txt_file(file)
78
+
79
+ for file in directory_path.iterdir():
80
+ if file.is_file():
81
+ self.create_code_description(file)
82
+
83
+
84
+ def create_code_description(self, file_path: str):
85
+ try:
86
+ with open(file_path, 'r', encoding='utf-8') as file:
87
+ content = file.read()
88
+ except Exception as e:
89
+ print(f"Failed to read file: {e}")
90
+ return
91
+
92
+ system_prompt = """
93
+ You are a professional Kali Linux security expert and technical document writer.
94
+ Please write detailed Markdown format documentation for the provided code, including the following sections:
95
+
96
+ ## 1. Code Function Description
97
+ Briefly describe the main function and purpose of the code.
98
+
99
+ ## 2. Execution Methods and Commands
100
+ Provide complete code execution commands and step-by-step instructions, including:
101
+ - Necessary command line parameters
102
+ - Execution examples
103
+ - Execution methods in a standard Kali Linux environment
104
+
105
+ ## 3. Environmental Dependencies
106
+ List the additional dependencies required to run in a standard Kali Linux environment:
107
+ - Software packages that need to be installed (using apt install commands)
108
+ - Python library dependencies (using pip install commands)
109
+ - Other environmental requirements
110
+
111
+ ## 4. Configuration Parameters
112
+ Describe the variables and parameters that need to be manually configured:
113
+ - Configuration items that must be edited
114
+ - Recommended security settings
115
+ - Important parameter descriptions
116
+
117
+ ## 5. Permission Requirements
118
+ Detail the permissions required to execute the code:
119
+ - Whether root permissions are needed
120
+ - File system access permission requirements
121
+ - Network access permission requirements
122
+
123
+ Note:
124
+ 1. Use professional Markdown format
125
+ 2. Ensure the content is detailed and accurate
126
+ 3. The script or code has already been saved. DO NOT tell users to do so
127
+ 4. Provide specific guidance for Kali Linux environment
128
+ 5. For content requiring user input, mark it using `{{placeholder}}` format.
129
+ """
130
+
131
+ # get extensions for code block tagging
132
+ file_suffix = Path(file_path).suffix[1:] # remove dot symbol
133
+ code_block_lang = file_suffix if file_suffix else "text"
134
+
135
+ user_prompt = f"""
136
+ Please write a Markdown document for the following code:
137
+
138
+ ```{code_block_lang}
139
+ {content[:15000]}
140
+ ```
141
+ """
142
+
143
+ try:
144
+ llm = get_model(_planning_model_name)
145
+ if llm is None:
146
+ print("LLM not initialized; skip create_code_description")
147
+ return
148
+ response = llm.invoke([
149
+ SystemMessage(content=system_prompt),
150
+ HumanMessage(content=user_prompt)
151
+ ])
152
+ markdown_content = response.content.strip() if hasattr(response, 'content') else str(response)
153
+ except Exception as e:
154
+ print(f"Failed to call LLM: {e}")
155
+ return
156
+
157
+ path_obj = Path(file_path)
158
+
159
+ folder_name = path_obj.stem
160
+ new_folder = path_obj.parent / folder_name
161
+ new_folder.mkdir(exist_ok=True)
162
+
163
+ readme_path = new_folder / "README.md"
164
+
165
+ new_file_path = new_folder / path_obj.name
166
+
167
+ try:
168
+ with open(readme_path, 'w', encoding='utf-8') as md_file:
169
+ md_file.write(markdown_content)
170
+
171
+ path_obj.rename(new_file_path)
172
+
173
+ print(f"md doc created successfully:")
174
+ print(f" code doc location: {new_file_path}")
175
+ print(f" md doc location: {readme_path}")
176
+
177
+ except Exception as e:
178
+ print(f"File operation failed: {e}")
179
+
180
+ def analyze_txt_file(self, file_path: str):
181
+ try:
182
+ with open(file_path, 'r', encoding='utf-8') as file:
183
+ content = file.read()
184
+ except Exception as e:
185
+ print(f"Failed to read file: {e}")
186
+ return
187
+
188
+ system_prompt = """
189
+ You are a code analysis expert and need to analyze the content of files provided by users to determine if they contain executable code snippets.
190
+ Please strictly output the results in the following JSON format:
191
+ {
192
+ "has_code": <boolean value, true means executable code is present, false means it is not>,
193
+ "code_content": <if code is present, extract the code content (preserving all comments and formatting); otherwise, it is an empty string>,
194
+ "language_suffix": <if code is present, the corresponding file suffix of the code (e.g., ".py"); otherwise, it is an empty string>
195
+ }
196
+
197
+ Note:
198
+ 1. Only analyze executable code; configuration files and pure data files do not count as executable code.
199
+ 2. Preserve all comments and formatting from the original code.
200
+ 3. If the file contains multiple languages, choose the most predominant language.
201
+ 4. The language suffix must include a dot (e.g., ".js").
202
+ """
203
+
204
+ user_prompt = f"""Analyze the content of the following document and determine if it contains executable code:
205
+ ```
206
+ {content[:15000]}
207
+ ```
208
+ """
209
+
210
+ try:
211
+ llm = get_model(_planning_model_name)
212
+ if llm is None:
213
+ print("LLM not initialized; skip analyze_txt_file")
214
+ return
215
+ response = llm.invoke([
216
+ SystemMessage(content=system_prompt),
217
+ HumanMessage(content=user_prompt)
218
+ ])
219
+ llm_output = response.content.strip() if hasattr(response, 'content') else str(response)
220
+ # Try to extract JSON in case model adds code fences
221
+ json_str = llm_output
222
+ if "```" in json_str:
223
+ # try common ```json ... ```
224
+ try:
225
+ json_str = json_str.split("```json", 1)[1].split("```", 1)[0].strip()
226
+ except Exception:
227
+ json_str = json_str.replace("```", "").strip()
228
+ result = json.loads(json_str)
229
+ except Exception as e:
230
+ print(f"Failed to call LLM or parse JSON: {e}")
231
+ return
232
+
233
+ path_obj = Path(file_path)
234
+
235
+ if result.get("has_code", False):
236
+ code_content = result.get("code_content", "")
237
+ language_suffix = result.get("language_suffix", "")
238
+
239
+ if not language_suffix.startswith("."):
240
+ language_suffix = f".{language_suffix}"
241
+
242
+ if language_suffix == "":
243
+ language_suffix = ".sh"
244
+
245
+ line_count = len(code_content.splitlines())
246
+ char_count = len(code_content)
247
+
248
+ if line_count >= 2 and char_count >= 50:
249
+ new_file_name = f"{path_obj.stem}{language_suffix}"
250
+ new_file_path = path_obj.with_name(new_file_name)
251
+
252
+ try:
253
+ with open(new_file_path, 'w', encoding='utf-8') as file:
254
+ file.write(code_content)
255
+ print(f"Code file has been created: {new_file_path}")
256
+
257
+ path_obj.unlink()
258
+ print(f"Remove original file: {file_path}")
259
+
260
+ except Exception as e:
261
+ print(f"File operation failed: {e}")
262
+ else:
263
+ print(f"Code content too small (lines: {line_count}, chars: {char_count}). Skip file creation.")
264
+ try:
265
+ path_obj.unlink()
266
+ print(f"Remove original file: {file_path}")
267
+ except Exception as e:
268
+ print(f"Failed to remove original file: {e}")
269
+ else:
270
+ try:
271
+ path_obj.unlink()
272
+ print(f"The file has been removed for not having executable code: {file_path}")
273
+ except Exception as e:
274
+ print(f"Failed to remove file: {e}")
275
+
276
+ def search_keyword(self, keyword: str, output_dir: str, loose_mode: bool = False):
277
+ """
278
+ Search for exploits using the searchsploit tool.
279
+
280
+ Security: Uses validated/sanitized keywords and subprocess without shell=True (CWE-78 fix).
281
+ """
282
+ # needs EDB-ID
283
+
284
+ try:
285
+ if loose_mode:
286
+ # Sanitize keyword for loose mode search
287
+ sanitized_keyword = _sanitize_keyword(keyword.replace(" exploit", ""))
288
+ if not sanitized_keyword:
289
+ logging.warning(f"Empty keyword after sanitization: {keyword}")
290
+ return
291
+ # SECURE: Use subprocess without shell=True
292
+ result = subprocess.run(
293
+ ["searchsploit", "--disable-colour", sanitized_keyword],
294
+ capture_output=True,
295
+ text=True,
296
+ timeout=60,
297
+ check=False
298
+ )
299
+ else:
300
+ # Validate CVE ID for strict mode search
301
+ validated_cve = _validate_cve_id(keyword)
302
+ # SECURE: Use subprocess without shell=True
303
+ result = subprocess.run(
304
+ ["searchsploit", "--disable-colour", "--ecve", "--cve", validated_cve],
305
+ capture_output=True,
306
+ text=True,
307
+ timeout=60,
308
+ check=False
309
+ )
310
+ output = result.stdout.strip()
311
+ except ValueError as e:
312
+ logging.error(f"Invalid CVE ID format: {e}")
313
+ return
314
+ except subprocess.TimeoutExpired:
315
+ logging.error(f"searchsploit search timed out for {keyword}")
316
+ return
317
+ except FileNotFoundError:
318
+ logging.error("searchsploit command not found. Please install exploitdb.")
319
+ return
320
+
321
+ exploits = []
322
+ exploit_details = []
323
+ # look into shellcodes returned
324
+ shellcodes = []
325
+ shellcode_details = []
326
+
327
+ exploit_flag, shellcode_flag = False, False
328
+ if "Exploits: No Results" not in output:
329
+ exploit_flag = True
330
+ if "Shellcodes: No Results" not in output:
331
+ shellcode_flag = True
332
+
333
+ lines = output.split('\n')
334
+ split_index = 0
335
+ for i in range(len(lines)):
336
+ if "Shellcode Title" in lines[i]:
337
+ split_index = i
338
+ break
339
+ words = lines[i].split()
340
+ # truncate words to only store those after the pipe character
341
+ # first find the index at which the pip character is
342
+ if '|' in words:
343
+ pipe_index = words.index('|')
344
+ exploit_details.append(words[:pipe_index])
345
+ words = words[pipe_index+1:]
346
+ for word in words:
347
+ exploits.append(word)
348
+ shellcode_lines = lines[split_index:]
349
+ for i in range(len(shellcode_lines)):
350
+ words = shellcode_lines[i].split()
351
+ # truncate words to only store those after the pipe character
352
+ # first find the index at which the pip character is
353
+ if '|' in words:
354
+ pipe_index = words.index('|')
355
+ shellcode_details.append(words[:pipe_index])
356
+ words = words[pipe_index+1:]
357
+ for word in words:
358
+ shellcodes.append(word)
359
+
360
+ if exploit_flag:
361
+ exploits = exploits[1:]
362
+ exploit_details = exploit_details[1:]
363
+ # self.download_exploit(exploits[0])
364
+ for i in range(len(exploits)):
365
+ filename = (exploits[i].split('/')[-1])
366
+ filename = exploits[i].split('/')[-1]
367
+ if filename.split('.')[1] != 'txt':
368
+ filename = filename.split('.')[0]
369
+ if not os.path.exists(os.path.join(output_dir, filename)):
370
+ os.mkdir(os.path.join(output_dir, filename))
371
+ with open(os.path.join(output_dir, filename, "README.md"), 'w') as f:
372
+ f.write(' '.join(exploit_details[i]))
373
+ self.download_exploit(exploits[i],'exploits', output_dir)
374
+
375
+ if shellcode_flag:
376
+ shellcodes = shellcodes[1:]
377
+ shellcode_details = shellcode_details[1:]
378
+ for i in range(len(shellcodes)):
379
+ filename = shellcodes[i].split('/')[-1]
380
+ if filename.split('.')[1] != 'txt':
381
+ filename = filename.split('.')[0]
382
+ if not os.path.exists(os.path.join(output_dir, filename)):
383
+ os.mkdir(os.path.join(output_dir, filename))
384
+ with open(os.path.join(output_dir, filename, "README.md"), 'w') as f:
385
+ f.write(' '.join(shellcode_details[i]))
386
+ self.download_exploit(shellcodes[i],'shellcodes', output_dir)
387
+
388
+ remove_empty_directories(output_dir)
389
+ self.create_directories(output_dir)
390
+
391
+ def download_exploit(self, sub_url: str, type: str, output_dir: str):
392
+ ''' Download an exploit from GitLab ExploitDB '''
393
+ project_id = os.getenv("GITLAB_PROJECT_ID")
394
+ filename = sub_url.split('/')[-1]
395
+ file_path = type + "/" + str(sub_url)
396
+ # print(file_path)
397
+
398
+ # URL-encode the file path
399
+ encoded_file_path = urllib.parse.quote(file_path, safe='')
400
+
401
+ url = f"https://gitlab.com/api/v4/projects/{project_id}/repository/files/{encoded_file_path}/raw?ref=main"
402
+ # print(url)
403
+ headers = {'PRIVATE-TOKEN': os.getenv("GITLAB_TOKEN")}
404
+ # Security: Add timeout to prevent indefinite hangs (CWE-400)
405
+ response = requests.get(url, headers=headers, timeout=30)
406
+ if response.status_code == 200:
407
+ with open(os.path.join(output_dir, filename.split('.')[0], filename), 'wb') as f:
408
+ f.write(response.content)
409
+ # print("File downloaded successfully.")
410
+
411
+ else:
412
+ print("Failed to download file:", response.status_code)
413
+
414
+ def search_keyword_local(self, keyword: str, output_dir: str, loose_mode: bool = False):
415
+ """
416
+ Search for exploits using the searchsploit tool (local mode).
417
+
418
+ Security: Uses validated/sanitized keywords and subprocess without shell=True (CWE-78 fix).
419
+ """
420
+ try:
421
+ if loose_mode:
422
+ # Sanitize keyword for loose mode search
423
+ sanitized_keyword = _sanitize_keyword(keyword.replace(" exploit", "", 1))
424
+ if not sanitized_keyword:
425
+ logging.warning(f"Empty keyword after sanitization: {keyword}")
426
+ return
427
+ # SECURE: Use subprocess without shell=True
428
+ result = subprocess.run(
429
+ ["searchsploit", "--disable-colour", sanitized_keyword],
430
+ capture_output=True,
431
+ text=True,
432
+ timeout=60,
433
+ check=False
434
+ )
435
+ else:
436
+ # Validate CVE ID for strict mode search
437
+ validated_cve = _validate_cve_id(keyword)
438
+ # SECURE: Use subprocess without shell=True
439
+ result = subprocess.run(
440
+ ["searchsploit", "--disable-colour", "--ecve", "--cve", validated_cve],
441
+ capture_output=True,
442
+ text=True,
443
+ timeout=60,
444
+ check=False
445
+ )
446
+ output = result.stdout.strip()
447
+ except ValueError as e:
448
+ logging.error(f"Invalid CVE ID format: {e}")
449
+ return
450
+ except subprocess.TimeoutExpired:
451
+ logging.error(f"searchsploit search timed out for {keyword}")
452
+ return
453
+ except FileNotFoundError:
454
+ logging.error("searchsploit command not found. Please install exploitdb.")
455
+ return
456
+
457
+ exploits = []
458
+ shellcodes = []
459
+
460
+ exploit_flag, shellcode_flag = False, False
461
+ if "Exploits: No Results" not in output:
462
+ exploit_flag = True
463
+ if "Shellcodes: No Results" not in output:
464
+ shellcode_flag = True
465
+
466
+ lines = output.split('\n')
467
+ split_index = 0
468
+ for i in range(len(lines)):
469
+ if "Shellcode Title" in lines[i]:
470
+ split_index = i
471
+ break
472
+ words = lines[i].split()
473
+ # truncate words to only store those after the pipe character
474
+ # first find the index at which the pip character is
475
+ if '|' in words:
476
+ pipe_index = words.index('|')
477
+ exploits.extend([word for word in words[pipe_index+1:] if word]) # extract relative path
478
+ shellcode_lines = lines[split_index:]
479
+ for i in range(len(shellcode_lines)):
480
+ words = shellcode_lines[i].split()
481
+ # truncate words to only store those after the pipe character
482
+ # first find the index at which the pip character is
483
+ if '|' in words:
484
+ pipe_index = words.index('|')
485
+ shellcodes.extend([word for word in words[pipe_index+1:] if word]) # extract relative path
486
+
487
+ # print(exploits)
488
+ # print(shellcodes)
489
+
490
+ if exploit_flag:
491
+ exploits = exploits[1:]
492
+ for i in range(len(exploits)):
493
+ #################################################################################
494
+ # if filename.split('.')[1] != 'txt': # to remove txt file, activate this logic #
495
+ #################################################################################
496
+ filename = os.path.basename(exploits[i])
497
+ filepath = os.path.join(source_dir, "exploits", exploits[i])
498
+ destination = os.path.join(output_dir, filename)
499
+ shutil.copy(filepath, destination)
500
+
501
+ if shellcode_flag:
502
+ shellcodes = shellcodes[1:]
503
+ for i in range(len(shellcodes)):
504
+ #################################################################################
505
+ # if filename.split('.')[1] != 'txt': # to remove txt file, activate this logic #
506
+ #################################################################################
507
+ filename = os.path.basename(shellcodes[i])
508
+ filepath = os.path.join(source_dir, "shellcodes", shellcodes[i])
509
+ destination = os.path.join(output_dir, filename)
510
+ shutil.copy(filepath, destination)
511
+
512
+ self.create_directories(output_dir)
513
+
514
+
515
+ # def main():
516
+ # e = ExploitDBSearcher()
517
+ # # download to the same name directory
518
+ # output_dir = "/root/Desktop/pentest/data/exp_info/"
519
+ # e.search_keyword_local('cve-2021-42013', output_dir)
520
+
521
+ # if __name__ == "__main__":
522
+ # main()
523
+