aiptx 2.0.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- aipt_v2/__init__.py +110 -0
- aipt_v2/__main__.py +24 -0
- aipt_v2/agents/AIPTxAgent/__init__.py +10 -0
- aipt_v2/agents/AIPTxAgent/aiptx_agent.py +211 -0
- aipt_v2/agents/__init__.py +46 -0
- aipt_v2/agents/base.py +520 -0
- aipt_v2/agents/exploit_agent.py +688 -0
- aipt_v2/agents/ptt.py +406 -0
- aipt_v2/agents/state.py +168 -0
- aipt_v2/app.py +957 -0
- aipt_v2/browser/__init__.py +31 -0
- aipt_v2/browser/automation.py +458 -0
- aipt_v2/browser/crawler.py +453 -0
- aipt_v2/cli.py +2933 -0
- aipt_v2/compliance/__init__.py +71 -0
- aipt_v2/compliance/compliance_report.py +449 -0
- aipt_v2/compliance/framework_mapper.py +424 -0
- aipt_v2/compliance/nist_mapping.py +345 -0
- aipt_v2/compliance/owasp_mapping.py +330 -0
- aipt_v2/compliance/pci_mapping.py +297 -0
- aipt_v2/config.py +341 -0
- aipt_v2/core/__init__.py +43 -0
- aipt_v2/core/agent.py +630 -0
- aipt_v2/core/llm.py +395 -0
- aipt_v2/core/memory.py +305 -0
- aipt_v2/core/ptt.py +329 -0
- aipt_v2/database/__init__.py +14 -0
- aipt_v2/database/models.py +232 -0
- aipt_v2/database/repository.py +384 -0
- aipt_v2/docker/__init__.py +23 -0
- aipt_v2/docker/builder.py +260 -0
- aipt_v2/docker/manager.py +222 -0
- aipt_v2/docker/sandbox.py +371 -0
- aipt_v2/evasion/__init__.py +58 -0
- aipt_v2/evasion/request_obfuscator.py +272 -0
- aipt_v2/evasion/tls_fingerprint.py +285 -0
- aipt_v2/evasion/ua_rotator.py +301 -0
- aipt_v2/evasion/waf_bypass.py +439 -0
- aipt_v2/execution/__init__.py +23 -0
- aipt_v2/execution/executor.py +302 -0
- aipt_v2/execution/parser.py +544 -0
- aipt_v2/execution/terminal.py +337 -0
- aipt_v2/health.py +437 -0
- aipt_v2/intelligence/__init__.py +194 -0
- aipt_v2/intelligence/adaptation.py +474 -0
- aipt_v2/intelligence/auth.py +520 -0
- aipt_v2/intelligence/chaining.py +775 -0
- aipt_v2/intelligence/correlation.py +536 -0
- aipt_v2/intelligence/cve_aipt.py +334 -0
- aipt_v2/intelligence/cve_info.py +1111 -0
- aipt_v2/intelligence/knowledge_graph.py +590 -0
- aipt_v2/intelligence/learning.py +626 -0
- aipt_v2/intelligence/llm_analyzer.py +502 -0
- aipt_v2/intelligence/llm_tool_selector.py +518 -0
- aipt_v2/intelligence/payload_generator.py +562 -0
- aipt_v2/intelligence/rag.py +239 -0
- aipt_v2/intelligence/scope.py +442 -0
- aipt_v2/intelligence/searchers/__init__.py +5 -0
- aipt_v2/intelligence/searchers/exploitdb_searcher.py +523 -0
- aipt_v2/intelligence/searchers/github_searcher.py +467 -0
- aipt_v2/intelligence/searchers/google_searcher.py +281 -0
- aipt_v2/intelligence/tools.json +443 -0
- aipt_v2/intelligence/triage.py +670 -0
- aipt_v2/interactive_shell.py +559 -0
- aipt_v2/interface/__init__.py +5 -0
- aipt_v2/interface/cli.py +230 -0
- aipt_v2/interface/main.py +501 -0
- aipt_v2/interface/tui.py +1276 -0
- aipt_v2/interface/utils.py +583 -0
- aipt_v2/llm/__init__.py +39 -0
- aipt_v2/llm/config.py +26 -0
- aipt_v2/llm/llm.py +514 -0
- aipt_v2/llm/memory.py +214 -0
- aipt_v2/llm/request_queue.py +89 -0
- aipt_v2/llm/utils.py +89 -0
- aipt_v2/local_tool_installer.py +1467 -0
- aipt_v2/models/__init__.py +15 -0
- aipt_v2/models/findings.py +295 -0
- aipt_v2/models/phase_result.py +224 -0
- aipt_v2/models/scan_config.py +207 -0
- aipt_v2/monitoring/grafana/dashboards/aipt-dashboard.json +355 -0
- aipt_v2/monitoring/grafana/dashboards/default.yml +17 -0
- aipt_v2/monitoring/grafana/datasources/prometheus.yml +17 -0
- aipt_v2/monitoring/prometheus.yml +60 -0
- aipt_v2/orchestration/__init__.py +52 -0
- aipt_v2/orchestration/pipeline.py +398 -0
- aipt_v2/orchestration/progress.py +300 -0
- aipt_v2/orchestration/scheduler.py +296 -0
- aipt_v2/orchestrator.py +2427 -0
- aipt_v2/payloads/__init__.py +27 -0
- aipt_v2/payloads/cmdi.py +150 -0
- aipt_v2/payloads/sqli.py +263 -0
- aipt_v2/payloads/ssrf.py +204 -0
- aipt_v2/payloads/templates.py +222 -0
- aipt_v2/payloads/traversal.py +166 -0
- aipt_v2/payloads/xss.py +204 -0
- aipt_v2/prompts/__init__.py +60 -0
- aipt_v2/proxy/__init__.py +29 -0
- aipt_v2/proxy/history.py +352 -0
- aipt_v2/proxy/interceptor.py +452 -0
- aipt_v2/recon/__init__.py +44 -0
- aipt_v2/recon/dns.py +241 -0
- aipt_v2/recon/osint.py +367 -0
- aipt_v2/recon/subdomain.py +372 -0
- aipt_v2/recon/tech_detect.py +311 -0
- aipt_v2/reports/__init__.py +17 -0
- aipt_v2/reports/generator.py +313 -0
- aipt_v2/reports/html_report.py +378 -0
- aipt_v2/runtime/__init__.py +53 -0
- aipt_v2/runtime/base.py +30 -0
- aipt_v2/runtime/docker.py +401 -0
- aipt_v2/runtime/local.py +346 -0
- aipt_v2/runtime/tool_server.py +205 -0
- aipt_v2/runtime/vps.py +830 -0
- aipt_v2/scanners/__init__.py +28 -0
- aipt_v2/scanners/base.py +273 -0
- aipt_v2/scanners/nikto.py +244 -0
- aipt_v2/scanners/nmap.py +402 -0
- aipt_v2/scanners/nuclei.py +273 -0
- aipt_v2/scanners/web.py +454 -0
- aipt_v2/scripts/security_audit.py +366 -0
- aipt_v2/setup_wizard.py +941 -0
- aipt_v2/skills/__init__.py +80 -0
- aipt_v2/skills/agents/__init__.py +14 -0
- aipt_v2/skills/agents/api_tester.py +706 -0
- aipt_v2/skills/agents/base.py +477 -0
- aipt_v2/skills/agents/code_review.py +459 -0
- aipt_v2/skills/agents/security_agent.py +336 -0
- aipt_v2/skills/agents/web_pentest.py +818 -0
- aipt_v2/skills/prompts/__init__.py +647 -0
- aipt_v2/system_detector.py +539 -0
- aipt_v2/telemetry/__init__.py +7 -0
- aipt_v2/telemetry/tracer.py +347 -0
- aipt_v2/terminal/__init__.py +28 -0
- aipt_v2/terminal/executor.py +400 -0
- aipt_v2/terminal/sandbox.py +350 -0
- aipt_v2/tools/__init__.py +44 -0
- aipt_v2/tools/active_directory/__init__.py +78 -0
- aipt_v2/tools/active_directory/ad_config.py +238 -0
- aipt_v2/tools/active_directory/bloodhound_wrapper.py +447 -0
- aipt_v2/tools/active_directory/kerberos_attacks.py +430 -0
- aipt_v2/tools/active_directory/ldap_enum.py +533 -0
- aipt_v2/tools/active_directory/smb_attacks.py +505 -0
- aipt_v2/tools/agents_graph/__init__.py +19 -0
- aipt_v2/tools/agents_graph/agents_graph_actions.py +69 -0
- aipt_v2/tools/api_security/__init__.py +76 -0
- aipt_v2/tools/api_security/api_discovery.py +608 -0
- aipt_v2/tools/api_security/graphql_scanner.py +622 -0
- aipt_v2/tools/api_security/jwt_analyzer.py +577 -0
- aipt_v2/tools/api_security/openapi_fuzzer.py +761 -0
- aipt_v2/tools/browser/__init__.py +5 -0
- aipt_v2/tools/browser/browser_actions.py +238 -0
- aipt_v2/tools/browser/browser_instance.py +535 -0
- aipt_v2/tools/browser/tab_manager.py +344 -0
- aipt_v2/tools/cloud/__init__.py +70 -0
- aipt_v2/tools/cloud/cloud_config.py +273 -0
- aipt_v2/tools/cloud/cloud_scanner.py +639 -0
- aipt_v2/tools/cloud/prowler_tool.py +571 -0
- aipt_v2/tools/cloud/scoutsuite_tool.py +359 -0
- aipt_v2/tools/executor.py +307 -0
- aipt_v2/tools/parser.py +408 -0
- aipt_v2/tools/proxy/__init__.py +5 -0
- aipt_v2/tools/proxy/proxy_actions.py +103 -0
- aipt_v2/tools/proxy/proxy_manager.py +789 -0
- aipt_v2/tools/registry.py +196 -0
- aipt_v2/tools/scanners/__init__.py +343 -0
- aipt_v2/tools/scanners/acunetix_tool.py +712 -0
- aipt_v2/tools/scanners/burp_tool.py +631 -0
- aipt_v2/tools/scanners/config.py +156 -0
- aipt_v2/tools/scanners/nessus_tool.py +588 -0
- aipt_v2/tools/scanners/zap_tool.py +612 -0
- aipt_v2/tools/terminal/__init__.py +5 -0
- aipt_v2/tools/terminal/terminal_actions.py +37 -0
- aipt_v2/tools/terminal/terminal_manager.py +153 -0
- aipt_v2/tools/terminal/terminal_session.py +449 -0
- aipt_v2/tools/tool_processing.py +108 -0
- aipt_v2/utils/__init__.py +17 -0
- aipt_v2/utils/logging.py +202 -0
- aipt_v2/utils/model_manager.py +187 -0
- aipt_v2/utils/searchers/__init__.py +269 -0
- aipt_v2/verify_install.py +793 -0
- aiptx-2.0.7.dist-info/METADATA +345 -0
- aiptx-2.0.7.dist-info/RECORD +187 -0
- aiptx-2.0.7.dist-info/WHEEL +5 -0
- aiptx-2.0.7.dist-info/entry_points.txt +7 -0
- aiptx-2.0.7.dist-info/licenses/LICENSE +21 -0
- aiptx-2.0.7.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,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
|
+
|