opencode-api-security-testing 2.1.0 → 2.1.2
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/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/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,525 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
API 参数 Hook 模块
|
|
5
|
+
|
|
6
|
+
使用 Playwright 在浏览器中 Hook API 调用,获取真实参数:
|
|
7
|
+
1. Hook XMLHttpRequest / Fetch / axios
|
|
8
|
+
2. 记录每次 API 调用的 URL、方法、参数
|
|
9
|
+
3. 分析参数语义(如 password、username 等)
|
|
10
|
+
4. 识别潜在的安全测试点
|
|
11
|
+
|
|
12
|
+
使用方式:
|
|
13
|
+
from core.api_interceptor import APIInterceptor
|
|
14
|
+
|
|
15
|
+
interceptor = APIInterceptor('http://target.com')
|
|
16
|
+
results = interceptor.hook_all_apis()
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import re
|
|
20
|
+
import sys
|
|
21
|
+
from typing import Dict, List, Set, Optional, Any
|
|
22
|
+
from dataclasses import dataclass, field
|
|
23
|
+
from datetime import datetime
|
|
24
|
+
|
|
25
|
+
sys.path.insert(0, '/workspace/skill-play/API-Security-Testing-Optimized')
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass
|
|
29
|
+
class HookedAPI:
|
|
30
|
+
"""Hook 到的 API 调用"""
|
|
31
|
+
url: str
|
|
32
|
+
method: str
|
|
33
|
+
params: Dict[str, Any] = field(default_factory=dict)
|
|
34
|
+
body: Any = None
|
|
35
|
+
function_name: str = ""
|
|
36
|
+
stack_trace: str = ""
|
|
37
|
+
timestamp: float = 0
|
|
38
|
+
|
|
39
|
+
# 分析结果
|
|
40
|
+
semantic_type: str = "" # login, reset_password, user_query, etc.
|
|
41
|
+
is_sensitive: bool = False
|
|
42
|
+
test_vectors: List[Dict] = field(default_factory=list)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class APIInterceptor:
|
|
46
|
+
"""
|
|
47
|
+
API 参数 Hook 器
|
|
48
|
+
|
|
49
|
+
在浏览器中注入 JavaScript 代码来 Hook 所有 API 调用
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
def __init__(self, target: str, headless: bool = True):
|
|
53
|
+
self.target = target
|
|
54
|
+
self.headless = headless
|
|
55
|
+
self.hooked_apis: List[HookedAPI] = []
|
|
56
|
+
self._page = None
|
|
57
|
+
|
|
58
|
+
# 敏感操作模式
|
|
59
|
+
self.sensitive_patterns = {
|
|
60
|
+
'password': ['password', 'passwd', 'pwd', 'secret'],
|
|
61
|
+
'auth': ['login', 'logout', 'auth', 'token', 'credential'],
|
|
62
|
+
'user': ['user', 'profile', 'account', 'avatar'],
|
|
63
|
+
'admin': ['admin', 'manage', 'system'],
|
|
64
|
+
'reset': ['reset', 'forgot', 'recovery'],
|
|
65
|
+
'delete': ['delete', 'remove', 'destroy'],
|
|
66
|
+
'payment': ['pay', 'order', 'transaction', 'money'],
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
# 可测试的操作模式
|
|
70
|
+
self.testable_patterns = [
|
|
71
|
+
('reset_password', 'password reset'),
|
|
72
|
+
('forgot_password', 'password recovery'),
|
|
73
|
+
('change_password', 'password change'),
|
|
74
|
+
('delete_account', 'account deletion'),
|
|
75
|
+
('modify_user', 'user modification'),
|
|
76
|
+
('admin_user', 'admin user management'),
|
|
77
|
+
]
|
|
78
|
+
|
|
79
|
+
def hook_all_apis(self, interactions: List[str] = None) -> Dict:
|
|
80
|
+
"""
|
|
81
|
+
Hook 所有 API 调用
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
interactions: 要执行的交互列表
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
{
|
|
88
|
+
'total': 100,
|
|
89
|
+
'apis': [...],
|
|
90
|
+
'sensitive': [...],
|
|
91
|
+
'testable': [...],
|
|
92
|
+
'test_vectors': [...]
|
|
93
|
+
}
|
|
94
|
+
"""
|
|
95
|
+
print(f" [API Hook] 启动 Hook 器")
|
|
96
|
+
print(f" [API Hook] 目标: {self.target}")
|
|
97
|
+
|
|
98
|
+
results = {'total': 0, 'apis': [], 'sensitive': [], 'testable': [], 'test_vectors': []}
|
|
99
|
+
|
|
100
|
+
try:
|
|
101
|
+
from playwright.sync_api import sync_playwright
|
|
102
|
+
|
|
103
|
+
with sync_playwright() as p:
|
|
104
|
+
browser = p.chromium.launch(headless=self.headless)
|
|
105
|
+
context = browser.new_context(
|
|
106
|
+
viewport={'width': 1920, 'height': 1080},
|
|
107
|
+
ignore_https_errors=True
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
hook_script = """
|
|
111
|
+
(function() {
|
|
112
|
+
window.__API_CALLS__ = [];
|
|
113
|
+
var origFetch = window.fetch;
|
|
114
|
+
window.fetch = function() {
|
|
115
|
+
var args = arguments;
|
|
116
|
+
var url = args[0] || '';
|
|
117
|
+
var options = args[1] || {};
|
|
118
|
+
window.__API_CALLS__.push({
|
|
119
|
+
type: 'fetch',
|
|
120
|
+
method: (options.method || 'GET').toUpperCase(),
|
|
121
|
+
url: url
|
|
122
|
+
});
|
|
123
|
+
return origFetch.apply(window, args);
|
|
124
|
+
};
|
|
125
|
+
var OrigXHR = window.XMLHttpRequest;
|
|
126
|
+
window.XMLHttpRequest = function() {
|
|
127
|
+
var xhr = new OrigXHR();
|
|
128
|
+
xhr.open = function() {
|
|
129
|
+
this.__method = arguments[0];
|
|
130
|
+
this.__url = arguments[1];
|
|
131
|
+
return OrigXHR.prototype.open.apply(this, arguments);
|
|
132
|
+
};
|
|
133
|
+
xhr.send = function() {
|
|
134
|
+
this.__data = arguments[0];
|
|
135
|
+
var self = this;
|
|
136
|
+
this.addEventListener('load', function() {
|
|
137
|
+
window.__API_CALLS__.push({
|
|
138
|
+
type: 'xhr',
|
|
139
|
+
method: self.__method || 'GET',
|
|
140
|
+
url: self.__url || '',
|
|
141
|
+
data: self.__data || ''
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
return OrigXHR.prototype.send.apply(this, arguments);
|
|
145
|
+
};
|
|
146
|
+
return xhr;
|
|
147
|
+
};
|
|
148
|
+
console.log('[Hook] API Hook 已启动');
|
|
149
|
+
})();
|
|
150
|
+
"""
|
|
151
|
+
|
|
152
|
+
context.add_init_script(hook_script)
|
|
153
|
+
|
|
154
|
+
page = context.new_page()
|
|
155
|
+
self._page = page
|
|
156
|
+
|
|
157
|
+
print(f" [API Hook] 访问目标...")
|
|
158
|
+
page.goto(self.target, wait_until='networkidle', timeout=30000)
|
|
159
|
+
page.wait_for_timeout(3000)
|
|
160
|
+
|
|
161
|
+
self._execute_interactions()
|
|
162
|
+
|
|
163
|
+
results = self._process_results()
|
|
164
|
+
|
|
165
|
+
browser.close()
|
|
166
|
+
|
|
167
|
+
except Exception as e:
|
|
168
|
+
print(f" [API Hook] 失败: {e}")
|
|
169
|
+
|
|
170
|
+
print(f" [API Hook] 捕获 {results['total']} 个 API 调用")
|
|
171
|
+
print(f" [API Hook] 发现 {len(results.get('sensitive', []))} 个敏感操作")
|
|
172
|
+
|
|
173
|
+
return results
|
|
174
|
+
|
|
175
|
+
def _inject_hook_code(self):
|
|
176
|
+
"""注入 Hook 代码到页面"""
|
|
177
|
+
|
|
178
|
+
# 简化的 Hook 代码
|
|
179
|
+
hook_code = """
|
|
180
|
+
(function() {
|
|
181
|
+
if (window.__HOOKED__) return;
|
|
182
|
+
window.__HOOKED__ = true;
|
|
183
|
+
window.__API_CALLS__ = [];
|
|
184
|
+
|
|
185
|
+
// Hook fetch
|
|
186
|
+
var origFetch = window.fetch;
|
|
187
|
+
window.fetch = function() {
|
|
188
|
+
var args = arguments;
|
|
189
|
+
var url = args[0] || '';
|
|
190
|
+
var options = args[1] || {};
|
|
191
|
+
|
|
192
|
+
window.__API_CALLS__.push({
|
|
193
|
+
type: 'fetch',
|
|
194
|
+
method: (options.method || 'GET').toUpperCase(),
|
|
195
|
+
url: url,
|
|
196
|
+
timestamp: Date.now()
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
return origFetch.apply(this, args);
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
// Hook XMLHttpRequest
|
|
203
|
+
var origXHR = window.XMLHttpRequest;
|
|
204
|
+
window.XMLHttpRequest = function() {
|
|
205
|
+
var xhr = new origXHR();
|
|
206
|
+
var origOpen = xhr.open;
|
|
207
|
+
var origSend = xhr.send;
|
|
208
|
+
|
|
209
|
+
xhr.open = function() {
|
|
210
|
+
this.__method = arguments[0];
|
|
211
|
+
this.__url = arguments[1];
|
|
212
|
+
return origOpen.apply(this, arguments);
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
xhr.send = function() {
|
|
216
|
+
this.__data = arguments[0];
|
|
217
|
+
var self = this;
|
|
218
|
+
this.addEventListener('load', function() {
|
|
219
|
+
window.__API_CALLS__.push({
|
|
220
|
+
type: 'xhr',
|
|
221
|
+
method: self.__method || 'GET',
|
|
222
|
+
url: self.__url || '',
|
|
223
|
+
data: self.__data || '',
|
|
224
|
+
timestamp: Date.now()
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
return origSend.apply(this, arguments);
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
return xhr;
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
console.log('[Hook] API Hook 已启动');
|
|
234
|
+
})();
|
|
235
|
+
"""
|
|
236
|
+
|
|
237
|
+
self._page.evaluate(hook_code)
|
|
238
|
+
|
|
239
|
+
def _execute_interactions(self):
|
|
240
|
+
"""执行交互操作触发 API 调用"""
|
|
241
|
+
print(f" [API Hook] 执行交互...")
|
|
242
|
+
|
|
243
|
+
# 1. 填写表单
|
|
244
|
+
try:
|
|
245
|
+
inputs = self._page.query_selector_all('input')
|
|
246
|
+
for i, inp in enumerate(inputs[:10]):
|
|
247
|
+
try:
|
|
248
|
+
inp.fill(f'test_value_{i}')
|
|
249
|
+
self._page.wait_for_timeout(300)
|
|
250
|
+
except:
|
|
251
|
+
pass
|
|
252
|
+
except:
|
|
253
|
+
pass
|
|
254
|
+
|
|
255
|
+
# 2. 点击按钮
|
|
256
|
+
try:
|
|
257
|
+
buttons = self._page.query_selector_all('button')
|
|
258
|
+
for btn in buttons[:10]:
|
|
259
|
+
try:
|
|
260
|
+
btn.click()
|
|
261
|
+
self._page.wait_for_timeout(500)
|
|
262
|
+
except:
|
|
263
|
+
pass
|
|
264
|
+
except:
|
|
265
|
+
pass
|
|
266
|
+
|
|
267
|
+
# 3. 导航
|
|
268
|
+
routes = ['#/login', '#/home', '#/admin', '#/profile']
|
|
269
|
+
for route in routes:
|
|
270
|
+
try:
|
|
271
|
+
self._page.goto(self.target + route, timeout=10000)
|
|
272
|
+
self._page.wait_for_timeout(2000)
|
|
273
|
+
except:
|
|
274
|
+
pass
|
|
275
|
+
|
|
276
|
+
def _process_results(self) -> Dict:
|
|
277
|
+
"""处理 Hook 结果"""
|
|
278
|
+
|
|
279
|
+
# 获取 Hook 到的调用
|
|
280
|
+
raw_calls = self._page.evaluate('window.__API_CALLS__ || []') if self._page else []
|
|
281
|
+
|
|
282
|
+
apis = []
|
|
283
|
+
sensitive = []
|
|
284
|
+
testable = []
|
|
285
|
+
test_vectors = []
|
|
286
|
+
|
|
287
|
+
for call in raw_calls:
|
|
288
|
+
url = call.get('url', '')
|
|
289
|
+
method = call.get('method', 'GET').upper()
|
|
290
|
+
data = call.get('data', {})
|
|
291
|
+
params = call.get('params', {})
|
|
292
|
+
|
|
293
|
+
# 只处理目标域名的 API
|
|
294
|
+
target_host = self.target.replace('http://', '').replace('https://', '').split('/')[0]
|
|
295
|
+
if target_host not in url:
|
|
296
|
+
continue
|
|
297
|
+
|
|
298
|
+
# 提取路径
|
|
299
|
+
path = url.replace(f'http://{target_host}', '').split('?')[0]
|
|
300
|
+
|
|
301
|
+
# 解析参数
|
|
302
|
+
parsed_params = self._parse_params(data, params)
|
|
303
|
+
|
|
304
|
+
# 创建 HookedAPI 对象
|
|
305
|
+
api = HookedAPI(
|
|
306
|
+
url=url,
|
|
307
|
+
method=method,
|
|
308
|
+
params=parsed_params,
|
|
309
|
+
body=data,
|
|
310
|
+
semantic_type=self._infer_semantic_type(path, method)
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
# 检测敏感操作
|
|
314
|
+
if self._is_sensitive_operation(path, method):
|
|
315
|
+
api.is_sensitive = True
|
|
316
|
+
sensitive.append(api)
|
|
317
|
+
|
|
318
|
+
# 生成测试向量
|
|
319
|
+
vectors = self._generate_test_vectors(api)
|
|
320
|
+
api.test_vectors = vectors
|
|
321
|
+
test_vectors.extend(vectors)
|
|
322
|
+
|
|
323
|
+
# 检测可测试操作
|
|
324
|
+
if self._is_testable_operation(path):
|
|
325
|
+
testable.append(api)
|
|
326
|
+
|
|
327
|
+
apis.append(api)
|
|
328
|
+
|
|
329
|
+
return {
|
|
330
|
+
'total': len(apis),
|
|
331
|
+
'apis': [a.url for a in apis],
|
|
332
|
+
'sensitive': [{'url': a.url, 'method': a.method, 'semantic': a.semantic_type} for a in sensitive],
|
|
333
|
+
'testable': [{'url': a.url, 'method': a.method, 'semantic': a.semantic_type} for a in testable],
|
|
334
|
+
'test_vectors': test_vectors,
|
|
335
|
+
'hooked_apis': apis,
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
def _parse_params(self, data: Any, params: Any) -> Dict:
|
|
339
|
+
"""解析参数"""
|
|
340
|
+
result = {}
|
|
341
|
+
|
|
342
|
+
if isinstance(data, dict):
|
|
343
|
+
result.update(data)
|
|
344
|
+
elif isinstance(data, str):
|
|
345
|
+
try:
|
|
346
|
+
result = json.loads(data) if data else {}
|
|
347
|
+
except:
|
|
348
|
+
# URL encoded
|
|
349
|
+
for pair in data.split('&'):
|
|
350
|
+
if '=' in pair:
|
|
351
|
+
k, v = pair.split('=', 1)
|
|
352
|
+
result[k] = v
|
|
353
|
+
|
|
354
|
+
if isinstance(params, dict):
|
|
355
|
+
result.update(params)
|
|
356
|
+
|
|
357
|
+
return result
|
|
358
|
+
|
|
359
|
+
def _infer_semantic_type(self, path: str, method: str) -> str:
|
|
360
|
+
"""推断语义类型"""
|
|
361
|
+
path_lower = path.lower()
|
|
362
|
+
|
|
363
|
+
if 'login' in path_lower or 'signin' in path_lower:
|
|
364
|
+
return 'login'
|
|
365
|
+
if 'reset' in path_lower and 'pass' in path_lower:
|
|
366
|
+
return 'reset_password'
|
|
367
|
+
if 'forgot' in path_lower or 'recovery' in path_lower:
|
|
368
|
+
return 'forgot_password'
|
|
369
|
+
if 'logout' in path_lower:
|
|
370
|
+
return 'logout'
|
|
371
|
+
if 'profile' in path_lower:
|
|
372
|
+
return 'user_profile'
|
|
373
|
+
if 'avatar' in path_lower:
|
|
374
|
+
return 'avatar_upload'
|
|
375
|
+
if 'user' in path_lower and ('add' in path_lower or 'create' in path_lower):
|
|
376
|
+
return 'create_user'
|
|
377
|
+
if 'user' in path_lower and ('delete' in path_lower or 'remove' in path_lower):
|
|
378
|
+
return 'delete_user'
|
|
379
|
+
if 'user' in path_lower and ('modify' in path_lower or 'update' in path_lower or 'edit' in path_lower):
|
|
380
|
+
return 'modify_user'
|
|
381
|
+
if 'password' in path_lower or 'passwd' in path_lower:
|
|
382
|
+
return 'password_operation'
|
|
383
|
+
if 'admin' in path_lower:
|
|
384
|
+
return 'admin_operation'
|
|
385
|
+
|
|
386
|
+
return 'general'
|
|
387
|
+
|
|
388
|
+
def _is_sensitive_operation(self, path: str, method: str) -> bool:
|
|
389
|
+
"""判断是否为敏感操作"""
|
|
390
|
+
path_lower = path.lower()
|
|
391
|
+
|
|
392
|
+
sensitive_keywords = [
|
|
393
|
+
'password', 'passwd', 'pwd',
|
|
394
|
+
'login', 'logout', 'auth',
|
|
395
|
+
'reset', 'forgot', 'recovery',
|
|
396
|
+
'credit', 'card', 'payment', 'money',
|
|
397
|
+
'private', 'secret', 'key',
|
|
398
|
+
]
|
|
399
|
+
|
|
400
|
+
for keyword in sensitive_keywords:
|
|
401
|
+
if keyword in path_lower:
|
|
402
|
+
return True
|
|
403
|
+
|
|
404
|
+
# 危险方法
|
|
405
|
+
if method in ['POST', 'PUT', 'DELETE'] and any(k in path_lower for k in ['user', 'admin', 'password', 'auth']):
|
|
406
|
+
return True
|
|
407
|
+
|
|
408
|
+
return False
|
|
409
|
+
|
|
410
|
+
def _is_testable_operation(self, path: str) -> bool:
|
|
411
|
+
"""判断是否可测试"""
|
|
412
|
+
path_lower = path.lower()
|
|
413
|
+
|
|
414
|
+
testable_keywords = [
|
|
415
|
+
'reset', 'forgot', 'recovery',
|
|
416
|
+
'password', 'passwd',
|
|
417
|
+
'delete', 'remove',
|
|
418
|
+
'admin', 'manage',
|
|
419
|
+
'user', 'profile',
|
|
420
|
+
'modify', 'update', 'edit',
|
|
421
|
+
'upload', 'download',
|
|
422
|
+
]
|
|
423
|
+
|
|
424
|
+
return any(keyword in path_lower for keyword in testable_keywords)
|
|
425
|
+
|
|
426
|
+
def _generate_test_vectors(self, api: HookedAPI) -> List[Dict]:
|
|
427
|
+
"""生成测试向量"""
|
|
428
|
+
vectors = []
|
|
429
|
+
path = api.url.lower()
|
|
430
|
+
method = api.method.upper()
|
|
431
|
+
params = api.params
|
|
432
|
+
|
|
433
|
+
# 1. 未授权访问测试
|
|
434
|
+
if method in ['GET', 'POST', 'PUT', 'DELETE']:
|
|
435
|
+
vectors.append({
|
|
436
|
+
'type': 'unauthorized_access',
|
|
437
|
+
'url': api.url,
|
|
438
|
+
'method': method,
|
|
439
|
+
'description': f'测试未授权访问 {path}',
|
|
440
|
+
'expected': '应该返回 401/403 或需要认证',
|
|
441
|
+
})
|
|
442
|
+
|
|
443
|
+
# 2. 参数篡改测试
|
|
444
|
+
if params:
|
|
445
|
+
for param_name in list(params.keys())[:3]:
|
|
446
|
+
vectors.append({
|
|
447
|
+
'type': 'parameter_tampering',
|
|
448
|
+
'url': api.url,
|
|
449
|
+
'method': method,
|
|
450
|
+
'param': param_name,
|
|
451
|
+
'description': f'篡改参数 {param_name}',
|
|
452
|
+
'test_value': 'admin\' OR \'1\'=\'1',
|
|
453
|
+
})
|
|
454
|
+
|
|
455
|
+
# 3. 密码重置相关测试
|
|
456
|
+
if 'reset' in path and 'password' in path:
|
|
457
|
+
vectors.append({
|
|
458
|
+
'type': 'password_reset_bypass',
|
|
459
|
+
'url': api.url,
|
|
460
|
+
'method': method,
|
|
461
|
+
'description': '测试任意密码重置',
|
|
462
|
+
'test_vectors': [
|
|
463
|
+
{'user_id': '1', 'new_password': 'hacked123'},
|
|
464
|
+
{'username': 'admin', 'token': 'fake_token'},
|
|
465
|
+
{'email': 'admin@test.com'},
|
|
466
|
+
]
|
|
467
|
+
})
|
|
468
|
+
|
|
469
|
+
# 4. 用户操作测试
|
|
470
|
+
if 'user' in path or 'admin' in path:
|
|
471
|
+
if method == 'DELETE':
|
|
472
|
+
vectors.append({
|
|
473
|
+
'type': 'idor',
|
|
474
|
+
'url': api.url,
|
|
475
|
+
'method': method,
|
|
476
|
+
'description': '测试 IDOR - 任意用户删除',
|
|
477
|
+
'test_ids': [1, 2, 3, 'admin'],
|
|
478
|
+
})
|
|
479
|
+
elif method == 'PUT' or 'post' in method.lower():
|
|
480
|
+
vectors.append({
|
|
481
|
+
'type': 'privilege_escalation',
|
|
482
|
+
'url': api.url,
|
|
483
|
+
'method': method,
|
|
484
|
+
'description': '测试权限提升',
|
|
485
|
+
'test_role': 'admin',
|
|
486
|
+
})
|
|
487
|
+
|
|
488
|
+
return vectors
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
def run_api_hook(target: str) -> Dict:
|
|
492
|
+
"""运行 API Hook"""
|
|
493
|
+
print("=" * 60)
|
|
494
|
+
print("API Hook - 参数提取与安全测试")
|
|
495
|
+
print("=" * 60)
|
|
496
|
+
|
|
497
|
+
interceptor = APIInterceptor(target)
|
|
498
|
+
results = interceptor.hook_all_apis()
|
|
499
|
+
|
|
500
|
+
print("\n" + "=" * 60)
|
|
501
|
+
print("Hook 结果")
|
|
502
|
+
print("=" * 60)
|
|
503
|
+
print(f"总 API 调用: {results['total']}")
|
|
504
|
+
print(f"敏感操作: {len(results['sensitive'])}")
|
|
505
|
+
print(f"可测试操作: {len(results['testable'])}")
|
|
506
|
+
print(f"测试向量: {len(results['test_vectors'])}")
|
|
507
|
+
|
|
508
|
+
if results['sensitive']:
|
|
509
|
+
print("\n敏感操作:")
|
|
510
|
+
for s in results['sensitive'][:10]:
|
|
511
|
+
print(f" [{s['method']}] {s['url']} ({s['semantic']})")
|
|
512
|
+
|
|
513
|
+
if results['test_vectors']:
|
|
514
|
+
print("\n测试向量示例:")
|
|
515
|
+
for tv in results['test_vectors'][:5]:
|
|
516
|
+
print(f" [{tv['type']}] {tv['method']} {tv['url']}")
|
|
517
|
+
print(f" {tv['description']}")
|
|
518
|
+
|
|
519
|
+
return results
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
if __name__ == "__main__":
|
|
523
|
+
import sys
|
|
524
|
+
target = sys.argv[1] if len(sys.argv) > 1 else "http://58.215.18.57:91"
|
|
525
|
+
run_api_hook(target)
|