opencode-api-security-testing 2.0.0 → 2.1.1
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.
- package/README.md +30 -24
- package/SKILL.md +1797 -0
- package/core/advanced_recon.py +788 -0
- package/core/agentic_analyzer.py +445 -0
- package/core/analyzers/api_parser.py +210 -0
- package/core/analyzers/response_analyzer.py +212 -0
- package/core/analyzers/sensitive_finder.py +184 -0
- package/core/api_fuzzer.py +422 -0
- package/core/api_interceptor.py +525 -0
- package/core/api_parser.py +955 -0
- package/core/browser_tester.py +479 -0
- package/core/cloud_storage_tester.py +1330 -0
- package/core/collectors/__init__.py +23 -0
- package/core/collectors/api_path_finder.py +300 -0
- package/core/collectors/browser_collect.py +645 -0
- package/core/collectors/browser_collector.py +411 -0
- package/core/collectors/http_client.py +111 -0
- package/core/collectors/js_collector.py +490 -0
- package/core/collectors/js_parser.py +780 -0
- package/core/collectors/url_collector.py +319 -0
- package/core/context_manager.py +682 -0
- package/core/deep_api_tester_v35.py +844 -0
- package/core/deep_api_tester_v55.py +366 -0
- package/core/dynamic_api_analyzer.py +532 -0
- package/core/http_client.py +179 -0
- package/core/models.py +296 -0
- package/core/orchestrator.py +890 -0
- package/core/prerequisite.py +227 -0
- package/core/reasoning_engine.py +1042 -0
- package/core/response_classifier.py +606 -0
- package/core/runner.py +938 -0
- package/core/scan_engine.py +599 -0
- package/core/skill_executor.py +435 -0
- package/core/skill_executor_v2.py +670 -0
- package/core/skill_executor_v3.py +704 -0
- package/core/smart_analyzer.py +687 -0
- package/core/strategy_pool.py +707 -0
- package/core/testers/auth_tester.py +264 -0
- package/core/testers/idor_tester.py +200 -0
- package/core/testers/sqli_tester.py +211 -0
- package/core/testing_loop.py +655 -0
- package/core/utils/base_path_dict.py +255 -0
- package/core/utils/payload_lib.py +167 -0
- package/core/utils/ssrf_detector.py +220 -0
- package/core/verifiers/vuln_verifier.py +536 -0
- package/package.json +17 -13
- package/references/asset-discovery.md +119 -612
- package/references/graphql-guidance.md +65 -641
- package/references/intake.md +84 -0
- package/references/report-template.md +131 -38
- package/references/rest-guidance.md +55 -526
- package/references/severity-model.md +52 -264
- package/references/test-matrix.md +65 -263
- package/references/validation.md +53 -400
- package/scripts/postinstall.js +46 -0
- package/src/index.ts +259 -275
- package/agents/cyber-supervisor.md +0 -55
- package/agents/probing-miner.md +0 -42
- package/agents/resource-specialist.md +0 -31
- package/commands/api-security-testing-scan.md +0 -59
- package/commands/api-security-testing-test.md +0 -49
- package/commands/api-security-testing.md +0 -72
- package/tsconfig.json +0 -17
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SKILL 执行器 - 智能决策执行
|
|
3
|
+
|
|
4
|
+
根据 SKILL.md 定义的决策流程:
|
|
5
|
+
1. 根据前置检查选择模块
|
|
6
|
+
2. 根据静态分析结果判断站点类型
|
|
7
|
+
3. 根据父路径探测结果决定后续行动
|
|
8
|
+
4. 智能跳过/继续测试
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import sys
|
|
12
|
+
sys.path.insert(0, '/workspace/skill-play/API-Security-Testing-Optimized')
|
|
13
|
+
|
|
14
|
+
from core.prerequisite import prerequisite_check
|
|
15
|
+
from core.api_parser import APIEndpointParser
|
|
16
|
+
from core.cloud_storage_tester import CloudStorageTester
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class SKILLExecutor:
|
|
20
|
+
"""SKILL 执行器 - 智能决策执行"""
|
|
21
|
+
|
|
22
|
+
def __init__(self, target: str):
|
|
23
|
+
self.target = target
|
|
24
|
+
self.session = None
|
|
25
|
+
self.playwright_available = False
|
|
26
|
+
self.browser_type = None
|
|
27
|
+
|
|
28
|
+
# 资产发现结果
|
|
29
|
+
self.parser = None # 共享的 parser 实例
|
|
30
|
+
self.js_files = []
|
|
31
|
+
self.static_endpoints = []
|
|
32
|
+
self.dynamic_endpoints = []
|
|
33
|
+
self.hooked_endpoints = []
|
|
34
|
+
self.parent_paths = {}
|
|
35
|
+
|
|
36
|
+
# 测试结果
|
|
37
|
+
self.vulnerabilities = []
|
|
38
|
+
self.cloud_findings = []
|
|
39
|
+
self.api_prefix = None
|
|
40
|
+
|
|
41
|
+
# 决策状态
|
|
42
|
+
self.site_type = None # pure_html / jquery_spa / vue_spa / react_spa / login_required
|
|
43
|
+
self.has_real_api = False
|
|
44
|
+
self.has_nginx_fallback = False
|
|
45
|
+
|
|
46
|
+
def run(self):
|
|
47
|
+
"""执行 SKILL 流程"""
|
|
48
|
+
print("=" * 70)
|
|
49
|
+
print(" API Security Testing Skill - 智能执行")
|
|
50
|
+
print("=" * 70)
|
|
51
|
+
print(f" 目标: {self.target}")
|
|
52
|
+
print()
|
|
53
|
+
|
|
54
|
+
# ========== 阶段 0: 前置检查 ==========
|
|
55
|
+
print("[阶段 0] 前置检查")
|
|
56
|
+
print("-" * 50)
|
|
57
|
+
self._check_prerequisites()
|
|
58
|
+
|
|
59
|
+
if not self.playwright_available:
|
|
60
|
+
print(" [WARN] Playwright 不可用,使用受限模式")
|
|
61
|
+
|
|
62
|
+
# ========== 阶段 1: 资产发现 - 静态分析 ==========
|
|
63
|
+
print("\n[阶段 1] 资产发现 - 静态分析")
|
|
64
|
+
print("-" * 50)
|
|
65
|
+
self._static_analysis()
|
|
66
|
+
|
|
67
|
+
# ========== 阶段 1.1: 判断站点类型 ==========
|
|
68
|
+
print("\n[决策] 判断站点类型")
|
|
69
|
+
print("-" * 50)
|
|
70
|
+
self._detect_site_type()
|
|
71
|
+
|
|
72
|
+
# ========== 阶段 1.2: 父路径探测 ==========
|
|
73
|
+
print("\n[阶段 1.2] 父路径探测")
|
|
74
|
+
print("-" * 50)
|
|
75
|
+
self._probe_parent_paths()
|
|
76
|
+
|
|
77
|
+
# ========== 阶段 1.3: 根据决策选择下一步 ==========
|
|
78
|
+
print("\n[决策] 选择后续行动")
|
|
79
|
+
print("-" * 50)
|
|
80
|
+
self._decide_next_action()
|
|
81
|
+
|
|
82
|
+
# ========== 阶段 2: 漏洞分析 ==========
|
|
83
|
+
if self.has_real_api:
|
|
84
|
+
print("\n[阶段 2] 漏洞分析")
|
|
85
|
+
print("-" * 50)
|
|
86
|
+
self._vulnerability_testing()
|
|
87
|
+
else:
|
|
88
|
+
print("\n[阶段 2] 漏洞分析 - [SKIP]")
|
|
89
|
+
print("-" * 50)
|
|
90
|
+
print(" 无真实 API 或 nginx fallback,跳过漏洞测试")
|
|
91
|
+
self._report_nginx_fallback()
|
|
92
|
+
|
|
93
|
+
# ========== 阶段 3: 云存储测试 ==========
|
|
94
|
+
print("\n[阶段 3] 云存储测试")
|
|
95
|
+
print("-" * 50)
|
|
96
|
+
self._cloud_storage_test()
|
|
97
|
+
|
|
98
|
+
# ========== 阶段 4: 报告 ==========
|
|
99
|
+
print("\n[阶段 4] 报告")
|
|
100
|
+
print("-" * 50)
|
|
101
|
+
self._generate_report()
|
|
102
|
+
|
|
103
|
+
return self._get_result()
|
|
104
|
+
|
|
105
|
+
def _check_prerequisites(self):
|
|
106
|
+
"""前置检查"""
|
|
107
|
+
self.playwright_available, self.browser_type, can_proceed = prerequisite_check()
|
|
108
|
+
|
|
109
|
+
if can_proceed:
|
|
110
|
+
import requests
|
|
111
|
+
self.session = requests.Session()
|
|
112
|
+
self.session.headers.update({'User-Agent': 'Mozilla/5.0'})
|
|
113
|
+
print(" [OK] 前置检查通过")
|
|
114
|
+
else:
|
|
115
|
+
print(" [FAIL] 前置检查失败")
|
|
116
|
+
|
|
117
|
+
def _static_analysis(self):
|
|
118
|
+
"""静态分析"""
|
|
119
|
+
# 使用共享的 parser 实例,避免重复 discover_js_files
|
|
120
|
+
self.parser = APIEndpointParser(self.target, self.session)
|
|
121
|
+
|
|
122
|
+
# 发现 JS 文件
|
|
123
|
+
self.js_files = self.parser.discover_js_files()
|
|
124
|
+
print(f" JS 文件: {len(self.js_files)}")
|
|
125
|
+
|
|
126
|
+
# 解析端点
|
|
127
|
+
self.static_endpoints = self.parser.parse_js_files(self.js_files)
|
|
128
|
+
print(f" 静态端点: {len(self.static_endpoints)}")
|
|
129
|
+
|
|
130
|
+
for ep in self.static_endpoints[:5]:
|
|
131
|
+
print(f" {ep.method} {ep.path}")
|
|
132
|
+
|
|
133
|
+
# 统计端点类型
|
|
134
|
+
path_apis = sum(1 for ep in self.static_endpoints if '/api/' in ep.path)
|
|
135
|
+
print(f" 含 /api/ 路径: {path_apis}")
|
|
136
|
+
|
|
137
|
+
def _detect_site_type(self):
|
|
138
|
+
"""判断站点类型"""
|
|
139
|
+
# 检测前端框架(从 JS 文件内容或 HTML)
|
|
140
|
+
html = self.session.get(self.target, timeout=10).text.lower()
|
|
141
|
+
|
|
142
|
+
frontend = 'Unknown'
|
|
143
|
+
ui = 'Unknown'
|
|
144
|
+
|
|
145
|
+
# 从 HTML 检测
|
|
146
|
+
if 'vue' in html:
|
|
147
|
+
frontend = 'Vue.js'
|
|
148
|
+
if 'react' in html:
|
|
149
|
+
frontend = 'React'
|
|
150
|
+
if 'angular' in html:
|
|
151
|
+
frontend = 'Angular'
|
|
152
|
+
if 'jquery' in html:
|
|
153
|
+
frontend = 'jQuery'
|
|
154
|
+
|
|
155
|
+
if 'element-ui' in html or 'element ui' in html:
|
|
156
|
+
ui = 'ElementUI'
|
|
157
|
+
if 'ant-design' in html:
|
|
158
|
+
ui = 'Ant Design'
|
|
159
|
+
|
|
160
|
+
# 根据 JS 文件数量和端点特征判断站点类型
|
|
161
|
+
if len(self.js_files) == 0:
|
|
162
|
+
self.site_type = 'pure_html'
|
|
163
|
+
print(f" 站点类型: 纯 HTML (无 JS)")
|
|
164
|
+
elif frontend == 'Unknown' and len(self.js_files) > 0:
|
|
165
|
+
# 可能是 JS 渲染的 SPA(HTML 不包含框架关键字)
|
|
166
|
+
self.site_type = 'modern_spa'
|
|
167
|
+
frontend = 'Vue.js/React (推断)'
|
|
168
|
+
print(f" 站点类型: 现代 SPA (基于 JS 文件)")
|
|
169
|
+
elif frontend in ['Vue.js', 'React', 'Angular']:
|
|
170
|
+
self.site_type = 'modern_spa'
|
|
171
|
+
print(f" 站点类型: 现代 SPA ({frontend})")
|
|
172
|
+
elif frontend == 'jQuery':
|
|
173
|
+
self.site_type = 'jquery_spa'
|
|
174
|
+
print(f" 站点类型: jQuery SPA")
|
|
175
|
+
else:
|
|
176
|
+
self.site_type = 'unknown'
|
|
177
|
+
print(f" 站点类型: 未知")
|
|
178
|
+
|
|
179
|
+
print(f" 前端框架: {frontend}")
|
|
180
|
+
print(f" UI 框架: {ui}")
|
|
181
|
+
print(f" JS 文件: {len(self.js_files)}")
|
|
182
|
+
|
|
183
|
+
# 决策:是否需要动态分析
|
|
184
|
+
if self.site_type in ['modern_spa', 'jquery_spa'] or len(self.js_files) > 0:
|
|
185
|
+
print(f" [建议] 启用动态分析 (发现 {len(self.js_files)} 个 JS 文件)")
|
|
186
|
+
else:
|
|
187
|
+
print(f" [建议] 可跳过动态分析")
|
|
188
|
+
|
|
189
|
+
def _probe_parent_paths(self):
|
|
190
|
+
"""父路径探测 - 使用共享的 parser 实例"""
|
|
191
|
+
# parser.parse_js_files 已经提取了父路径
|
|
192
|
+
self.parent_paths = self.parser.probe_parent_paths()
|
|
193
|
+
|
|
194
|
+
real_apis = sum(1 for p in self.parent_paths.values() if p.get('is_api'))
|
|
195
|
+
html_fallback = sum(1 for p in self.parent_paths.values() if not p.get('is_api'))
|
|
196
|
+
|
|
197
|
+
print(f" 父路径: {len(self.parent_paths)}")
|
|
198
|
+
print(f" JSON API: {real_apis}")
|
|
199
|
+
print(f" HTML fallback: {html_fallback}")
|
|
200
|
+
|
|
201
|
+
if real_apis > 0:
|
|
202
|
+
self.has_real_api = True
|
|
203
|
+
print(f" [OK] 发现真实 API")
|
|
204
|
+
else:
|
|
205
|
+
self.has_real_api = False
|
|
206
|
+
if html_fallback > 0:
|
|
207
|
+
self.has_nginx_fallback = True
|
|
208
|
+
print(f" [WARN] nginx fallback")
|
|
209
|
+
else:
|
|
210
|
+
print(f" [INFO] 未发现 API 响应")
|
|
211
|
+
|
|
212
|
+
# 保存检测到的 API 前缀
|
|
213
|
+
for p in self.parent_paths.values():
|
|
214
|
+
if p.get('prefix'):
|
|
215
|
+
self.api_prefix = p.get('prefix')
|
|
216
|
+
print(f" [API Prefix] {self.api_prefix}")
|
|
217
|
+
break
|
|
218
|
+
|
|
219
|
+
def _decide_next_action(self):
|
|
220
|
+
"""决策后续行动"""
|
|
221
|
+
print("\n 决策分析:")
|
|
222
|
+
|
|
223
|
+
# 决策 1: 是否执行动态分析
|
|
224
|
+
if self.site_type in ['modern_spa', 'jquery_spa'] and self.playwright_available:
|
|
225
|
+
print(" - [执行] 动态分析 (SPA 站点)")
|
|
226
|
+
self._dynamic_analysis()
|
|
227
|
+
else:
|
|
228
|
+
skip_reason = []
|
|
229
|
+
if self.site_type not in ['modern_spa', 'jquery_spa']:
|
|
230
|
+
skip_reason.append("非 SPA")
|
|
231
|
+
if not self.playwright_available:
|
|
232
|
+
skip_reason.append("Playwright 不可用")
|
|
233
|
+
print(f" - [跳过] 动态分析 ({', '.join(skip_reason)})")
|
|
234
|
+
|
|
235
|
+
# 决策 2: 是否执行 API Hook
|
|
236
|
+
if self.playwright_available and self.has_real_api:
|
|
237
|
+
print(" - [执行] API Hook (有真实 API)")
|
|
238
|
+
self._api_hook()
|
|
239
|
+
else:
|
|
240
|
+
skip_reason = []
|
|
241
|
+
if not self.playwright_available:
|
|
242
|
+
skip_reason.append("Playwright 不可用")
|
|
243
|
+
if not self.has_real_api:
|
|
244
|
+
skip_reason.append("无真实 API")
|
|
245
|
+
print(f" - [跳过] API Hook ({', '.join(skip_reason)})")
|
|
246
|
+
|
|
247
|
+
# 决策 3: 是否执行漏洞测试
|
|
248
|
+
if self.has_real_api:
|
|
249
|
+
print(" - [执行] 漏洞测试 (有真实 API)")
|
|
250
|
+
else:
|
|
251
|
+
print(" - [跳过] 漏洞测试 (无真实 API)")
|
|
252
|
+
|
|
253
|
+
def _dynamic_analysis(self):
|
|
254
|
+
"""动态分析"""
|
|
255
|
+
print("\n [动态分析] 启动 Playwright...")
|
|
256
|
+
try:
|
|
257
|
+
from core.dynamic_api_analyzer import DynamicAPIAnalyzer
|
|
258
|
+
|
|
259
|
+
analyzer = DynamicAPIAnalyzer(self.target)
|
|
260
|
+
results = analyzer.analyze_full()
|
|
261
|
+
|
|
262
|
+
count = len(results.get('endpoints', []))
|
|
263
|
+
print(f" [动态分析] 发现 {count} 个端点")
|
|
264
|
+
|
|
265
|
+
self.dynamic_endpoints = results.get('endpoints', [])
|
|
266
|
+
|
|
267
|
+
except Exception as e:
|
|
268
|
+
print(f" [动态分析] 失败: {e}")
|
|
269
|
+
|
|
270
|
+
def _api_hook(self):
|
|
271
|
+
"""API Hook"""
|
|
272
|
+
print("\n [API Hook] 启动 Hook...")
|
|
273
|
+
try:
|
|
274
|
+
from core.api_interceptor import APIInterceptor
|
|
275
|
+
|
|
276
|
+
interceptor = APIInterceptor(self.target)
|
|
277
|
+
results = interceptor.hook_all_apis()
|
|
278
|
+
|
|
279
|
+
count = len(results.get('endpoints', []))
|
|
280
|
+
print(f" [API Hook] 捕获 {count} 个 API 调用")
|
|
281
|
+
|
|
282
|
+
self.hooked_endpoints = results.get('endpoints', [])
|
|
283
|
+
|
|
284
|
+
except Exception as e:
|
|
285
|
+
print(f" [API Hook] 失败: {e}")
|
|
286
|
+
|
|
287
|
+
def _vulnerability_testing(self):
|
|
288
|
+
"""漏洞测试"""
|
|
289
|
+
print(" [漏洞测试] 执行...")
|
|
290
|
+
|
|
291
|
+
# 合并所有端点(统一格式)
|
|
292
|
+
all_endpoints = []
|
|
293
|
+
|
|
294
|
+
# 静态端点
|
|
295
|
+
for ep in self.static_endpoints:
|
|
296
|
+
all_endpoints.append({
|
|
297
|
+
'path': ep.path,
|
|
298
|
+
'method': ep.method,
|
|
299
|
+
})
|
|
300
|
+
|
|
301
|
+
# 动态端点
|
|
302
|
+
for ep in self.dynamic_endpoints:
|
|
303
|
+
all_endpoints.append({
|
|
304
|
+
'path': ep.get('path', ''),
|
|
305
|
+
'method': ep.get('method', 'GET'),
|
|
306
|
+
})
|
|
307
|
+
|
|
308
|
+
# Hook 端点
|
|
309
|
+
for ep in self.hooked_endpoints:
|
|
310
|
+
all_endpoints.append({
|
|
311
|
+
'path': ep.get('path', ''),
|
|
312
|
+
'method': ep.get('method', 'GET'),
|
|
313
|
+
})
|
|
314
|
+
|
|
315
|
+
# 去重
|
|
316
|
+
seen = set()
|
|
317
|
+
unique_endpoints = []
|
|
318
|
+
for ep in all_endpoints:
|
|
319
|
+
key = f"{ep['method']}:{ep['path']}"
|
|
320
|
+
if key not in seen and ep['path']:
|
|
321
|
+
seen.add(key)
|
|
322
|
+
unique_endpoints.append(ep)
|
|
323
|
+
|
|
324
|
+
print(f" 总端点: {len(unique_endpoints)}")
|
|
325
|
+
|
|
326
|
+
# 简化测试:只测 SQL 注入
|
|
327
|
+
sqli_payloads = ["' OR '1'='1"]
|
|
328
|
+
|
|
329
|
+
for ep in unique_endpoints[:10]:
|
|
330
|
+
path = ep['path']
|
|
331
|
+
method = ep['method']
|
|
332
|
+
|
|
333
|
+
if method != 'GET':
|
|
334
|
+
continue
|
|
335
|
+
|
|
336
|
+
# 使用完整 URL(包含 API 前缀)
|
|
337
|
+
if self.api_prefix:
|
|
338
|
+
url = f"http://{self.target.replace('http://', '').split('/')[0]}{self.api_prefix}{path.lstrip('/')}"
|
|
339
|
+
else:
|
|
340
|
+
url = self.target.rstrip('/') + path
|
|
341
|
+
|
|
342
|
+
if '?' not in url:
|
|
343
|
+
url = url + '?id=1'
|
|
344
|
+
|
|
345
|
+
try:
|
|
346
|
+
r = self.session.get(url.replace('id=1', sqli_payloads[0]), timeout=5)
|
|
347
|
+
ct = r.headers.get('Content-Type', '').lower()
|
|
348
|
+
|
|
349
|
+
if 'text/html' in ct:
|
|
350
|
+
continue
|
|
351
|
+
|
|
352
|
+
# 检测 SQL 错误
|
|
353
|
+
text_lower = r.text.lower()
|
|
354
|
+
sql_patterns = ['sql syntax', 'sql error', 'mysql', 'oracle',
|
|
355
|
+
'sqlite', 'sqlstate', 'postgresql']
|
|
356
|
+
if any(p in text_lower for p in sql_patterns):
|
|
357
|
+
self.vulnerabilities.append({
|
|
358
|
+
'type': 'SQL Injection',
|
|
359
|
+
'severity': 'CRITICAL',
|
|
360
|
+
'endpoint': path,
|
|
361
|
+
})
|
|
362
|
+
print(f" [!] {path}: SQL注入")
|
|
363
|
+
|
|
364
|
+
except:
|
|
365
|
+
pass
|
|
366
|
+
|
|
367
|
+
print(f" 发现漏洞: {len(self.vulnerabilities)}")
|
|
368
|
+
|
|
369
|
+
def _report_nginx_fallback(self):
|
|
370
|
+
"""报告 nginx fallback 问题"""
|
|
371
|
+
self.vulnerabilities.append({
|
|
372
|
+
'type': 'Backend API Unreachable / nginx fallback',
|
|
373
|
+
'severity': 'HIGH',
|
|
374
|
+
'endpoint': 'Multiple paths',
|
|
375
|
+
'evidence': f'{len(self.parent_paths)} paths return HTML (nginx fallback)'
|
|
376
|
+
})
|
|
377
|
+
print(f" 添加问题: nginx fallback")
|
|
378
|
+
|
|
379
|
+
def _cloud_storage_test(self):
|
|
380
|
+
"""云存储测试"""
|
|
381
|
+
tester = CloudStorageTester(self.target)
|
|
382
|
+
tester.session = self.session
|
|
383
|
+
findings, storage_url = tester.full_test(self.target)
|
|
384
|
+
|
|
385
|
+
print(f" 云存储: {storage_url}")
|
|
386
|
+
print(f" 发现: {len(findings)}")
|
|
387
|
+
|
|
388
|
+
self.cloud_findings = findings
|
|
389
|
+
|
|
390
|
+
def _generate_report(self):
|
|
391
|
+
"""生成报告"""
|
|
392
|
+
total_endpoints = len(self.static_endpoints) + len(self.dynamic_endpoints)
|
|
393
|
+
|
|
394
|
+
print("\n" + "=" * 50)
|
|
395
|
+
print(" 测试完成")
|
|
396
|
+
print("=" * 50)
|
|
397
|
+
print(f" 站点类型: {self.site_type}")
|
|
398
|
+
print(f" 静态端点: {len(self.static_endpoints)}")
|
|
399
|
+
print(f" 动态端点: {len(self.dynamic_endpoints)}")
|
|
400
|
+
print(f" Hook 端点: {len(self.hooked_endpoints)}")
|
|
401
|
+
print(f" JSON API: {sum(1 for p in self.parent_paths.values() if p.get('is_api'))}")
|
|
402
|
+
print(f" 漏洞: {len(self.vulnerabilities)}")
|
|
403
|
+
print(f" 云存储: {len(self.cloud_findings)}")
|
|
404
|
+
|
|
405
|
+
def _get_result(self):
|
|
406
|
+
"""获取结果"""
|
|
407
|
+
return {
|
|
408
|
+
'target': self.target,
|
|
409
|
+
'site_type': self.site_type,
|
|
410
|
+
'endpoints': {
|
|
411
|
+
'static': len(self.static_endpoints),
|
|
412
|
+
'dynamic': len(self.dynamic_endpoints),
|
|
413
|
+
'hooked': len(self.hooked_endpoints),
|
|
414
|
+
'parent_paths': len(self.parent_paths),
|
|
415
|
+
'json_api': sum(1 for p in self.parent_paths.values() if p.get('is_api')),
|
|
416
|
+
},
|
|
417
|
+
'vulnerabilities': self.vulnerabilities,
|
|
418
|
+
'cloud_findings': self.cloud_findings,
|
|
419
|
+
'api_prefix': self.api_prefix,
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
if __name__ == "__main__":
|
|
424
|
+
import sys
|
|
425
|
+
|
|
426
|
+
if len(sys.argv) > 1:
|
|
427
|
+
target = sys.argv[1]
|
|
428
|
+
else:
|
|
429
|
+
target = "http://58.216.151.148:8972/do/mh/jtmhindex"
|
|
430
|
+
|
|
431
|
+
executor = SKILLExecutor(target)
|
|
432
|
+
result = executor.run()
|
|
433
|
+
|
|
434
|
+
print("\n\n结果:")
|
|
435
|
+
print(result)
|