目录
本系列为通过《pikachu靶场通关笔记》的XSS关卡(共10关)渗透集合,通过对XSS关卡源码的代码审计找到XSS风险的真实原因,讲解XSS的原理并进行渗透实践,本文为XSS关卡03-存储型XSS的渗透部分。
一、XSS
XSS 全称为跨站脚本攻击(Cross - Site Scripting),因其高危害性长期位列OWASP Top 10安全威胁。攻击者通过注入恶意脚本(通常为JavaScript)到网页中,脚本在其浏览器执行。XSS主要分为3种类别,具体如下表所示。
类型 | 存储方式 | 触发条件 | 危害范围 | 典型场景 | 防御重点 |
---|---|---|---|---|---|
存储型XSS | 恶意脚本永久存储在服务器 | 用户访问包含恶意脚本的页面 | 所有访问用户 | 论坛评论区、用户资料页 | 输入过滤+输出编码 |
反射型XSS | 不存储,通过URL参数传递 | 用户点击特制恶意链接 | 单个点击用户 | 搜索框、错误页面 | URL参数消毒+CSP策略 |
DOM型XSS | 不经过服务器,纯前端风险 | 前端JS动态解析恶意构造的输入 | 执行恶意代码的用户 | 单页应用(SPA)、URL哈希值 | 安全的DOM操作+前端过滤 |
二、存储型XSS
存储型XSS(持久性跨站脚本攻击)是XSS风险中最严重的一种类型,其特点是恶意脚本被永久存储在目标服务器上(如数据库、文件系统等),当其他用户访问包含该恶意脚本的页面时,脚本会自动在其浏览器中执行,造成持续性的安全威胁。
这类攻击常见于用户提交内容可被重复展示的场景,如论坛评论区、用户资料页、商品评价区等Web交互功能,攻击者只需提交一次恶意代码(如包含JavaScript的评论),所有后续访问该页面的用户都会成为受害者。
分类 | 存储型XSS |
---|---|
别名 | 持久型XSS |
存储位置 | 服务器端(数据库、文件系统等) |
触发方式 | 用户访问被注入的页面时自动执行 |
攻击场景 | 论坛评论、用户资料、商品评价等可存储用户输入的交互功能 |
危害范围 | 所有访问被污染页面的用户 |
攻击特点 | 长期存在,无需诱导点击(与反射型XSS不同) |
修复难度 | 高(需清理数据库中已存储的恶意脚本) |
存储型XSS的危害性极大,可导致大规模用户数据泄露(如窃取登录凭证、会话Cookie)、网页内容篡改(如植入钓鱼表单),甚至传播恶意软件。由于恶意脚本存储在服务器,其影响会持续存在,直到被人工清除。
三、源码分析
进入pikachu靶场XSS系列种的第03关卡-存储型XSS页面,打开后发现是一个留言板,如下所示。
http://127.0.0.1/pikachu/vul/xss/xss_stored.php
查看存储型XSS关卡源码xss_stored.php文件内容,通过POST传入的参数message会被存储到数据库,且没有对message参数进行转义HTML特殊字符的过滤,说明有XSS存储型风险,具体如下所示。
接下来对源码进行详细注释,具体如下所示。
// 调用 connect 函数,建立与数据库的连接,并将返回的连接对象赋值给变量 $link
// 这个函数应该是自定义的,用于封装数据库连接的逻辑
$link = connect();
// 初始化一个空字符串变量 $html,用于存储后续要输出的提示信息
$html = '';
// 检查 $_POST 数组中是否存在键为 "message" 的元素,并且该元素的值不为 null
// 即判断用户是否通过 POST 请求提交了名为 "message" 的数据
if (array_key_exists("message", $_POST) && $_POST['message'] != null) {
// 调用 escape 函数,对用户输入的 "message" 数据进行转义处理
// 该函数可能是自定义的,用于防止 SQL 注入攻击,将特殊字符进行转义
// 传入数据库连接对象 $link 和用户输入的 "message" 数据作为参数
$message = escape($link, $_POST['message']);
// 构建一个 SQL 插入语句,将用户输入的 "message" 数据插入到名为 "message" 的数据库表中
// 插入的数据包含两个字段:content 存储用户输入的消息内容,time 存储当前时间
// 使用 now() 函数获取当前时间
$query = "insert into message(content,time) values('$message',now())";
// 调用 execute 函数,执行上述构建好的 SQL 插入语句
// 该函数应该是自定义的,用于封装执行 SQL 语句的逻辑
// 传入数据库连接对象 $link 和 SQL 语句 $query 作为参数
$result = execute($link, $query);
// 检查执行 SQL 语句后受影响的行数是否不等于 1
// 如果不等于 1,说明插入操作可能没有成功,数据库出现异常
if (mysqli_affected_rows($link) != 1) {
// 如果插入操作失败,向 $html 变量中追加一段提示信息
// 提示用户数据库出现异常,提交失败
$html .= "<p>数据库出现异常,提交失败!</p>";
}
}
这段代码存在存储型 XSS风险,具体原因如下。
- 仅进行 SQL 转义,未对 XSS 攻击进行防护:虽然代码调用了 escape 函数对用户输入进行处理,其目的可能是为了防止 SQL 注入,但这个处理并没有考虑到 XSS 攻击的情况。escape 函数可能只是对 SQL 语句中的特殊字符进行转义,而没有对可能的 HTML 标签和 JavaScript 代码进行处理。
- 未对输出内容进行过滤和转义:当这些包含恶意脚本的内容从数据库中取出并显示在网页上时,由于没有对输出内容进行过滤和转义,浏览器会将其中的脚本代码解析并执行,从而导致 XSS 攻击。任何访问该页面的用户都会受到影响,因为恶意脚本会被存储在数据库中并持续影响后续的页面访问。
其中escape函数为自定义函数,在mysql.inc.php文件中定义,具体如下所示。
/**
* 对数据进行转义处理,防止 SQL 注入攻击
*
* 该函数可以处理字符串和数组类型的数据。对于字符串,使用 mysqli_real_escape_string 函数进行转义;
* 对于数组,会递归调用自身对数组中的每个元素进行转义处理。
*
* @param mysqli $link 数据库连接对象,用于 mysqli_real_escape_string 函数
* @param mixed $data 需要进行转义处理的数据,可以是字符串或数组
* @return mixed 转义后的数据,保持与输入数据相同的类型(字符串或数组)
*/
function escape($link, $data) {
// 检查传入的数据是否为字符串类型
if (is_string($data)) {
// 如果是字符串,使用 mysqli_real_escape_string 函数对字符串进行转义
// 该函数会对字符串中的特殊字符(如单引号、双引号、反斜杠等)进行转义,防止 SQL 注入
// 返回转义后的字符串
return mysqli_real_escape_string($link, $data);
}
// 检查传入的数据是否为数组类型
if (is_array($data)) {
// 如果是数组,遍历数组中的每个元素
foreach ($data as $key => $val) {
// 递归调用 escape 函数,对数组中的每个元素进行转义处理
// 将转义后的元素重新赋值给数组中对应的键
$data[$key] = escape($link, $val);
}
}
// 如果数据既不是字符串也不是数组,直接返回原始数据
return $data;
}
四、渗透实战
1、输入mooyuan试一试
进入靶场的xss存储型关卡,如下所示是一个留言板页面,尝试输入mooyuan。
此时可以通过数据库管理软件,查看pikachu的message表,如下所示表中有一条为mooyuan的内容,说明留言板写入内容mooyuan被存储到数据库中。
2、注入Payload
XSS注入语句<script>alert("mooyuan")</script>的含义如下所示。
- <script> 标签:HTML 中定义 JavaScript 代码块的标记。
- alert("mooyuan"):调用浏览器的弹窗函数,显示内容为 mooyuan 的警告框。
- 整体作用:当该脚本被浏览器解析时,会立即弹出警示框。
<script>alert("mooyuan")</script>
输入<script>alert("mooyuan")</script>,弹框显示mooyuan说明渗透成功,如下所示。
3、查看数据库
查看数据库,如下所示XSS脚本被写入数据库。
4、再次进入留言板页面
更换浏览器访问存储型XSS页面,进入页面即弹框mooyuan,触发了XSS脚本的执行,渗透成功。
http://127.0.0.1/pikachu/vul/xss/xss_stored.php
点击确定后,页面如下所示。
可以通过点击最下面的删除,将注入的恶意XSS脚本删除。