xss-labs 是一套基于 PHP 的 XSS 靶场,以闯关的形式让我们体验各种 XSS 漏洞利用方式。虽然已经是很多年前的了,但其中基本的 XSS 漏洞依然很有意义。
一共有 20 关,每一关的目标都是实现弹窗(alert(1)
),如果成功就会自动进入下一关。
(本文首发于 blog.skywt.cn,建议 查看原文以获得更好的阅读体验。)
环境搭建
克隆这个代码仓库的代码,其中是 xss-labs 的 PHP 源文件。只要放在 PHP 环境下即可,推荐使用 XAMPP 或者 Docker。
部署完成后,访问 index.php,就可以看到入口了:
Warm Up
前两关是对于 XSS 基本原理的应用,没有任何的防御。
Level 1:文本解析为 HTML
URL 为 level1.php?name=test
时,传入的参数是 test,显示的是「欢迎用户 test」。显然,这个页面会将我们传入的名字显示出来。
传入参数 <h1>test</h1>
作为 name
,发现确实显示为了一级标题。看来传入的 name 直接被作为 HTML 显示了。于是尝试传入 <script>alert(1);</script>
作为 name
,成功弹窗。
?name=<script>alert(1);</script>
Level 2:input 标签 value 注入
这关多了一个文本框。如果依然尝试传入 <script>alert(1);</script>
,会发现在 h2 元素中符号被转义了(查看网页源代码能看到),比如 <
被转义成了 <
等,所以不会被解析成 HTML。
然而,可以看到后面的文本框 input
元素,其 value 值并没有被转义。
<h2 align=center>没有找到和<script>alert(1)</script>相关的结果.</h2><center>
<input name=keyword value="<script>alert(1)</script>">
既然如此,我们可以闭合 value 的内容的后引号,然后闭合这个 input 标签,接下来再加入我们想要注入的 <script>alert(1);</script>
。只需要在其之前加上 ">
即可。
?keyword="><script>alert(1);</script>
字符过滤绕过
Level 3:htmlspecialchars()
的弱点
本题要求 PHP 版本低于 8.1.0。在 8.1.0 版本中此漏洞已经修复。
HTML 标准规定了一个叫做**实体(Entity)**的概念。在 HTML 中,<h1>
会被解释为标题标签,那我们如何以文本形式显示出 <h1>
这四个字符呢?答案是使用字符实体,将 <
用 <
替代,将 >
用 >
替代。在 HTML 中,如果我们写 <h1>
,就会显示为 <h1>
而不会被解析。
在 PHP 中,后端需要渲染出一个 HTML 给前端,对传来的字符串就要进行这样的处理,将 <
这样的字符转换为 <
这样的实体。Level 1 就是没有进行这种处理的下场。
一个通常的做法就是:使用 PHP 自带的 htmlspecialchars()
函数处理字符串,进行字符转义。具体用法可以参考官方文档。
在 8.1.0 及以上的 PHP 版本中,这个函数默认会转义 <
、>
、&
、'
、"
这五个字符,基本可以防范这里的 XSS 攻击。
但是,8.1.0 以下版本的 PHP 默认只会转义 <
、>
、&
、"
这四个字符,不会转义单引号 '
。这就给这个函数带来了巨大的安全隐患。
回到这题,我们仍然尝试 ?keyword=<script>alert(1)</script>
,打开页面源码,观察这个 input 输入框:
<input name=keyword value='<script>alert(1)</script>'><