关注这个漏洞的其他相关笔记:XSS 漏洞 - 学习手册-CSDN博客
0x01:存储型 XSS —— 理论篇
存储型 XSS 又称持久型 XSS,攻击脚本将被永久的存放在目标服务器的数据库或文件中,具有很高的隐蔽性。
常见的攻击方式: 这种攻击多见于论坛、博客、留言板,攻击者在发帖过程中,将恶意脚本连同正常信息一起注入帖子的内容中。随着帖子被服务器存储下来,恶意脚本也永久地被存放在服务器的后端存储器中。当其他用户浏览这个被注入了恶意脚本的帖子时,恶意脚本会在他们的浏览器中得到执行。
其攻击流程如下图所示:
从上面的攻击流程中,我们可以看到,存储型 XSS 的攻击方式能将恶意代码永久的嵌入一个页面中,所有访问这个页面的用户都将成为受害者。如果我们能够谨慎对待不明链接,那么反射型 XSS 攻击将没有多大作为,而存储型 XSS 则不同,由于它注入在一些我们信任的页面,因此无论我们多么小心,都难免会受到攻击(谁会一直打开浏览器的源码模式抓包呀)。
0x02:存储型 XSS —— 实战篇
实验工具准备
PHP 运行环境:phpstudy_x64_8.1.1.3.zip(安装教程:PhpStudy 安装-CSDN博客)
PIKACHU 靶场:pikachu-master.zip (安装教程:PIKACHU 靶场初识-CSDN博客)
本次实验采用的是现成的 PIKACHU 靶场,该靶场的搭建流程参考上面的链接,这里就不多说了。
0x0201:存储型 XSS 攻击
在浏览器的导航栏中输入下面的网址,访问 PIKACHU 靶场中的存储型 XSS 漏洞地址:
http://127.0.0.1/pikachu/vul/xss/xss_stored.php
可以看到是一个留言板,我们可以写几条留言看看:
从上图可以看出来,使用不同的浏览器,在不同的时刻去查看之前的留言,都是能看到相同的内容的。
我们鼠标右击页面,查看一下网页源码:
可以发现,我们传入的内容直接回显在了 <p>
标签中。那么我们尝试留言一个 JavaScript 脚本看看呢:
<script>alert(/You've Been Tricked/)</script>
留言之后,点击 submit
页面直接弹窗了。此时,查看一下留言板的网页源码:
可以看到,我们之前留言的 JavaScript 脚本被成功嵌入到了前端页面中,此时我们使用另一个浏览器访问当前页面,模拟其他用户的登录操作:
可以看到,其他用户一访问这个存在存储型 XSS 漏洞的站点,就收到了弹窗,证明被 XSS 攻击了。至此,存储型 XSS 攻击的全流程演示完毕。
0x0202:存储型 XSS 代码分析
下面是触发存储型 XSS 漏洞的关键代码:
<?php
$link = connect(); // 获取数据库连接对象
$html = '';
if (array_key_exists("message", $_POST) && $_POST['message'] != null) {
// 如果 POST 请求中存在 message 字段,且 message 字段不为空
$message = escape($link, $_POST['message']); // 对传入的数据进行转义,避免 SQL 注入漏洞
$query = "insert into message(content,time) values('$message',now())";
$result = execute($link, $query);// 执行 $query 中的内容,将客户端传递的留言插入到数据库中
if (mysqli_affected_rows($link) != 1) {
$html .= "<p>数据库出现异常,提交失败!</p>";
}
}
?>
<div id="xsss_main">
<p class="xsss_title">我是一个留言板:</p>
<form method="post">
// 用户留言的地方,在 textarea 中填写留言后,点击 submit 即可以 POST 方式提交到当前页面,后续会被直接插入到数据库中
<textarea class="xsss_in" name="message"></textarea><br />
<input class="xsss_submit" type="submit" name="submit" value="submit" />
</form>
<div id="show_message"><br /><br />
<p class="line">留言列表:</p>
<?php
echo $html;
$query = "select * from message";
$result = execute($link, $query); // 查询数据库中存放的所有留言
while ($data = mysqli_fetch_assoc($result)) {
// 遍历查询出来的结果,并拼接回显
echo "<p class='con'>{$data['content']}</p><a href='xss_stored.php?id={$data['id']}'>删除</a>"; // 将数据库中的留言渲染回页面
}
echo $html;
?>
</div>
</div>
用户在留言板中留言后,通过 <form>
表单,将数据传递给后端,后端接收到用户的数据后,使用自己编写的 escape()
函数对用户传入的内容进行了转义,从而避免了 SQL 注入攻击。转义完成后就将用户提交的内容插入到了数据库中。在接下来的逻辑中,目标将数据库中存储的内容直接回显回了页面,没有做任何的过滤,导致出现了 XSS 漏洞。
从上面的代码分析来看,目标只在将用户提交的信息插入数据库的时候做了一次过滤,我们看一下其过滤逻辑:
//转义,避免fuck
function escape($link,$data){
if(is_string($data)){
// 传入的如果是字符串,就直接使用 mysqli_real_escape_string() 进行转义
return mysqli_real_escape_string($link,$data);
}
if(is_array($data)){
// 传入的如果是数组,则对没个元素都使用 mysqli_real_escape_string() 进行转义
foreach ($data as $key=>$val){
$data[$key]=escape($link,$val);
}
}
return $data;
}
mysqli_real_escape_string()
函数的定义与用法如下图所示:
可以发现该函数只转义在 SQL 语句中使用的字符串中的特殊字符。并不包括我们在 XSS 中常用的 <
、>
这些特殊符号,所以目标后端只对输入时的 SQL 注入做了防范,但对 XSS 并未做防御。
所以,当用户在留言板中写入 <script>alert(/You've Been Tricked/)</script>
时,数据库中的数据如下图所示:
当其他用访问该留言板时,页面回显并执行了 <script>alert(/You've Been Tricked/)</script>
,导致弹窗。