opencode-api-security-testing 3.0.8 → 3.0.10
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/agents/api-cyber-supervisor.md +9 -3
- package/agents/api-probing-miner.md +10 -2
- package/agents/api-resource-specialist.md +44 -35
- package/agents/api-vuln-verifier.md +56 -24
- package/package.json +1 -1
- package/postinstall.mjs +1 -0
- package/preuninstall.mjs +43 -32
- package/src/index.ts +3 -100
- package/README.md +0 -74
- package/SKILL.md +0 -1797
- package/core/advanced_recon.py +0 -788
- package/core/agentic_analyzer.py +0 -445
- package/core/analyzers/api_parser.py +0 -210
- package/core/analyzers/response_analyzer.py +0 -212
- package/core/analyzers/sensitive_finder.py +0 -184
- package/core/api_fuzzer.py +0 -422
- package/core/api_interceptor.py +0 -525
- package/core/api_parser.py +0 -955
- package/core/browser_tester.py +0 -479
- package/core/cloud_storage_tester.py +0 -1330
- package/core/collectors/__init__.py +0 -23
- package/core/collectors/api_path_finder.py +0 -300
- package/core/collectors/browser_collect.py +0 -645
- package/core/collectors/browser_collector.py +0 -411
- package/core/collectors/http_client.py +0 -111
- package/core/collectors/js_collector.py +0 -490
- package/core/collectors/js_parser.py +0 -780
- package/core/collectors/url_collector.py +0 -319
- package/core/context_manager.py +0 -682
- package/core/deep_api_tester_v35.py +0 -844
- package/core/deep_api_tester_v55.py +0 -366
- package/core/dynamic_api_analyzer.py +0 -532
- package/core/http_client.py +0 -179
- package/core/models.py +0 -296
- package/core/orchestrator.py +0 -890
- package/core/prerequisite.py +0 -227
- package/core/reasoning_engine.py +0 -1042
- package/core/response_classifier.py +0 -606
- package/core/runner.py +0 -938
- package/core/scan_engine.py +0 -599
- package/core/skill_executor.py +0 -435
- package/core/skill_executor_v2.py +0 -670
- package/core/skill_executor_v3.py +0 -704
- package/core/smart_analyzer.py +0 -687
- package/core/strategy_pool.py +0 -707
- package/core/testers/auth_tester.py +0 -264
- package/core/testers/idor_tester.py +0 -200
- package/core/testers/sqli_tester.py +0 -211
- package/core/testing_loop.py +0 -655
- package/core/utils/base_path_dict.py +0 -255
- package/core/utils/payload_lib.py +0 -167
- package/core/utils/ssrf_detector.py +0 -220
- package/core/verifiers/vuln_verifier.py +0 -536
- package/references/README.md +0 -72
- package/references/asset-discovery.md +0 -119
- package/references/fuzzing-patterns.md +0 -129
- package/references/graphql-guidance.md +0 -108
- package/references/intake.md +0 -84
- package/references/pua-agent.md +0 -192
- package/references/report-template.md +0 -156
- package/references/rest-guidance.md +0 -76
- package/references/severity-model.md +0 -76
- package/references/test-matrix.md +0 -86
- package/references/validation.md +0 -78
- package/references/vulnerabilities/01-sqli-tests.md +0 -1128
- package/references/vulnerabilities/02-user-enum-tests.md +0 -423
- package/references/vulnerabilities/03-jwt-tests.md +0 -499
- package/references/vulnerabilities/04-idor-tests.md +0 -362
- package/references/vulnerabilities/05-sensitive-data-tests.md +0 -466
- package/references/vulnerabilities/06-biz-logic-tests.md +0 -501
- package/references/vulnerabilities/07-security-config-tests.md +0 -511
- package/references/vulnerabilities/08-brute-force-tests.md +0 -457
- package/references/vulnerabilities/09-vulnerability-chains.md +0 -465
- package/references/vulnerabilities/10-auth-tests.md +0 -537
- package/references/vulnerabilities/11-graphql-tests.md +0 -355
- package/references/vulnerabilities/12-ssrf-tests.md +0 -396
- package/references/vulnerabilities/README.md +0 -148
- package/references/workflows.md +0 -192
- package/src/hooks/directory-agents-injector.ts +0 -106
|
@@ -1,704 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
SKILL 执行器 v3.0 - 配置驱动执行
|
|
3
|
-
|
|
4
|
-
根据 SKILL.md v3.0 定义的 vulnerability_detection_config 执行检测:
|
|
5
|
-
1. SQL 注入检测
|
|
6
|
-
2. 未授权访问检测
|
|
7
|
-
3. 越权访问检测
|
|
8
|
-
4. 敏感信息泄露检测
|
|
9
|
-
5. API 版本发现
|
|
10
|
-
6. 路径遍历检测
|
|
11
|
-
7. 认证绕过检测
|
|
12
|
-
8. 暴力破解检测
|
|
13
|
-
9. CORS 配置错误检测
|
|
14
|
-
10. 云存储安全检测
|
|
15
|
-
|
|
16
|
-
认证上下文支持:
|
|
17
|
-
- Bearer Token
|
|
18
|
-
- JWT
|
|
19
|
-
- Session Cookie
|
|
20
|
-
"""
|
|
21
|
-
|
|
22
|
-
import sys
|
|
23
|
-
sys.path.insert(0, '/workspace/skill-play/API-Security-Testing-Optimized')
|
|
24
|
-
|
|
25
|
-
from core.prerequisite import prerequisite_check
|
|
26
|
-
from core.api_parser import APIEndpointParser
|
|
27
|
-
from core.cloud_storage_tester import CloudStorageTester
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
class SKILLExecutorV3:
|
|
31
|
-
"""SKILL 执行器 v3.0 - 配置驱动"""
|
|
32
|
-
|
|
33
|
-
def __init__(self, target: str):
|
|
34
|
-
self.target = target
|
|
35
|
-
self.session = None
|
|
36
|
-
self.playwright_available = False
|
|
37
|
-
self.browser_type = None
|
|
38
|
-
|
|
39
|
-
# 资产发现结果
|
|
40
|
-
self.parser = None
|
|
41
|
-
self.js_files = []
|
|
42
|
-
self.static_endpoints = []
|
|
43
|
-
self.dynamic_endpoints = []
|
|
44
|
-
self.hooked_endpoints = []
|
|
45
|
-
self.parent_paths = {}
|
|
46
|
-
|
|
47
|
-
# API前缀
|
|
48
|
-
self.api_prefix = None
|
|
49
|
-
self.api_prefix_sources = {}
|
|
50
|
-
|
|
51
|
-
# 认证上下文
|
|
52
|
-
self.auth_context = {
|
|
53
|
-
'type': None,
|
|
54
|
-
'token': None,
|
|
55
|
-
'cookies': None,
|
|
56
|
-
'logged_in': False
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
# 测试结果
|
|
60
|
-
self.vulnerabilities = []
|
|
61
|
-
self.cloud_findings = []
|
|
62
|
-
|
|
63
|
-
# 决策状态
|
|
64
|
-
self.site_type = None
|
|
65
|
-
self.has_real_api = False
|
|
66
|
-
self.has_dynamic_endpoints = False
|
|
67
|
-
self.html_fallback_all = False
|
|
68
|
-
|
|
69
|
-
def run(self):
|
|
70
|
-
"""执行 SKILL 流程 v3.0"""
|
|
71
|
-
print("=" * 70)
|
|
72
|
-
print(" API Security Testing Skill v3.0 - 配置驱动执行")
|
|
73
|
-
print("=" * 70)
|
|
74
|
-
print(f" 目标: {self.target}")
|
|
75
|
-
print()
|
|
76
|
-
|
|
77
|
-
# ========== 阶段 0: 前置检查 ==========
|
|
78
|
-
print("[阶段 0] 前置检查")
|
|
79
|
-
print("-" * 50)
|
|
80
|
-
self._check_prerequisites()
|
|
81
|
-
|
|
82
|
-
# ========== 阶段 1: 资产发现 ==========
|
|
83
|
-
print("\n[阶段 1] 资产发现")
|
|
84
|
-
print("-" * 50)
|
|
85
|
-
|
|
86
|
-
self._static_analysis()
|
|
87
|
-
self._detect_site_type()
|
|
88
|
-
self._probe_parent_paths()
|
|
89
|
-
|
|
90
|
-
if self.site_type in ['modern_spa', 'jquery_spa'] and self.playwright_available:
|
|
91
|
-
self._dynamic_analysis()
|
|
92
|
-
|
|
93
|
-
if self.playwright_available and (self.has_real_api or self.has_dynamic_endpoints):
|
|
94
|
-
self._api_hook()
|
|
95
|
-
|
|
96
|
-
# ========== 阶段 2: 漏洞检测 (配置驱动) ==========
|
|
97
|
-
print("\n[阶段 2] 漏洞检测")
|
|
98
|
-
print("-" * 50)
|
|
99
|
-
|
|
100
|
-
self._update_decision_state()
|
|
101
|
-
|
|
102
|
-
if self.has_real_api or self.has_dynamic_endpoints:
|
|
103
|
-
self._run_vulnerability_tests()
|
|
104
|
-
else:
|
|
105
|
-
if self.html_fallback_all:
|
|
106
|
-
self._report_nginx_fallback()
|
|
107
|
-
|
|
108
|
-
# ========== 阶段 3: 云存储测试 ==========
|
|
109
|
-
print("\n[阶段 3] 云存储测试")
|
|
110
|
-
print("-" * 50)
|
|
111
|
-
self._cloud_storage_test()
|
|
112
|
-
|
|
113
|
-
# ========== 阶段 4: 报告 ==========
|
|
114
|
-
print("\n[阶段 4] 报告")
|
|
115
|
-
print("-" * 50)
|
|
116
|
-
self._generate_report()
|
|
117
|
-
|
|
118
|
-
return self._get_result()
|
|
119
|
-
|
|
120
|
-
def _check_prerequisites(self):
|
|
121
|
-
"""前置检查"""
|
|
122
|
-
self.playwright_available, self.browser_type, can_proceed = prerequisite_check()
|
|
123
|
-
|
|
124
|
-
if can_proceed:
|
|
125
|
-
import requests
|
|
126
|
-
self.session = requests.Session()
|
|
127
|
-
self.session.headers.update({'User-Agent': 'Mozilla/5.0'})
|
|
128
|
-
print(" [OK] 前置检查通过")
|
|
129
|
-
else:
|
|
130
|
-
print(" [FAIL] 前置检查失败")
|
|
131
|
-
|
|
132
|
-
def _static_analysis(self):
|
|
133
|
-
"""静态分析"""
|
|
134
|
-
self.parser = APIEndpointParser(self.target, self.session)
|
|
135
|
-
|
|
136
|
-
self.js_files = self.parser.discover_js_files()
|
|
137
|
-
print(f" JS 文件: {len(self.js_files)}")
|
|
138
|
-
|
|
139
|
-
self.static_endpoints = self.parser.parse_js_files(self.js_files)
|
|
140
|
-
print(f" 静态端点: {len(self.static_endpoints)}")
|
|
141
|
-
|
|
142
|
-
for ep in self.static_endpoints[:5]:
|
|
143
|
-
print(f" {ep.method} {ep.path}")
|
|
144
|
-
|
|
145
|
-
def _detect_site_type(self):
|
|
146
|
-
"""判断站点类型"""
|
|
147
|
-
html = self.session.get(self.target, timeout=10).text.lower()
|
|
148
|
-
|
|
149
|
-
frontend = 'Unknown'
|
|
150
|
-
if 'vue' in html: frontend = 'Vue.js'
|
|
151
|
-
elif 'react' in html: frontend = 'React'
|
|
152
|
-
elif 'jquery' in html: frontend = 'jQuery'
|
|
153
|
-
|
|
154
|
-
if len(self.js_files) == 0:
|
|
155
|
-
self.site_type = 'pure_html'
|
|
156
|
-
elif frontend == 'Unknown':
|
|
157
|
-
self.site_type = 'modern_spa'
|
|
158
|
-
else:
|
|
159
|
-
self.site_type = 'modern_spa'
|
|
160
|
-
|
|
161
|
-
print(f" 站点类型: {self.site_type}")
|
|
162
|
-
|
|
163
|
-
def _probe_parent_paths(self):
|
|
164
|
-
"""父路径探测"""
|
|
165
|
-
self.parent_paths = self.parser.probe_parent_paths()
|
|
166
|
-
|
|
167
|
-
json_api_count = sum(1 for p in self.parent_paths.values() if p.get('is_api'))
|
|
168
|
-
print(f" 父路径: {len(self.parent_paths)}, JSON API: {json_api_count}")
|
|
169
|
-
|
|
170
|
-
if json_api_count > 0:
|
|
171
|
-
self.has_real_api = True
|
|
172
|
-
|
|
173
|
-
def _dynamic_analysis(self):
|
|
174
|
-
"""动态分析"""
|
|
175
|
-
print(" [动态分析] 启动 Playwright...")
|
|
176
|
-
try:
|
|
177
|
-
from core.dynamic_api_analyzer import DynamicAPIAnalyzer
|
|
178
|
-
from urllib.parse import urlparse
|
|
179
|
-
|
|
180
|
-
analyzer = DynamicAPIAnalyzer(self.target)
|
|
181
|
-
results = analyzer.analyze_full()
|
|
182
|
-
|
|
183
|
-
count = len(results.get('endpoints', []))
|
|
184
|
-
print(f" [动态分析] 发现 {count} 个端点")
|
|
185
|
-
|
|
186
|
-
self.dynamic_endpoints = results.get('endpoints', [])
|
|
187
|
-
|
|
188
|
-
# 从动态端点提取 API 前缀
|
|
189
|
-
target_host = urlparse(self.target).netloc
|
|
190
|
-
for ep in self.dynamic_endpoints:
|
|
191
|
-
ep_path = ep.get('path', '')
|
|
192
|
-
# 跳过非API资源文件
|
|
193
|
-
if any(ext in ep_path.lower() for ext in ['.js', '.css', '.png', '.jpg', '.html', '.svg']):
|
|
194
|
-
continue
|
|
195
|
-
if ep_path.startswith('http'):
|
|
196
|
-
ep_host = urlparse(ep_path).netloc
|
|
197
|
-
ep_path_only = urlparse(ep_path).path
|
|
198
|
-
if ep_host == target_host:
|
|
199
|
-
parts = ep_path_only.strip('/').split('/')
|
|
200
|
-
if len(parts) >= 2:
|
|
201
|
-
potential_prefix = '/' + '/'.join(parts[:2])
|
|
202
|
-
if potential_prefix not in ['/login', '/logout']:
|
|
203
|
-
self.api_prefix = potential_prefix
|
|
204
|
-
print(f" [API Prefix] from dynamic: {self.api_prefix}")
|
|
205
|
-
break
|
|
206
|
-
|
|
207
|
-
# 从静态端点推断API前缀(备选方案)
|
|
208
|
-
if not self.api_prefix and self.static_endpoints:
|
|
209
|
-
# 静态端点如 /sys/user/getUserInfo 说明有 /sys/ 路径
|
|
210
|
-
# 需要推断完整的API前缀
|
|
211
|
-
static_paths = [ep.path for ep in self.static_endpoints[:10]]
|
|
212
|
-
|
|
213
|
-
# 提取静态端点的共同前缀
|
|
214
|
-
common_prefix = ''
|
|
215
|
-
if static_paths:
|
|
216
|
-
parts_list = [p.strip('/').split('/') for p in static_paths if p.startswith('/')]
|
|
217
|
-
if parts_list:
|
|
218
|
-
# 找共同的前两个路径段
|
|
219
|
-
if len(parts_list[0]) >= 2:
|
|
220
|
-
prefix_parts = [parts_list[0][0], parts_list[0][1]]
|
|
221
|
-
common_prefix = '/' + '/'.join(prefix_parts)
|
|
222
|
-
|
|
223
|
-
# 如果目标URL有子路径,尝试使用子路径作为API前缀
|
|
224
|
-
from urllib.parse import urlparse
|
|
225
|
-
target_path = urlparse(self.target).path.strip('/')
|
|
226
|
-
if target_path:
|
|
227
|
-
# 目标URL的子路径可能是API前缀(如 /ipark-admin -> /ipark)
|
|
228
|
-
target_base = target_path.replace('-admin', '').replace('-api', '')
|
|
229
|
-
if target_base and not target_base.startswith('_'):
|
|
230
|
-
self.api_prefix = '/' + target_base
|
|
231
|
-
else:
|
|
232
|
-
self.api_prefix = '/' + target_path.split('/')[0]
|
|
233
|
-
else:
|
|
234
|
-
self.api_prefix = common_prefix or ''
|
|
235
|
-
|
|
236
|
-
print(f" [API Prefix] inferred: {self.api_prefix}")
|
|
237
|
-
|
|
238
|
-
if count > 0:
|
|
239
|
-
self.has_dynamic_endpoints = True
|
|
240
|
-
|
|
241
|
-
except Exception as e:
|
|
242
|
-
print(f" [动态分析] 失败: {e}")
|
|
243
|
-
|
|
244
|
-
def _api_hook(self):
|
|
245
|
-
"""API Hook - 尝试获取认证上下文"""
|
|
246
|
-
print(" [API Hook] 尝试获取认证上下文...")
|
|
247
|
-
try:
|
|
248
|
-
from core.api_interceptor import APIInterceptor
|
|
249
|
-
|
|
250
|
-
interceptor = APIInterceptor(self.target)
|
|
251
|
-
results = interceptor.hook_all_apis()
|
|
252
|
-
|
|
253
|
-
# 分析认证相关请求
|
|
254
|
-
for req in results.get('requests', [])[:20]:
|
|
255
|
-
if '/login' in req.get('url', '').lower():
|
|
256
|
-
print(f" [认证] 发现登录请求")
|
|
257
|
-
break
|
|
258
|
-
|
|
259
|
-
except Exception as e:
|
|
260
|
-
print(f" [API Hook] 失败: {e}")
|
|
261
|
-
|
|
262
|
-
def _update_decision_state(self):
|
|
263
|
-
"""更新决策状态"""
|
|
264
|
-
json_api_count = sum(1 for p in self.parent_paths.values() if p.get('is_api'))
|
|
265
|
-
dynamic_count = len(self.dynamic_endpoints)
|
|
266
|
-
|
|
267
|
-
self.has_real_api = json_api_count > 0 or dynamic_count > 0
|
|
268
|
-
self.has_dynamic_endpoints = dynamic_count > 0
|
|
269
|
-
|
|
270
|
-
self.html_fallback_all = (
|
|
271
|
-
len(self.parent_paths) > 0 and
|
|
272
|
-
json_api_count == 0 and
|
|
273
|
-
dynamic_count == 0
|
|
274
|
-
)
|
|
275
|
-
|
|
276
|
-
print(f" has_real_api: {self.has_real_api}")
|
|
277
|
-
print(f" has_dynamic_endpoints: {self.has_dynamic_endpoints}")
|
|
278
|
-
|
|
279
|
-
def _run_vulnerability_tests(self):
|
|
280
|
-
"""漏洞测试 - 遵循 SKILL.md 配置执行"""
|
|
281
|
-
|
|
282
|
-
all_endpoints = self._merge_all_endpoints()
|
|
283
|
-
sorted_endpoints = self._sort_by_priority(all_endpoints)
|
|
284
|
-
|
|
285
|
-
print(f" [漏洞检测] 总端点: {len(sorted_endpoints)}")
|
|
286
|
-
|
|
287
|
-
# ===== 高优先级检测 =====
|
|
288
|
-
print("\n [高优先级] SQL注入检测...")
|
|
289
|
-
self._test_sql_injection(sorted_endpoints)
|
|
290
|
-
|
|
291
|
-
print("\n [高优先级] 认证绕过检测...")
|
|
292
|
-
self._test_auth_bypass(sorted_endpoints)
|
|
293
|
-
|
|
294
|
-
print("\n [高优先级] 越权访问检测...")
|
|
295
|
-
self._test_privilege_escalation(sorted_endpoints)
|
|
296
|
-
|
|
297
|
-
# ===== 中优先级检测 =====
|
|
298
|
-
print("\n [中优先级] 未授权访问检测...")
|
|
299
|
-
self._test_unauthorized_access(sorted_endpoints)
|
|
300
|
-
|
|
301
|
-
print("\n [中优先级] 敏感信息泄露检测...")
|
|
302
|
-
self._test_sensitive_data(sorted_endpoints)
|
|
303
|
-
|
|
304
|
-
print("\n [中优先级] 路径遍历检测...")
|
|
305
|
-
self._test_path_traversal(sorted_endpoints)
|
|
306
|
-
|
|
307
|
-
print("\n [中优先级] CORS配置错误检测...")
|
|
308
|
-
self._test_cors_misconfiguration(sorted_endpoints)
|
|
309
|
-
|
|
310
|
-
# ===== 低优先级检测 =====
|
|
311
|
-
print("\n [低优先级] API版本发现...")
|
|
312
|
-
self._test_api_version_discovery()
|
|
313
|
-
|
|
314
|
-
# 深度Fuzzing
|
|
315
|
-
print("\n [深度测试] API Fuzzing...")
|
|
316
|
-
self._run_deep_fuzz_test(sorted_endpoints)
|
|
317
|
-
|
|
318
|
-
print(f"\n [漏洞检测] 完成,发现 {len(self.vulnerabilities)} 个问题")
|
|
319
|
-
|
|
320
|
-
def _merge_all_endpoints(self):
|
|
321
|
-
"""合并所有端点"""
|
|
322
|
-
all_endpoints = []
|
|
323
|
-
seen = set()
|
|
324
|
-
|
|
325
|
-
for ep in self.static_endpoints:
|
|
326
|
-
key = f"{ep.method}:{ep.path}"
|
|
327
|
-
if key not in seen:
|
|
328
|
-
seen.add(key)
|
|
329
|
-
all_endpoints.append({
|
|
330
|
-
'path': ep.path,
|
|
331
|
-
'method': ep.method,
|
|
332
|
-
'source': 'static'
|
|
333
|
-
})
|
|
334
|
-
|
|
335
|
-
for ep in self.dynamic_endpoints:
|
|
336
|
-
key = f"{ep.get('method', 'GET')}:{ep.get('path', '')}"
|
|
337
|
-
if key not in seen and ep.get('path'):
|
|
338
|
-
seen.add(key)
|
|
339
|
-
all_endpoints.append({
|
|
340
|
-
'path': ep.get('path'),
|
|
341
|
-
'method': ep.get('method', 'GET'),
|
|
342
|
-
'source': 'dynamic'
|
|
343
|
-
})
|
|
344
|
-
|
|
345
|
-
for ep in self.hooked_endpoints:
|
|
346
|
-
key = f"{ep.get('method', 'GET')}:{ep.get('path', '')}"
|
|
347
|
-
if key not in seen and ep.get('path'):
|
|
348
|
-
seen.add(key)
|
|
349
|
-
all_endpoints.append({
|
|
350
|
-
'path': ep.get('path'),
|
|
351
|
-
'method': ep.get('method', 'GET'),
|
|
352
|
-
'source': 'hooked'
|
|
353
|
-
})
|
|
354
|
-
|
|
355
|
-
return all_endpoints
|
|
356
|
-
|
|
357
|
-
def _sort_by_priority(self, endpoints):
|
|
358
|
-
"""按优先级排序"""
|
|
359
|
-
priority_map = {'high': 0, 'medium': 1, 'low': 2}
|
|
360
|
-
|
|
361
|
-
def get_priority(ep):
|
|
362
|
-
path = ep['path'].lower()
|
|
363
|
-
if any(p in path for p in ['/auth/', '/login', '/oauth', '/admin', '/user/delete', '/user/export']):
|
|
364
|
-
return 'high'
|
|
365
|
-
if any(p in path for p in ['/api/', '/system/', '/config/', '/manage/']):
|
|
366
|
-
return 'medium'
|
|
367
|
-
return 'low'
|
|
368
|
-
|
|
369
|
-
for ep in endpoints:
|
|
370
|
-
ep['priority'] = get_priority(ep)
|
|
371
|
-
|
|
372
|
-
return sorted(endpoints, key=lambda x: priority_map.get(x.get('priority'), 2))
|
|
373
|
-
|
|
374
|
-
def _build_url(self, path):
|
|
375
|
-
"""构建完整URL"""
|
|
376
|
-
from urllib.parse import urlparse
|
|
377
|
-
|
|
378
|
-
if path.startswith('http'):
|
|
379
|
-
return urlparse(path).path, {}
|
|
380
|
-
|
|
381
|
-
base = self.target.split('?')[0].rstrip('/')
|
|
382
|
-
|
|
383
|
-
# 添加 API 前缀
|
|
384
|
-
if self.api_prefix and not path.startswith('/personnelWeb') and not path.startswith('/api'):
|
|
385
|
-
if path.startswith('/users') or path.startswith('/system') or path.startswith('/menu'):
|
|
386
|
-
path = '/' + self.api_prefix.split('/')[1] + path
|
|
387
|
-
|
|
388
|
-
return base + path, {}
|
|
389
|
-
|
|
390
|
-
# ===== 漏洞检测方法 =====
|
|
391
|
-
|
|
392
|
-
def _test_sql_injection(self, endpoints):
|
|
393
|
-
"""SQL 注入检测 - SKILL.md 配置"""
|
|
394
|
-
payloads = [
|
|
395
|
-
"' OR '1'='1",
|
|
396
|
-
"' OR '1'='1' --",
|
|
397
|
-
"' OR '1'='1' #",
|
|
398
|
-
"1' OR '1'='1",
|
|
399
|
-
]
|
|
400
|
-
params = ['id', 'page', 'pageNum', 'pageSize', 'userId']
|
|
401
|
-
error_patterns = [
|
|
402
|
-
'sql syntax', 'sql error', 'mysql', 'oracle', 'sqlite',
|
|
403
|
-
'sqlstate', 'postgresql', 'syntax error', 'microsoft sql'
|
|
404
|
-
]
|
|
405
|
-
|
|
406
|
-
tested = 0
|
|
407
|
-
for ep in endpoints[:30]:
|
|
408
|
-
path = ep['path']
|
|
409
|
-
method = ep.get('method', 'GET')
|
|
410
|
-
|
|
411
|
-
base_url, _ = self._build_url(path)
|
|
412
|
-
|
|
413
|
-
for param in params:
|
|
414
|
-
for payload in payloads[:2]:
|
|
415
|
-
try:
|
|
416
|
-
if method == 'POST':
|
|
417
|
-
r = self.session.post(base_url, json={param: payload}, timeout=5)
|
|
418
|
-
else:
|
|
419
|
-
r = self.session.get(f"{base_url}?{param}={payload}", timeout=5)
|
|
420
|
-
|
|
421
|
-
tested += 1
|
|
422
|
-
ct = r.headers.get('Content-Type', '').lower()
|
|
423
|
-
if 'text/html' in ct:
|
|
424
|
-
continue
|
|
425
|
-
|
|
426
|
-
text_lower = r.text.lower()
|
|
427
|
-
if any(p in text_lower for p in error_patterns):
|
|
428
|
-
self.vulnerabilities.append({
|
|
429
|
-
'type': 'SQL Injection',
|
|
430
|
-
'severity': 'CRITICAL',
|
|
431
|
-
'endpoint': path,
|
|
432
|
-
'param': param,
|
|
433
|
-
'payload': payload,
|
|
434
|
-
'evidence': 'SQL error detected'
|
|
435
|
-
})
|
|
436
|
-
print(f" [!] SQL注入: {path} ({param})")
|
|
437
|
-
break
|
|
438
|
-
except:
|
|
439
|
-
pass
|
|
440
|
-
|
|
441
|
-
if any(v['endpoint'] == path for v in self.vulnerabilities):
|
|
442
|
-
break
|
|
443
|
-
|
|
444
|
-
print(f" 测试了 {tested} 个请求")
|
|
445
|
-
|
|
446
|
-
def _test_auth_bypass(self, endpoints):
|
|
447
|
-
"""认证绕过检测 - SKILL.md 配置"""
|
|
448
|
-
login_endpoints = [ep for ep in endpoints if '/login' in ep['path'].lower() or '/auth' in ep['path'].lower()]
|
|
449
|
-
|
|
450
|
-
if not login_endpoints:
|
|
451
|
-
print(" 未发现登录端点,跳过")
|
|
452
|
-
return
|
|
453
|
-
|
|
454
|
-
print(f" 发现 {len(login_endpoints)} 个认证端点")
|
|
455
|
-
|
|
456
|
-
# 测试空token
|
|
457
|
-
for ep in login_endpoints[:3]:
|
|
458
|
-
path = ep['path']
|
|
459
|
-
base_url, _ = self._build_url(path)
|
|
460
|
-
|
|
461
|
-
try:
|
|
462
|
-
# 空token
|
|
463
|
-
r = self.session.post(base_url, json={}, timeout=5)
|
|
464
|
-
if r.status_code == 200 and 'token' not in r.text.lower():
|
|
465
|
-
print(f" [可疑] {path} 空payload返回200")
|
|
466
|
-
except:
|
|
467
|
-
pass
|
|
468
|
-
|
|
469
|
-
def _test_privilege_escalation(self, endpoints):
|
|
470
|
-
"""越权访问检测 - SKILL.md 配置"""
|
|
471
|
-
# 测试低权限 token 访问高权限资源
|
|
472
|
-
admin_paths = ['/admin', '/system/admin', '/manage']
|
|
473
|
-
|
|
474
|
-
for ep in endpoints:
|
|
475
|
-
path = ep['path'].lower()
|
|
476
|
-
if any(p in path for p in admin_paths):
|
|
477
|
-
base_url, _ = self._build_url(ep['path'])
|
|
478
|
-
try:
|
|
479
|
-
r = self.session.get(base_url, timeout=5)
|
|
480
|
-
if r.status_code == 200:
|
|
481
|
-
self.vulnerabilities.append({
|
|
482
|
-
'type': 'Vertical Privilege Escalation',
|
|
483
|
-
'severity': 'HIGH',
|
|
484
|
-
'endpoint': ep['path'],
|
|
485
|
-
'evidence': 'Admin endpoint accessible'
|
|
486
|
-
})
|
|
487
|
-
print(f" [!] 越权: {ep['path']}")
|
|
488
|
-
except:
|
|
489
|
-
pass
|
|
490
|
-
|
|
491
|
-
def _test_unauthorized_access(self, endpoints):
|
|
492
|
-
"""未授权访问检测 - SKILL.md 配置"""
|
|
493
|
-
sensitive_patterns = ['/admin', '/user/list', '/user/export', '/config', '/system', '/manage']
|
|
494
|
-
|
|
495
|
-
found = 0
|
|
496
|
-
for ep in endpoints:
|
|
497
|
-
path = ep['path'].lower()
|
|
498
|
-
if not any(p in path for p in sensitive_patterns):
|
|
499
|
-
continue
|
|
500
|
-
|
|
501
|
-
base_url, _ = self._build_url(ep['path'])
|
|
502
|
-
try:
|
|
503
|
-
if ep.get('method') == 'POST':
|
|
504
|
-
r = self.session.post(base_url, json={}, timeout=5)
|
|
505
|
-
else:
|
|
506
|
-
r = self.session.get(base_url, timeout=5)
|
|
507
|
-
|
|
508
|
-
found += 1
|
|
509
|
-
if r.status_code == 200:
|
|
510
|
-
text_lower = r.text.lower()
|
|
511
|
-
if any(k in text_lower for k in ['user', 'admin', 'password', 'email', 'phone']):
|
|
512
|
-
self.vulnerabilities.append({
|
|
513
|
-
'type': 'Unauthorized Access',
|
|
514
|
-
'severity': 'HIGH',
|
|
515
|
-
'endpoint': ep['path'],
|
|
516
|
-
'evidence': 'Sensitive data exposed without auth'
|
|
517
|
-
})
|
|
518
|
-
print(f" [!] 未授权: {ep['path']}")
|
|
519
|
-
except:
|
|
520
|
-
pass
|
|
521
|
-
|
|
522
|
-
print(f" 测试了 {found} 个敏感端点")
|
|
523
|
-
|
|
524
|
-
def _test_sensitive_data(self, endpoints):
|
|
525
|
-
"""敏感信息泄露检测 - SKILL.md 配置"""
|
|
526
|
-
sensitive_fields = ['password', 'passwd', 'secret', 'token', 'api_key', 'jwt', 'private_key']
|
|
527
|
-
|
|
528
|
-
for ep in endpoints[:20]:
|
|
529
|
-
base_url, _ = self._build_url(ep['path'])
|
|
530
|
-
try:
|
|
531
|
-
if ep.get('method') == 'POST':
|
|
532
|
-
r = self.session.post(base_url, json={}, timeout=5)
|
|
533
|
-
else:
|
|
534
|
-
r = self.session.get(base_url, timeout=5)
|
|
535
|
-
|
|
536
|
-
if r.status_code == 200:
|
|
537
|
-
text_lower = r.text.lower()
|
|
538
|
-
for field in sensitive_fields:
|
|
539
|
-
if f'"{field}"' in r.text or f'"{field.upper()}"' in r.text:
|
|
540
|
-
self.vulnerabilities.append({
|
|
541
|
-
'type': 'Sensitive Data Exposure',
|
|
542
|
-
'severity': 'MEDIUM',
|
|
543
|
-
'endpoint': ep['path'],
|
|
544
|
-
'evidence': f'Contains field: {field}'
|
|
545
|
-
})
|
|
546
|
-
print(f" [!] 敏感信息: {ep['path']} ({field})")
|
|
547
|
-
break
|
|
548
|
-
except:
|
|
549
|
-
pass
|
|
550
|
-
|
|
551
|
-
def _test_path_traversal(self, endpoints):
|
|
552
|
-
"""路径遍历检测 - SKILL.md 配置"""
|
|
553
|
-
traversal_payloads = [
|
|
554
|
-
"../../../etc/passwd",
|
|
555
|
-
"..\\..\\..\\windows\\system32\\config\\sam",
|
|
556
|
-
"%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd"
|
|
557
|
-
]
|
|
558
|
-
|
|
559
|
-
file_endpoints = [ep for ep in endpoints if any(p in ep['path'].lower() for p in ['/file', '/download', '/export', '/import'])]
|
|
560
|
-
|
|
561
|
-
for ep in file_endpoints[:10]:
|
|
562
|
-
base_url, _ = self._build_url(ep['path'])
|
|
563
|
-
for payload in traversal_payloads[:1]:
|
|
564
|
-
try:
|
|
565
|
-
r = self.session.get(f"{base_url}?path={payload}", timeout=5)
|
|
566
|
-
if 'root:' in r.text or '/bin/bash' in r.text:
|
|
567
|
-
self.vulnerabilities.append({
|
|
568
|
-
'type': 'Path Traversal',
|
|
569
|
-
'severity': 'HIGH',
|
|
570
|
-
'endpoint': ep['path'],
|
|
571
|
-
'payload': payload,
|
|
572
|
-
'evidence': 'System file exposed'
|
|
573
|
-
})
|
|
574
|
-
print(f" [!] 路径遍历: {ep['path']}")
|
|
575
|
-
except:
|
|
576
|
-
pass
|
|
577
|
-
|
|
578
|
-
def _test_cors_misconfiguration(self, endpoints):
|
|
579
|
-
"""CORS配置错误检测 - SKILL.md 配置"""
|
|
580
|
-
for ep in endpoints[:10]:
|
|
581
|
-
base_url, _ = self._build_url(ep['path'])
|
|
582
|
-
try:
|
|
583
|
-
r = self.session.get(base_url, timeout=5)
|
|
584
|
-
cors_origin = r.headers.get('Access-Control-Allow-Origin', '')
|
|
585
|
-
cors_cred = r.headers.get('Access-Control-Allow-Credentials', '')
|
|
586
|
-
|
|
587
|
-
if cors_origin == '*' and cors_cred == 'true':
|
|
588
|
-
self.vulnerabilities.append({
|
|
589
|
-
'type': 'CORS Misconfiguration',
|
|
590
|
-
'severity': 'MEDIUM',
|
|
591
|
-
'endpoint': ep['path'],
|
|
592
|
-
'evidence': 'Allow-Origin: * with Allow-Credentials: true'
|
|
593
|
-
})
|
|
594
|
-
print(f" [!] CORS错误: {ep['path']}")
|
|
595
|
-
elif cors_origin == '*':
|
|
596
|
-
print(f" [低] CORS宽松: {ep['path']} (Origin: *)")
|
|
597
|
-
except:
|
|
598
|
-
pass
|
|
599
|
-
|
|
600
|
-
def _test_api_version_discovery(self):
|
|
601
|
-
"""API版本发现 - SKILL.md 配置"""
|
|
602
|
-
version_paths = ['/v1', '/v2', '/v3', '/api/v1', '/api/v2', '/swagger', '/swagger-ui', '/api-docs']
|
|
603
|
-
target_base = self.target.split('?')[0].rstrip('/')
|
|
604
|
-
|
|
605
|
-
for vp in version_paths:
|
|
606
|
-
try:
|
|
607
|
-
r = self.session.get(target_base + vp, timeout=3, allow_redirects=False)
|
|
608
|
-
if r.status_code in [200, 301, 302]:
|
|
609
|
-
print(f" [发现] {vp} -> {r.status_code}")
|
|
610
|
-
except:
|
|
611
|
-
pass
|
|
612
|
-
|
|
613
|
-
def _run_deep_fuzz_test(self, endpoints):
|
|
614
|
-
"""深度Fuzzing测试"""
|
|
615
|
-
from core.api_fuzzer import APIfuzzer
|
|
616
|
-
|
|
617
|
-
fuzzer = APIfuzzer(session=self.session)
|
|
618
|
-
|
|
619
|
-
api_paths = []
|
|
620
|
-
for ep in endpoints:
|
|
621
|
-
from urllib.parse import urlparse
|
|
622
|
-
path = ep['path']
|
|
623
|
-
if path.startswith('http'):
|
|
624
|
-
path = urlparse(path).path
|
|
625
|
-
api_paths.append(path)
|
|
626
|
-
|
|
627
|
-
fuzz_targets = fuzzer.generate_parent_fuzz_targets(api_paths, max_per_parent=30)
|
|
628
|
-
print(f" 生成 {len(fuzz_targets)} 个Fuzz目标")
|
|
629
|
-
|
|
630
|
-
base_url = self.target.split('?')[0].rstrip('/')
|
|
631
|
-
results = fuzzer.fuzz_paths(base_url, fuzz_targets[:100], timeout=3.0)
|
|
632
|
-
|
|
633
|
-
alive = fuzzer.get_alive_endpoints()
|
|
634
|
-
print(f" 存活端点: {len(alive)}")
|
|
635
|
-
|
|
636
|
-
def _report_nginx_fallback(self):
|
|
637
|
-
"""报告 nginx fallback"""
|
|
638
|
-
self.vulnerabilities.append({
|
|
639
|
-
'type': 'Backend API Unreachable / nginx fallback',
|
|
640
|
-
'severity': 'HIGH',
|
|
641
|
-
'evidence': '后端API服务不可达'
|
|
642
|
-
})
|
|
643
|
-
|
|
644
|
-
def _cloud_storage_test(self):
|
|
645
|
-
"""云存储测试"""
|
|
646
|
-
tester = CloudStorageTester(self.target)
|
|
647
|
-
tester.session = self.session
|
|
648
|
-
findings, storage_url = tester.full_test(self.target)
|
|
649
|
-
|
|
650
|
-
print(f" 云存储: {storage_url or 'N/A'}, 发现: {len(findings)}")
|
|
651
|
-
self.cloud_findings = findings
|
|
652
|
-
|
|
653
|
-
def _generate_report(self):
|
|
654
|
-
"""生成报告"""
|
|
655
|
-
print("\n" + "=" * 50)
|
|
656
|
-
print(" 测试完成 v3.0")
|
|
657
|
-
print("=" * 50)
|
|
658
|
-
print(f" 站点类型: {self.site_type}")
|
|
659
|
-
print(f" 静态端点: {len(self.static_endpoints)}")
|
|
660
|
-
print(f" 动态端点: {len(self.dynamic_endpoints)}")
|
|
661
|
-
print(f" API Prefix: {self.api_prefix}")
|
|
662
|
-
|
|
663
|
-
# 按严重性分组统计
|
|
664
|
-
critical = [v for v in self.vulnerabilities if v['severity'] == 'CRITICAL']
|
|
665
|
-
high = [v for v in self.vulnerabilities if v['severity'] == 'HIGH']
|
|
666
|
-
medium = [v for v in self.vulnerabilities if v['severity'] == 'MEDIUM']
|
|
667
|
-
|
|
668
|
-
print(f"\n 漏洞统计:")
|
|
669
|
-
print(f" CRITICAL: {len(critical)}")
|
|
670
|
-
print(f" HIGH: {len(high)}")
|
|
671
|
-
print(f" MEDIUM: {len(medium)}")
|
|
672
|
-
|
|
673
|
-
if self.vulnerabilities:
|
|
674
|
-
print(f"\n 发现的问题:")
|
|
675
|
-
for v in self.vulnerabilities[:10]:
|
|
676
|
-
print(f" [{v['severity']}] {v['type']}: {v.get('endpoint', 'N/A')}")
|
|
677
|
-
|
|
678
|
-
def _get_result(self):
|
|
679
|
-
"""获取结果"""
|
|
680
|
-
return {
|
|
681
|
-
'target': self.target,
|
|
682
|
-
'site_type': self.site_type,
|
|
683
|
-
'endpoints': {
|
|
684
|
-
'static': len(self.static_endpoints),
|
|
685
|
-
'dynamic': len(self.dynamic_endpoints),
|
|
686
|
-
'hooked': len(self.hooked_endpoints),
|
|
687
|
-
'merged': len(self._merge_all_endpoints()),
|
|
688
|
-
},
|
|
689
|
-
'api_prefix': self.api_prefix,
|
|
690
|
-
'vulnerabilities': self.vulnerabilities,
|
|
691
|
-
'cloud_findings': self.cloud_findings,
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
if __name__ == "__main__":
|
|
696
|
-
import sys
|
|
697
|
-
|
|
698
|
-
target = sys.argv[1] if len(sys.argv) > 1 else "http://49.65.100.160:6004/"
|
|
699
|
-
|
|
700
|
-
executor = SKILLExecutorV3(target)
|
|
701
|
-
result = executor.run()
|
|
702
|
-
|
|
703
|
-
print("\n\n结果:")
|
|
704
|
-
print(result)
|