opencode-api-security-testing 5.0.0 → 5.2.0
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 +6 -0
- package/agents/api-cyber-supervisor.md +5 -3
- package/package.json +1 -1
- package/postinstall.mjs +181 -6
- package/src/index.ts +165 -0
- package/src/tools/endpoint-discover.ts +325 -0
- package/src/tools/report-generator.ts +355 -0
- package/src/utils/env-checker.ts +264 -0
- package/references/references/README.md +0 -72
- package/references/references/asset-discovery.md +0 -119
- package/references/references/fuzzing-patterns.md +0 -129
- package/references/references/graphql-guidance.md +0 -108
- package/references/references/intake.md +0 -84
- package/references/references/pua-agent.md +0 -192
- package/references/references/report-template.md +0 -156
- package/references/references/rest-guidance.md +0 -76
- package/references/references/severity-model.md +0 -76
- package/references/references/test-matrix.md +0 -86
- package/references/references/validation.md +0 -78
- package/references/references/vulnerabilities/01-sqli-tests.md +0 -1128
- package/references/references/vulnerabilities/02-user-enum-tests.md +0 -423
- package/references/references/vulnerabilities/03-jwt-tests.md +0 -499
- package/references/references/vulnerabilities/04-idor-tests.md +0 -362
- package/references/references/vulnerabilities/05-sensitive-data-tests.md +0 -466
- package/references/references/vulnerabilities/06-biz-logic-tests.md +0 -501
- package/references/references/vulnerabilities/07-security-config-tests.md +0 -511
- package/references/references/vulnerabilities/08-brute-force-tests.md +0 -457
- package/references/references/vulnerabilities/09-vulnerability-chains.md +0 -465
- package/references/references/vulnerabilities/10-auth-tests.md +0 -537
- package/references/references/vulnerabilities/11-graphql-tests.md +0 -355
- package/references/references/vulnerabilities/12-ssrf-tests.md +0 -396
- package/references/references/vulnerabilities/README.md +0 -148
- package/references/references/workflows.md +0 -192
- package/src/src/index.ts +0 -535
|
@@ -1,423 +0,0 @@
|
|
|
1
|
-
# 用户枚举测试
|
|
2
|
-
|
|
3
|
-
## 1. 概述
|
|
4
|
-
|
|
5
|
-
用户枚举(User Enumeration)是指通过某些接口可以判断用户名、手机号、邮箱等是否存在于系统中。
|
|
6
|
-
|
|
7
|
-
**危险等级**: 中
|
|
8
|
-
|
|
9
|
-
## 2. 测试点识别
|
|
10
|
-
|
|
11
|
-
### 2.1 常见枚举接口
|
|
12
|
-
|
|
13
|
-
| 接口模式 | 示例 |
|
|
14
|
-
|----------|------|
|
|
15
|
-
| 用户查重 | `/user/checkOnlyUser?username=X` |
|
|
16
|
-
| 用户存在 | `/user/exist?phone=X` |
|
|
17
|
-
| 用户验证 | `/user/validate?email=X` |
|
|
18
|
-
| 注册接口 | `/user/register` |
|
|
19
|
-
| 登录接口 | `/login` (响应差异) |
|
|
20
|
-
| 密码重置 | `/user/resetPassword` |
|
|
21
|
-
|
|
22
|
-
### 2.2 响应差异点
|
|
23
|
-
|
|
24
|
-
| 特征 | 说明 |
|
|
25
|
-
|------|------|
|
|
26
|
-
| 不同错误消息 | "用户不存在" vs "密码错误" |
|
|
27
|
-
| 响应码差异 | code=0 vs code=1 |
|
|
28
|
-
| 响应时间差异 | 存在用户 vs 不存在 |
|
|
29
|
-
|
|
30
|
-
## 3. 测试方法
|
|
31
|
-
|
|
32
|
-
### 3.1 用户名枚举
|
|
33
|
-
|
|
34
|
-
```bash
|
|
35
|
-
# 用户存在
|
|
36
|
-
curl "http://api/sys/user/checkOnlyUser?username=admin"
|
|
37
|
-
# 响应: {"success":false,"message":"用户账号已存在","code":0,"result":true}
|
|
38
|
-
|
|
39
|
-
# 用户不存在
|
|
40
|
-
curl "http://api/sys/user/checkOnlyUser?username=notexist"
|
|
41
|
-
# 响应: {"success":false,"message":"","code":0,"result":false}
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
### 3.2 手机号枚举
|
|
45
|
-
|
|
46
|
-
```bash
|
|
47
|
-
curl "http://api/user/check?phone=13800138000"
|
|
48
|
-
curl "http://api/user/existByPhone?phone=13800138000"
|
|
49
|
-
curl "http://api/sms/send?phone=13800138000"
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
### 3.3 邮箱枚举
|
|
53
|
-
|
|
54
|
-
```bash
|
|
55
|
-
curl "http://api/user/check?email=test@mail.com"
|
|
56
|
-
curl "http://api/user/validateEmail?email=test@mail.com"
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
### 3.4 登录接口响应差异
|
|
60
|
-
|
|
61
|
-
```bash
|
|
62
|
-
# 用户不存在
|
|
63
|
-
POST /api/login
|
|
64
|
-
{"username": "notexist", "password": "any"}
|
|
65
|
-
# 响应: {"message":"用户不存在"}
|
|
66
|
-
|
|
67
|
-
# 密码错误
|
|
68
|
-
POST /api/login
|
|
69
|
-
{"username": "admin", "password": "wrong"}
|
|
70
|
-
# 响应: {"message":"密码错误"}
|
|
71
|
-
|
|
72
|
-
# 用户存在但响应相同
|
|
73
|
-
POST /api/login
|
|
74
|
-
{"username": "notexist", "password": "wrong"}
|
|
75
|
-
# 响应: {"message":"用户名或密码错误"} ← 统一消息(安全)
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
### 3.5 注册接口探测
|
|
79
|
-
|
|
80
|
-
```bash
|
|
81
|
-
POST /api/user/register
|
|
82
|
-
{"username": "admin", "password": "test"}
|
|
83
|
-
# 响应: {"message":"该用户已注册"}
|
|
84
|
-
|
|
85
|
-
POST /api/user/register
|
|
86
|
-
{"username": "newuser", "password": "test"}
|
|
87
|
-
# 响应: {"message":"注册成功"}
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
## 4. 枚举用户名字典
|
|
91
|
-
|
|
92
|
-
### 4.1 常见管理员账号
|
|
93
|
-
|
|
94
|
-
```
|
|
95
|
-
admin, administrator, root, user, manager
|
|
96
|
-
sysadmin, system, security, backup
|
|
97
|
-
admin1, admin123, admin888
|
|
98
|
-
root1, root123
|
|
99
|
-
administrator, adminitrator (拼写错误)
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
### 4.2 常见测试账号
|
|
103
|
-
|
|
104
|
-
```
|
|
105
|
-
test, test1, test123, tester
|
|
106
|
-
demo, demo1
|
|
107
|
-
guest, visitor
|
|
108
|
-
default
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
### 4.3 业务相关账号
|
|
112
|
-
|
|
113
|
-
```
|
|
114
|
-
owner, operator, developer
|
|
115
|
-
support, helpdesk
|
|
116
|
-
noreply, no-reply
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
## 5. 防护绕过
|
|
120
|
-
|
|
121
|
-
### 5.1 大小写混淆
|
|
122
|
-
|
|
123
|
-
```bash
|
|
124
|
-
username=Admin
|
|
125
|
-
username=ADMIN
|
|
126
|
-
username=AdMiN
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
### 5.2 特殊字符
|
|
130
|
-
|
|
131
|
-
```bash
|
|
132
|
-
username=admin
|
|
133
|
-
username=admin\
|
|
134
|
-
username=admin"
|
|
135
|
-
username=admin<>
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
### 5.3 时间差异规避
|
|
139
|
-
|
|
140
|
-
```bash
|
|
141
|
-
# 多次请求间增加延迟
|
|
142
|
-
sleep 1
|
|
143
|
-
curl "http://api/user/check?username=admin"
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
## 6. 关联漏洞
|
|
147
|
-
|
|
148
|
-
| 后续漏洞 | 利用路径 |
|
|
149
|
-
|----------|----------|
|
|
150
|
-
| 暴力破解 | 确认用户名 → 定向爆破密码 |
|
|
151
|
-
| 密码重置 | 利用已确认的用户 → 重置密码 |
|
|
152
|
-
| 社会工程学 | 利用确认的手机号/邮箱 → 钓鱼攻击 |
|
|
153
|
-
|
|
154
|
-
## 7. 测试检查清单
|
|
155
|
-
|
|
156
|
-
```
|
|
157
|
-
□ 测试用户查重接口(/user/checkOnlyUser)
|
|
158
|
-
□ 测试手机号查重接口
|
|
159
|
-
□ 测试邮箱查重接口
|
|
160
|
-
□ 测试登录接口响应差异
|
|
161
|
-
□ 测试注册接口探测
|
|
162
|
-
□ 枚举常见管理员账号
|
|
163
|
-
□ 枚举常见测试账号
|
|
164
|
-
□ 测试大小写绕过
|
|
165
|
-
□ 测试特殊字符绕过
|
|
166
|
-
□ 判断响应差异是否可利用
|
|
167
|
-
□ 评估漏洞风险和影响
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
## 8. 误报判断标准
|
|
171
|
-
|
|
172
|
-
### 8.1 核心判断原则
|
|
173
|
-
|
|
174
|
-
```
|
|
175
|
-
【重要】响应差异 ≠ 用户枚举漏洞!
|
|
176
|
-
|
|
177
|
-
判断逻辑:
|
|
178
|
-
1. 先获取正常响应基准
|
|
179
|
-
2. 对比有效用户和无效用户的响应
|
|
180
|
-
3. 差异必须是"明确的用户存在/不存在指示"
|
|
181
|
-
|
|
182
|
-
【真实漏洞特征】
|
|
183
|
-
- 明确区分"用户不存在"和"密码错误"
|
|
184
|
-
- 响应中直接包含"用户已存在"等字样
|
|
185
|
-
- 响应时间有明显差异(数据库查询 vs 直接返回)
|
|
186
|
-
|
|
187
|
-
【误报特征】
|
|
188
|
-
- 有效/无效用户返回相同错误消息
|
|
189
|
-
- 响应差异只是"参数格式校验"失败
|
|
190
|
-
- WAF拦截导致的响应差异
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
### 8.2 curl + 对比验证流程
|
|
194
|
-
|
|
195
|
-
```bash
|
|
196
|
-
# 1. 【必须先执行】获取正常响应基准
|
|
197
|
-
curl -s -X POST http://api/login \
|
|
198
|
-
-H "Content-Type: application/json" \
|
|
199
|
-
-d '{"username":"admin","password":"wrong"}' > enum_baseline.json
|
|
200
|
-
|
|
201
|
-
# 2. 测试可能存在的用户
|
|
202
|
-
curl -s -X POST http://api/login \
|
|
203
|
-
-H "Content-Type: application/json" \
|
|
204
|
-
-d '{"username":"admin","password":"wrong"}' > enum_test1.json
|
|
205
|
-
|
|
206
|
-
# 3. 测试不存在的用户
|
|
207
|
-
curl -s -X POST http://api/login \
|
|
208
|
-
-H "Content-Type: application/json" \
|
|
209
|
-
-d '{"username":"nonexist_abc123","password":"wrong"}' > enum_test2.json
|
|
210
|
-
|
|
211
|
-
# 4. 对比两次响应
|
|
212
|
-
diff enum_baseline.json enum_test1.json
|
|
213
|
-
# 应该完全相同(如果都返回"用户名或密码错误")
|
|
214
|
-
|
|
215
|
-
diff enum_baseline.json enum_test2.json
|
|
216
|
-
# 判断:
|
|
217
|
-
# - 完全相同 → 不是用户枚举漏洞(安全)
|
|
218
|
-
# - 有明显差异 → 可能是用户枚举漏洞
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
### 8.3 多维度判断矩阵
|
|
222
|
-
|
|
223
|
-
| 场景 | 响应A | 响应B | 差异 | 判断 |
|
|
224
|
-
|------|-------|-------|------|------|
|
|
225
|
-
| 正常 | {"msg":"用户名或密码错误"} | {"msg":"用户名或密码错误"} | 无 | ✅ 安全 |
|
|
226
|
-
| 正常 | {"msg":"用户名或密码错误"} | {"msg":"用户不存在"} | 有 | ⚠️ 可能漏洞 |
|
|
227
|
-
| 误报 | {"msg":"参数格式错误"} | {"msg":"用户名或密码错误"} | 有 | ❌ 误报(格式校验) |
|
|
228
|
-
| 误报 | {"msg":"请输入密码"} | {"msg":"密码错误"} | 有 | ❌ 误报(字段缺失) |
|
|
229
|
-
|
|
230
|
-
### 8.4 Python脚本(复杂场景)
|
|
231
|
-
|
|
232
|
-
```python
|
|
233
|
-
import requests
|
|
234
|
-
import json
|
|
235
|
-
|
|
236
|
-
class UserEnumTester:
|
|
237
|
-
def __init__(self, target):
|
|
238
|
-
self.target = target
|
|
239
|
-
self.baseline = None
|
|
240
|
-
|
|
241
|
-
def get_baseline(self):
|
|
242
|
-
"""获取正常响应基准(无效用户)"""
|
|
243
|
-
resp = requests.post(
|
|
244
|
-
f"{self.target}/login",
|
|
245
|
-
json={"username": "nonexist_abc123", "password": "wrong"},
|
|
246
|
-
headers={"Content-Type": "application/json"}
|
|
247
|
-
)
|
|
248
|
-
self.baseline = resp.json()
|
|
249
|
-
return self.baseline
|
|
250
|
-
|
|
251
|
-
def is_user_exists(self, username):
|
|
252
|
-
"""
|
|
253
|
-
测试用户是否存在
|
|
254
|
-
|
|
255
|
-
Returns:
|
|
256
|
-
(exists, reason, response)
|
|
257
|
-
- exists: True/False/Unknown
|
|
258
|
-
- reason: 判断原因
|
|
259
|
-
- response: 完整响应
|
|
260
|
-
"""
|
|
261
|
-
resp = requests.post(
|
|
262
|
-
f"{self.target}/login",
|
|
263
|
-
json={"username": username, "password": "wrong"},
|
|
264
|
-
headers={"Content-Type": "application/json"}
|
|
265
|
-
)
|
|
266
|
-
|
|
267
|
-
if resp.status_code != 200:
|
|
268
|
-
return False, "HTTP错误", resp
|
|
269
|
-
|
|
270
|
-
try:
|
|
271
|
-
data = resp.json()
|
|
272
|
-
except:
|
|
273
|
-
return False, "非JSON响应", resp
|
|
274
|
-
|
|
275
|
-
# 检查响应是否与基准完全相同
|
|
276
|
-
if self.baseline and data == self.baseline:
|
|
277
|
-
return False, "响应与基准相同(可能是安全)", data
|
|
278
|
-
|
|
279
|
-
# 检查是否有明确的用户存在指示
|
|
280
|
-
msg = data.get('msg', '').lower()
|
|
281
|
-
code = str(data.get('code', ''))
|
|
282
|
-
|
|
283
|
-
# 用户不存在的指示
|
|
284
|
-
not_exist_keywords = ['不存在', 'not exist', 'not found', '用户不存在']
|
|
285
|
-
# 用户存在的指示
|
|
286
|
-
exist_keywords = ['已存在', 'exist', '用户已注册', '密码错误', '密码不匹配']
|
|
287
|
-
|
|
288
|
-
for kw in not_exist_keywords:
|
|
289
|
-
if kw in msg:
|
|
290
|
-
return False, f"响应包含'{kw}',用户可能不存在", data
|
|
291
|
-
|
|
292
|
-
for kw in exist_keywords:
|
|
293
|
-
if kw in msg:
|
|
294
|
-
return True, f"响应包含'{kw}',用户可能存在", data
|
|
295
|
-
|
|
296
|
-
# 检查响应码差异
|
|
297
|
-
if self.baseline:
|
|
298
|
-
if data.get('code') != self.baseline.get('code'):
|
|
299
|
-
return None, f"响应码不同(A:{self.baseline.get('code')} B:{data.get('code')})", data
|
|
300
|
-
|
|
301
|
-
return None, "响应有差异但无法判断", data
|
|
302
|
-
|
|
303
|
-
def run_tests(self, usernames):
|
|
304
|
-
"""批量测试用户枚举"""
|
|
305
|
-
print(f"\n=== 用户枚举测试 ===\n")
|
|
306
|
-
|
|
307
|
-
# 获取基准
|
|
308
|
-
self.get_baseline()
|
|
309
|
-
print(f"基准响应: {self.baseline}\n")
|
|
310
|
-
|
|
311
|
-
results = {'exists': [], 'not_exists': [], 'unknown': []}
|
|
312
|
-
|
|
313
|
-
for username in usernames:
|
|
314
|
-
exists, reason, resp = self.is_user_exists(username)
|
|
315
|
-
|
|
316
|
-
if exists is True:
|
|
317
|
-
results['exists'].append({
|
|
318
|
-
'username': username,
|
|
319
|
-
'reason': reason,
|
|
320
|
-
'response': resp
|
|
321
|
-
})
|
|
322
|
-
print(f"[存在] {username}: {reason}")
|
|
323
|
-
elif exists is False:
|
|
324
|
-
results['not_exists'].append({
|
|
325
|
-
'username': username,
|
|
326
|
-
'reason': reason,
|
|
327
|
-
'response': resp
|
|
328
|
-
})
|
|
329
|
-
print(f"[不存在] {username}: {reason}")
|
|
330
|
-
else:
|
|
331
|
-
results['unknown'].append({
|
|
332
|
-
'username': username,
|
|
333
|
-
'reason': reason,
|
|
334
|
-
'response': resp
|
|
335
|
-
})
|
|
336
|
-
print(f"[未知] {username}: {reason}")
|
|
337
|
-
|
|
338
|
-
return results
|
|
339
|
-
|
|
340
|
-
# 使用示例
|
|
341
|
-
if __name__ == "__main__":
|
|
342
|
-
tester = UserEnumTester("http://api")
|
|
343
|
-
|
|
344
|
-
test_users = [
|
|
345
|
-
'admin', 'administrator', 'root', 'user', 'test',
|
|
346
|
-
'demo', 'guest', 'nonexist_abc123', 'superadmin'
|
|
347
|
-
]
|
|
348
|
-
|
|
349
|
-
results = tester.run_tests(test_users)
|
|
350
|
-
|
|
351
|
-
print(f"\n=== 测试结果汇总 ===")
|
|
352
|
-
print(f"存在: {len(results['exists'])}个")
|
|
353
|
-
print(f"不存在: {len(results['not_exists'])}个")
|
|
354
|
-
print(f"未知: {len(results['unknown'])}个")
|
|
355
|
-
|
|
356
|
-
# 如果存在unknown,需要进一步分析
|
|
357
|
-
if results['unknown']:
|
|
358
|
-
print(f"\n[注意] 存在{len(results['unknown'])}个无法确认的用户")
|
|
359
|
-
print("建议:人工分析这些用户的响应,确定是否为漏洞")
|
|
360
|
-
```
|
|
361
|
-
|
|
362
|
-
## 9. 实战判断案例
|
|
363
|
-
|
|
364
|
-
### 案例1:安全的登录接口
|
|
365
|
-
|
|
366
|
-
```
|
|
367
|
-
【场景】:所有用户名返回相同错误消息
|
|
368
|
-
|
|
369
|
-
curl测试:
|
|
370
|
-
curl -X POST /api/login -d '{"username":"admin","password":"wrong"}'
|
|
371
|
-
→ {"code":"90000","msg":"用户名或密码错误。"}
|
|
372
|
-
|
|
373
|
-
curl -X POST /api/login -d '{"username":"nonexist","password":"wrong"}'
|
|
374
|
-
→ {"code":"90000","msg":"用户名或密码错误。"}
|
|
375
|
-
|
|
376
|
-
判断:
|
|
377
|
-
- 响应完全相同
|
|
378
|
-
- 结论:【安全】不是用户枚举漏洞
|
|
379
|
-
```
|
|
380
|
-
|
|
381
|
-
### 案例2:真实用户枚举
|
|
382
|
-
|
|
383
|
-
```
|
|
384
|
-
【场景】:有效/无效用户返回不同消息
|
|
385
|
-
|
|
386
|
-
curl测试:
|
|
387
|
-
curl -X POST /api/login -d '{"username":"admin","password":"wrong"}'
|
|
388
|
-
→ {"code":"90001","msg":"密码错误,用户账号存在。"}
|
|
389
|
-
|
|
390
|
-
curl -X POST /api/login -d '{"username":"nonexist","password":"wrong"}'
|
|
391
|
-
→ {"code":"90002","msg":"用户不存在。"}
|
|
392
|
-
|
|
393
|
-
判断:
|
|
394
|
-
- 有效用户:"密码错误,用户账号存在"
|
|
395
|
-
- 无效用户:"用户不存在"
|
|
396
|
-
- 结论:【确认漏洞】可枚举有效用户
|
|
397
|
-
```
|
|
398
|
-
|
|
399
|
-
### 案例3:参数格式校验误报
|
|
400
|
-
|
|
401
|
-
```
|
|
402
|
-
【场景】:响应差异但不是用户枚举
|
|
403
|
-
|
|
404
|
-
curl测试:
|
|
405
|
-
curl -X POST /api/login -d '{"username":"","password":""}'
|
|
406
|
-
→ {"code":"90003","msg":"参数不能为空。"}
|
|
407
|
-
|
|
408
|
-
curl -X POST /api/login -d '{"username":"admin","password":"wrong"}'
|
|
409
|
-
→ {"code":"90000","msg":"用户名或密码错误。"}
|
|
410
|
-
|
|
411
|
-
判断:
|
|
412
|
-
- 差异是"参数为空" vs "用户名或密码错误"
|
|
413
|
-
- 这是参数格式校验,不是用户枚举
|
|
414
|
-
- 结论:【误报】需要填充有效用户名再测试
|
|
415
|
-
```
|
|
416
|
-
|
|
417
|
-
## 10. 风险评估
|
|
418
|
-
|
|
419
|
-
| 风险级别 | 条件 |
|
|
420
|
-
|----------|------|
|
|
421
|
-
| 高 | 可快速枚举大量用户/邮箱,无限制 |
|
|
422
|
-
| 中 | 有一定限制但可枚举个 |
|
|
423
|
-
| 低 | 有严格限制但仍可枚举 |
|