opencode-api-security-testing 2.1.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.
Files changed (61) hide show
  1. package/SKILL.md +1797 -0
  2. package/core/advanced_recon.py +788 -0
  3. package/core/agentic_analyzer.py +445 -0
  4. package/core/analyzers/api_parser.py +210 -0
  5. package/core/analyzers/response_analyzer.py +212 -0
  6. package/core/analyzers/sensitive_finder.py +184 -0
  7. package/core/api_fuzzer.py +422 -0
  8. package/core/api_interceptor.py +525 -0
  9. package/core/api_parser.py +955 -0
  10. package/core/browser_tester.py +479 -0
  11. package/core/cloud_storage_tester.py +1330 -0
  12. package/core/collectors/__init__.py +23 -0
  13. package/core/collectors/api_path_finder.py +300 -0
  14. package/core/collectors/browser_collect.py +645 -0
  15. package/core/collectors/browser_collector.py +411 -0
  16. package/core/collectors/http_client.py +111 -0
  17. package/core/collectors/js_collector.py +490 -0
  18. package/core/collectors/js_parser.py +780 -0
  19. package/core/collectors/url_collector.py +319 -0
  20. package/core/context_manager.py +682 -0
  21. package/core/deep_api_tester_v35.py +844 -0
  22. package/core/deep_api_tester_v55.py +366 -0
  23. package/core/dynamic_api_analyzer.py +532 -0
  24. package/core/http_client.py +179 -0
  25. package/core/models.py +296 -0
  26. package/core/orchestrator.py +890 -0
  27. package/core/prerequisite.py +227 -0
  28. package/core/reasoning_engine.py +1042 -0
  29. package/core/response_classifier.py +606 -0
  30. package/core/runner.py +938 -0
  31. package/core/scan_engine.py +599 -0
  32. package/core/skill_executor.py +435 -0
  33. package/core/skill_executor_v2.py +670 -0
  34. package/core/skill_executor_v3.py +704 -0
  35. package/core/smart_analyzer.py +687 -0
  36. package/core/strategy_pool.py +707 -0
  37. package/core/testers/auth_tester.py +264 -0
  38. package/core/testers/idor_tester.py +200 -0
  39. package/core/testers/sqli_tester.py +211 -0
  40. package/core/testing_loop.py +655 -0
  41. package/core/utils/base_path_dict.py +255 -0
  42. package/core/utils/payload_lib.py +167 -0
  43. package/core/utils/ssrf_detector.py +220 -0
  44. package/core/verifiers/vuln_verifier.py +536 -0
  45. package/package.json +17 -13
  46. package/references/asset-discovery.md +119 -612
  47. package/references/graphql-guidance.md +65 -641
  48. package/references/intake.md +84 -0
  49. package/references/report-template.md +131 -38
  50. package/references/rest-guidance.md +55 -526
  51. package/references/severity-model.md +52 -264
  52. package/references/test-matrix.md +65 -263
  53. package/references/validation.md +53 -400
  54. package/scripts/postinstall.js +46 -0
  55. package/agents/cyber-supervisor.md +0 -55
  56. package/agents/probing-miner.md +0 -42
  57. package/agents/resource-specialist.md +0 -31
  58. package/commands/api-security-testing-scan.md +0 -59
  59. package/commands/api-security-testing-test.md +0 -49
  60. package/commands/api-security-testing.md +0 -72
  61. package/tsconfig.json +0 -17
@@ -0,0 +1,704 @@
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)