新闻详情

新闻详情

首页 / 资讯中心 / 详情

sqli-labs Less-24二次注入实战:从卡关到彻底理解存储型注入

发布时间:2026/9/25 12:45:24来源:尧图网络
sqli-labs Less-24二次注入实战:从卡关到彻底理解存储型注入
sqli-labs 刷到 Less-24 的时候很多人会突然卡住。前面那些关卡只要在 URL 里加个单引号、改个参数页面就会原形毕露但 Less-24 打开就是一个普通的登录页输入admin、1 or 11这些经典 payload页面纹丝不动。这时候别急着砸键盘这一关其实已经在数据库里悄悄埋好了雷。Less-24 是 sqli-labs 靶场里专门讲二次注入的一关英文名 Second Degree Injections也叫二阶注入或存储型注入。它用一个再普通不过的“修改密码”功能把“输入入库后再被取出拼接”这个隐蔽场景完整演了一遍。这篇文章适合已经把 Less-1 到 Less-23 走过一遍、想在业务逻辑层面理解注入的读者也适合第一次接触“二次注入”概念、不知道 payload 该怎么构造的新手。1. 这一关到底在考什么Less-24 的二次注入原理1.1 sqli-labs 中 Less-24 的定位与“卡关”原因sqli-labs 是一个基于 PHP 和 MySQL 的 SQL 注入练习靶场关卡被分成几大块前二十几关主要考最基础的参数拼接和报错回显后面还有布尔盲注、时间盲注、堆叠注入等。Less-24 位于一个很特别的转折点它不再把一个查询参数直接变成注入点而是把攻击载荷拆成两段先在注册功能里“存进去”再让修改密码功能在毫不知情的情况下“取出来用”。很多人在前面关卡养成的习惯是看到参数就测单引号看到回显就判断列数看到报错就想办法利用报错函数。到了 Less-24 这些手段全部失效因为第一次请求返回的是完全正常的注册成功、登录成功。这不是靶场出了问题而是注入发生的时机变了。如果你只用“请求-响应”的视角去刷题很难理解这一关必须把视角切换到“数据在数据库里流动了一圈之后又回到 SQL 语句里”的时间线才能看到漏洞全貌。1.2 普通注入与二次注入的核心差异普通注入和二次注入的区别可以打一个比方普通注入是快递包裹在安检机前当场爆炸你一提交参数页面立刻变形报错也好、回显也好都是同一个请求内完成。二次注入则是先裹好炸弹寄存进仓储柜所有安检环节都正常等某天柜子被打开、行李被转运到另一个地方才在完全不同的业务操作里引爆。对比项普通注入二次注入触发时机第一次请求立即生效第一次只负责存储后续操作才生效输入点参数直接被拼入 SQL输入先入库再从库里读出拼入另一条 SQL表面特征报错、回显、布尔差异明显第一次请求完全正常无明显异常判断难度低通过 fuzz 容易发现高需要梳理完整数据流典型场景搜索、排序、详情页注册后改密码、评论后后台审核、昵称被后台引用普通注入的漏洞点往往写在 URL 和参数里扫一眼代码就能定位。二次注入的漏洞点却藏在业务链路里入口和触发点分离入口安全不代表整条链路安全。这也是为什么很多开发者会对这类漏洞感到意外明明注册时已经做了转义和过滤怎么最后还是被注入了。1.3 Less-24 的业务模型登录、注册、改密码Less-24 模拟了一个非常标准的用户系统有注册页有登录页登录后能看到当前用户信息可以修改密码。数据库里只有一张 users 表核心字段是 id、username、passwd结构简单得不能再简单。但正因为简单你才能清楚地看到同一个 username 字段在三条 SQL 里的不同命运注册时username 作为用户输入经过转义后写入数据库。登录时username 作为查询条件经过转义后匹配记录。修改密码时username 从 session 中取出直接拼接进 UPDATE 语句。前两条都有防护第三条没有。二次注入的整个过程就是围绕第三条 SQL 展开的。如果你在注册时构造了一个“看似正常、实则有毒”的用户名它就能在一个看似与注入毫无关系的功能里改变完全不同的另一条数据记录。2. 环境准备与前置知识点2.1 本地靶场环境搭建sqli-labs 是十多年前的经典项目源码里大量使用老式mysql_*函数PHP 7 及以上版本已经把这些函数移除了。如果你是第一次搭直接用最新版 PHP 跑源码大概率第一关就报Call to undefined function mysql_connect()还没开始注入就被环境劝退。我自己的做法是优先用 Docker。只要本地装了 Docker一句命令就能把带 PHP 运行环境的靶场拉起来docker run -d -p 8080:80 --name sqli-labs \ -e MYSQL_ROOT_PASSWORDroot \ acgpiano/sqli-labs:latest这个镜像名是我常用的一个社区里也有别的版本具体不唯一。重点是端口映射把容器里的 80 端口映射到本机 8080启动后访问http://localhost:8080/sqli-labs/Less-24/就能看到关卡页面。如果你更习惯在本地直接跑 PHP也可以把源码丢进 PHP 5.6 MySQL 的环境里但说实话为这一个老靶场折腾本地扩展不太值得Docker 是性价比最高的方案。2.2 说清楚源码里的三条关键 SQLLess-24 源码逻辑并不复杂网上也能找到我这里用简化伪代码说明避免被无关代码干扰// create_user.php 注册 $u mysql_real_escape_string($_POST[username]); $p mysql_real_escape_string($_POST[password]); $sql INSERT INTO users (username, passwd) VALUES ($u, $p); // login_user.php 登录 $u mysql_real_escape_string($_POST[login_user]); $p mysql_real_escape_string($_POST[login_password]); $sql SELECT * FROM users WHERE username$u AND password$p; // 登录成功后 $_SESSION[username] $row[username]; // pass_change.php 修改密码 $u $_SESSION[username]; // 没有再次转义 $p mysql_real_escape_string($_POST[new_password]); $sql UPDATE users SET passwd$p WHERE username$u;注册和登录都对输入做了mysql_real_escape_string唯独修改密码时用的是 session 里的username这个值来自登录时从数据库查出来的原始记录没有再经过任何过滤。这正是二次注入能成立的原因不是第一次没防住而是第二次没有重新设防。2.3 必懂的两个 SQL 语法点引号闭合与注释符要构造 Less-24 的 payload只需要理解两个 SQL 细节。第一是单引号闭合。SQL 里字符串常量用单引号包裹admin会让原本匹配普通字符串的提前变成结束符后面的内容从“字符串内容”变成“SQL 语法”。二次注入就是在数据库里存了一个包含单引号的字符串等它被拼进 UPDATE 时这个单引号再次“复活”。第二是注释符。MySQL 支持#和--两种方式注释掉行尾剩余内容。--后面必须跟一个空格而#没有这个限制。在 Less-24 这种 POST 表单场景里我推荐直接用#省去空格缺失带来的各种小问题。这里有个容易踩的坑#在 URL 里是锚点浏览器不会把它发送给服务器。如果你习惯在地址栏手动测试 GET 请求必须把#编码成%23。但 Less-24 的登录、注册、修改密码全部是表单 POST直接填#没有问题不需要编码。3. 完整通关实操从注册畸形用户名到改掉 admin 密码3.1 先走一遍正常功能流程进入 Less-24 后不要急着注入。先用普通用户走一遍注册—登录—修改密码的完整流程注册一个test / 123456登录改成654321再退出重新登录验证。这一步有两个作用一是确认靶场环境本身没问题二是让你记住正常 SQL 长什么样。后面注入成功时页面返回看起来和正常流程几乎一模一样唯一的区别是密码悄悄换到了 admin 头上。另外如果你之前玩过其他关卡users 表可能已经被改得乱七八糟建议在关卡页面先点一下重置数据库的入口。不重置就开测后面排查问题会增加很多干扰变量。3.2 注册一个叫 admin# 的用户在创建用户表单里输入以下内容用户名admin#密码123456点击注册页面会提示注册成功看起来完全正常。但这时候数据库里已经出现了一个“特别”的用户记录。为什么能注册成功因为注册代码对用户名做了转义admin#被处理成了admin\#插入语句变成INSERT INTO users (username, passwd) VALUES (admin\#, 123456)在 MySQL 看来\只是字符串里的一个普通单引号字符并不会破坏整条语句的语法结构。最终users 表里 username 字段保存的值是还原后的admin#而不是带反斜杠的admin\#。反斜杠是给 SQL 解析器看的临时保护数据库里存的是真实数据。这个细节是整个二次注入的起点如果这里把它理解错了后面所有步骤都会跟着错。3.3 用这个畸形用户名登录注册完成后用admin#作为用户名、123456作为密码登录。登录 SQL 同样经过转义SELECT * FROM users WHERE usernameadmin\# AND password123456因为单引号被转义这里的admin#被当作一个整体字符串去匹配正好匹配到刚才注册的那条记录。登录成功后系统从数据库里把 username 取出来写入$_SESSION[username]这个值就是原始的admin#。页面欢迎语上会显示类似 “Welcome to the password restricted area admin#” 的文字。如果你看到这个说明 session 里存的确实是带单引号的那个用户名而不是 admin。这一点很关键等下修改密码时注入的来源就是这个 session 值。3.4 在修改密码页面完成真正的注入登录后进入修改密码页面页面会要求填当前密码、新密码、确认新密码。此时随便填当前密码000000故意填错也没关系新密码hacked123确认新密码hacked123提交后台执行的 UPDATE 语句实际上是这样的UPDATE users SET passwdhacked123 WHERE usernameadmin# AND password000000#把 AND password000000全部注释掉实际执行的就是UPDATE users SET passwdhacked123 WHERE usernameadminadmin 用户的密码被改成了hacked123。提交后页面大概率提示密码修改成功看起来平平无奇但数据库里已经发生了一次真正的注入。你填写的新密码字段本身是经过转义再拼进 SQL 的想从这里直接注入非常困难真正的注入点来自 session 里的用户名而不是当前表单。提示故意把当前密码填错是判断注入是否生效的好方法。如果当前密码校验被#注释掉那么填错也能修改成功。你可以在本地做一次对照实验用普通用户 test 登录故意填错当前密码系统会拒绝修改用admin#登录同样填错当前密码系统却放行差异立刻显现。3.5 验证注入结果修改成功后退出当前账号回到登录页。这次用真正的 admin 账号登录用户名admin密码hacked123如果能登录成功说明二次注入链路完整打通。原本属于admin#这个畸形用户的一次密码修改操作实际上改写的是 admin 的密码。到这里Less-24 就算通关了。如果你还想多验证一层可以去数据库里直接查看 users 表会发现 admin 的 passwd 字段已经变成新值而admin#用户的密码还是原来的123456。这个对比能帮助你更直观地理解 UPDATE 语句的作用目标。4. 二次注入的深层原因与代码级拆解4.1 转义函数为什么没能“一劳永逸”mysql_real_escape_string的作用是在单引号、双引号、反斜杠等字符前加上反斜杠让它们进入 SQL 时只作为字符串内容而不是语法符号。在注册这一步它确实挡住了注入输入的用户名被安全地写进数据库没有产生额外的 SQL 语义。但转义函数有一个天然局限它只针对调用它的那一次 SQL 拼接生效。数据一旦写入数据库转义时加上的反斜杠不会跟着一起存储。下次把 username 从库里查出来时你得到的是已经还原的admin#单引号是真实存在的。如果后续代码拿这个值直接拼 SQL而没有再做一次转义单引号就重新获得了“结束字符串”的能力。一句话总结转义是语句级的临时保护不是数据级的永久免疫。4.2 数据库里的数据不等于“安全数据”很多开发者有一种本能假设经过第一道防线入库的数据之后从数据库里读出来就可以放心用。Less-24 把这种假设直接打碎了。任何进入数据库的数据如果之后还会被拼接到 SQL、HTML、命令、文件路径里都应该被重新当作不可信输入处理。放到真实业务里评论内容、昵称、收货地址、头像 URL 这些用户可控且会被后台功能反复引用的字段都是二次注入的高危区域。比如用户在注册时给自己起了个包含 SQL 片段的名字后台在导出报表、修改资料、生成订单时把这个用户名拼进 SQL就可能触发注入。入口过滤做得再严也只覆盖了第一跳后续每一跳都需要重新防御。4.3 正确修复从转义到参数化查询Less-24 的正确修复方式不是简单地给$_SESSION[username]也套一个mysql_real_escape_string而是改用参数化查询彻底让 SQL 结构和数据分离。$stmt $pdo-prepare(UPDATE users SET passwd ? WHERE username ?); $stmt-execute([$newPass, $username]);参数化查询会把占位符位置的内容严格当作数据传入数据库引擎不会把它解析成 SQL 语法。就算$username是admin#也只会去匹配一个真实存在的用户名admin#影响不到 admin。更彻底的设计修复是在用户体系里不要用 username 作为 UPDATE 的条件而是用不可被用户控制的自增主键 id。登录成功后把 user_id 写入 session修改密码时执行UPDATE users SET passwd ? WHERE id ?。id 是数字主键不存在引号闭合、注释符这些语法问题从根上消掉了这整类风险。平时设计接口时能用主键定位就尽量别用业务字段定位这是我在实际开发里很看重的一条原则。5. 常见问题与排查记录5.1 注册 admin# 后登录失败先检查用户名里是不是混入了全角单引号或空格。admin#必须是半角符号#和单引号之间不要有空格。还要检查数据库里是否已经存在同名记录如果之前已经注册过一次再注册会撞唯一约束表面上可能提示成功实际插入失败。另外如果你是在地址栏里用 GET 方式手动构造 URL 来测试登录接口#会被浏览器当作锚点截断请求包里根本没有#自然匹配不到admin#这条记录。Less-24 的登录是 POST 表单直接在页面上填写就行不需要在地址栏操作。5.2 改完密码后 admin 没变最常见的原因是登录成功后 session 里存的不是admin#而是admin。比如某些魔改版本的靶场在登录时会把用户提交的原始输入直接写入 session而不是存数据库查出来的值。解决办法是先看登录后页面的欢迎语显示什么如果显示admin说明 session 已经不对了需要重置数据库重新走注册流程。还有一种情况是当前密码校验逻辑没有被注释掉。某些版本修改密码的 SQL 可能是UPDATE users SET passwd$p WHERE username$u AND password$curr如果#没有生效说明注释符可能被转义了检查#前面是否被加了反斜杠或者当前密码字段被其他逻辑单独验证。遇到这种情况可以用admin--注意--后要有空格作为用户名替代方案绕过当前密码校验。5.3 环境报 mysql_connect 不存在这是用新版 PHP 直接跑老靶场的典型问题mysql_*系列函数在 PHP 7 中已经被移除。推荐方案是使用封装好的 Docker 镜像一条命令解决环境依赖如果非要本地方案可以安装 PHP 5.6或者把源码里的mysql_*函数批量替换为mysqli_*但参数顺序和返回值有差异替换完还可能遇到新的兼容问题。我自己实测下来Docker 是最省时间的选择。5.4 浏览器自动填充和翻译插件干扰浏览器密码管理器可能自动填充当前密码、新密码导致你提交的并不是自己手动填写的值修改结果自然对不上。建议在无痕窗口里测试或者提交前点开表单确认每个字段的值。这个坑在 Less-24 里特别容易出现因为页面表单标题是“Change Password”浏览器识别后会自动干预。5.5 忘记了数据库初始状态的重要性Less-24 的 users 表在多次测试后容易被改得面目全非admin 密码已经不是初始值admin#可能存在多条甚至表结构都被前一个实验影响。每次刷这一关之前先重置数据库清空所有干扰数据让测试环境回到已知状态。这样再看结果判断会非常干净。6. 最后分享一点自己的做题心得Less-24 让我第一次对“信任链”有了具体感知。以前我总以为SQL 注入的防线在入口只要每个输入点都过滤、转义漏洞就不存在了。但这一关逼着我重新审视数据流同一个字段可能在一个环节被严格保护却在另一个环节被当成可信值直接使用。安全不是一个单点问题而是一条完整的数据链问题。刷完这关之后我养成了一个习惯看代码时不仅看输入点有没有防护更看数据库读取值在后续 SQL 拼接、模板渲染、日志输出里有没有被重新当作“安全内容”。这个思维转变比记住admin#这个 payload 重要得多。也提醒一句这些手法都只在本地靶场里玩拿到真实系统上做测试既不道德也不合法。如果你想继续深挖二次注入可以试试把同样思路迁移到“修改用户名”“评论回复”“后台搜索”这些真实业务场景理解会更深。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

为什么AI算力集群这么烧钱?Flex:ai解决大模型与小模型混部场景的GPU浪费难题 2026/9/25 13:16:20

为什么AI算力集群这么烧钱?Flex:ai解决大模型与小模型混部场景的GPU浪费难题

为什么AI算力集群这么烧钱?Flex:ai解决大模型与小模型混部场景的GPU浪费难题 【免费下载链接】flexai Flex:ai是一个面向AI容器场景的开源项目,其核心能力包含两大部分,分别是XPU虚拟化和多级智能调度。其中XPU虚拟化分为本地XPU虚拟化和跨节…

阅读更多 →
Claude Code命令速查大全:TaoToken统一Key接入CLI斜杠命令与快捷键配置 2026/9/25 13:16:14

Claude Code命令速查大全:TaoToken统一Key接入CLI斜杠命令与快捷键配置

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
Apache Pulsar 授权与 ACL 实战指南:superuser、Proxy Roles 与租户级权限管理 2026/9/25 13:16:08

Apache Pulsar 授权与 ACL 实战指南:superuser、Proxy Roles 与租户级权限管理

消息队列后端流处理 【免费下载链接】pulsar Apache Pulsar - distributed pub-sub messaging system 项目地址: https://gitcode.com/gh_mirrors/pulsar28/pulsar 点击查看 免费下载 本指南以 Apache Pulsar 官方文档《Authentication and authorization in Pulsa…

阅读更多 →
J4125 与 I3-6100U 性能对比:用 TaoToken 统一 Key 跑通本地 AI 工具配置 2026/9/25 13:16:08

J4125 与 I3-6100U 性能对比:用 TaoToken 统一 Key 跑通本地 AI 工具配置

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
【教程】无需迁移IDE!Augment原生插件实现Cursor无缝平替 Claude-4无限用:TaoToken统一Key接入配置与验证 2026/9/25 13:16:08

【教程】无需迁移IDE!Augment原生插件实现Cursor无缝平替 Claude-4无限用:TaoToken统一Key接入配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
nRF54LC10A休眠电流50nA实测:低功耗设计从原理到落地 2026/9/25 13:15:55

nRF54LC10A休眠电流50nA实测:低功耗设计从原理到落地

1. 这颗芯片到底在卷什么:从休眠电流到电池寿命的换算逻辑第一次看到“休眠电流不到 50 nA”这个数字,我的反应是——这基本已经摸到了当前低功耗设计的物理天花板附近。nRF54LC10A 是 Nordic 在 nRF54L 系列里主打超低功耗的那一档产品,官方…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉