我从事 Web 应用程序渗透测试人员已有一段时间了,多年来一定发现了数百个跨站点脚本 (XSS) 漏洞。1跨站点脚本是一个出了名的难以解决的问题,并且由于 Web 服务器对客户端攻击缺乏可见性,因此阻碍了对它的检测。
所以,有一天我有了一个疯狂的想法:“是否有可能构建一个Canary,如果网站的任何地方存在 XSS 漏洞,它会通知网站所有者?”
让我们来一探究竟……
警告:本博客涉及一些技术性内容,面向对 Web 开发、JavaScript 和 Linux 有基本了解的读者。
挑选Canary
Canary是一种微妙的指示器,它发出特定情况或事件的信号,通常用作预警系统或潜在问题的警报。
当安全研究人员在网站上寻找 XSS 漏洞时,他们通常会执行alert() JavaScript 函数作为概念验证。这是因为alert()函数提供了明确无误的漏洞利用指示,可暂停网页的执行,并允许黑客快速识别和验证 XSS 漏洞的存在。因此,alert()已成为在线 XSS 词汇表中的必备词。
根据我的经验,现代网站很少使用alert()函数在浏览器中显示信息,因此在合法浏览过程中,这种情况并不常见。因此,当现代网页上触发警告框时,很可能是 XSS 攻击成功的标志。
构建 XSS Canary
确定alert()函数为触发器后,我们现在可以设计Canary的其余部分。我们的目标是发出 XSS 漏洞的信号,并向蓝队提供有价值的见解。为了实现这一目标,我们的Canary必须:
- 保存原始alert()函数的副本,保留其原始行为。
- 捕获关键上下文信息以全面了解攻击。
- 将关键上下文信息发送到回调服务器,在那里可以进行分析并用于通知安全决策。
- 调用已保存的alert()函数,保留原有的功能。
以下 JavaScript 示例演示了挂钩alert()函数的基本实现,使我们能够在继续执行原始行为之前执行自定义代码。在本例中,我们将先计算 2+2,然后再继续。
const originalAlert = window.alert; // Saving a copy of alert()window.alert = function(...args) { // Hooking alert() console.log(2+2); // Run any code you want here originalAlert.apply(window, args); // Calling the original alert()};
挂载alert()函数后,我们现在可以将注意力转移到重建攻击序列,并更深入地了解我们的网站是如何被利用的。为此,我们将生成详细的堆栈跟踪,清晰地显示导致漏洞触发的执行路径。我们还将收集警报消息、受感染页面的 URL、事件的时间戳和文档引用者。然后,我们将在漏洞利用时捕获 DOM 的快照,其中应包含页面中反映的任何恶意代码。
const debugData = { alert_msg: args.join(' '), // The message displayed by the attacker stack: error.stack, // A full stack trace of the execution url: window.location.href, // The current URL with parameters ref: document.referrer, // The referrer domain dom: document.documentElement.outerHTML, // A copy of the DOM timestamp: newDate().toISOString() // Timestamp};
// Send the data to the callback server via a POST requestfetch('https://example.com/xss', {method: 'POST',headers: {'Content-Type': 'application/json', },// Convert the debug data to JSONbody: JSON.stringify(debugData)})
const originalAlert = window.alert;window.alert = function(...args) {// Create an error to capture the stack traceconst error = newError();// Gather the debugging informationconst debugData = {alert_msg: args.join(' '), // Alert Message stack: error.stack, // Stack Trace url: window.location.href, // Current URL ref: document.referrer, // Website Referrer dom: document.documentElement.outerHTML, // Copy of the DOM timestamp: newDate().toISOString() // Timestamp };// Send the data to the canary callback server via a POST request fetch('https://example.com/xss', {method: 'POST',headers: {'Content-Type': 'application/json', },// Convert the debug data to JSON body: JSON.stringify(debugData) }) .catch((error) => {console.error('Failed to send xss report:', error); });// Call the original alert function to ensure the alert still works originalAlert.apply(window, args);};
XSS Canary 回调 Web 服务器设置
在继续操作之前,请确保您有一个专用域和一个专用 Linux 虚拟专用服务器 (VPS),该服务器运行 Ubuntu 或 Debian,至少有两个核心和 2GB RAM。从域设置指向 VPS 公共 IP 地址的 A 记录。以下步骤将一般将此域称为example.com。
要从 XSS Canary接收日志,您需要设置回调服务器以在管理门户中查看收集的报告。您可以随意修改服务器代码以通过电子邮件、Slack、Discord 或您选择的任何其他消息平台发送通知。如果您开发了实时消息集成,请考虑与社区分享。
安装脚本
为了在您的服务器上轻松安装 XSS canary 回调软件,我创建了一个安装脚本。3此脚本首先安装依赖项,然后创建系统守护程序以低权限用户身份运行 Web 服务器。命令中的电子邮件由 Let's Encrypt 用于在您的 SSL 证书即将到期时通知您,尽管默认情况下启用了自动续订。以 root 身份将 curl 传输到 bash 通常是不明智的,因此,请在专用 VPS 上执行以下命令之前阅读代码。
bash <(curl -s https://xsscanary.com/install) example.com [email protected]
回调 Web 服务器有两个端点。首先,/xss端点接受包含来自 XSS Canary的调试信息的 POST 请求。收到请求后,服务器将随附的 XSS Canary数据作为 JSON 对象存储在xss_canary.json文件中。其次,/dashboard端点返回一个受密码保护的页面,供管理员查看传入的Canary。安装后,您可以使用用户名admin和安装脚本输出中显示的密码在此处登录。
将 XSS Canary 插入到您的网站
现在我们已经设置好了回调服务器,我们将注意力转回到canary代码上。
为了简化流程,我在xsscanary.com上提供了Canary代码。域GET 参数允许您替换网站的回调服务器,从而轻松将canary集成到您现有的基础设施中。该脚本有两个版本,一个在调试信息中包含 DOM,另一个则排除它。下面提供了两个版本的链接:
https://xsscanary.com/canary.js?domain=example.comhttps://xsscanary.com/canary_no_dom.js?domain=example.com
但是,从互联网上找到的随机博客中引用 JavaScript 文件通常是一种不好的做法,因为我可能有一天会轻易将代码更改为恶意代码。相反,您应该首先独立阅读并验证代码,然后使用子资源完整性(SRI) 检查。这会对脚本的 SHA-384 哈希进行硬编码,这样浏览器只有在哈希完全匹配时才会执行它。这种安全措施可以防止您不得不信任我。
以下网站将为您创建一个安全的canary脚本标签。我将在博客的其余部分使用test.xsscanary.com作为我的回调域。
https://www.srihash.org/
生成 XSS Canary SRI 脚本标签
或者,您可以选择直接在您的网站上包含 XSS canary 脚本,这样就无需依赖外部源并提供额外的控制和安全性。
测试 canary
要测试您的 XSS canary,请将以下 HTML 文件中突出显示的脚本标签替换为您自己的由srihash.org生成的脚本标签。将更新后的 HTML 文件保存到您的桌面,然后在您喜欢的 Web 浏览器中打开它,以验证 canary 是否按预期工作。
<!DOCTYPE html><htmllang="en"><scriptsrc="https://xsscanary.com/canary.js?domain=test.xsscanary.com"integrity="sha384-sxhmxvuSR2mKLQjVnLSd0BjPODym8uvUotztbvITfsgmI2jtpgHv3Er2d5IikySU"crossorigin="anonymous"></script><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width, initial-scale=1.0"><title>XSS Canary Test</title></head><body><h1>Testing XSS Canary</h1><p>Welcome to the vulnerable webpage. </br>Please check the URL's <strong>id</strong> parameter!</p><script>// Retrieve the 'id' query parameter from location.searchvar urlParams = new URLSearchParams(location.search);// Only using the 'id' parameter from the query stringvar userId = urlParams.get('id'); // Vulnerable: Using document.write without sanitizationdocument.write("<p>User ID: " + userId + "</p>");</script></body></html>
一旦打开 HTML 文件,XSS canary 就会自动加载,如下面的开发人员工具屏幕截图中的 (1) 和 (2) 所示。此网页的id GET 参数中存在基于 DOM 的跨站点脚本漏洞,可通过将有效负载?id=”><svg%20onload=alert(“BHIS”)>添加到 URL 栏来利用该漏洞,如下所示 (3)。id参数值以不安全的方式写入网页,从而触发漏洞。因为我们钩住了alert()函数,所以 canary 被激活 (4) 并将调试信息发送到我的回调服务器(在本例中为test.xsscanary.com)。最后,调用原始alert()函数,向用户显示警报 (5),而没有任何视觉迹象表明已发送了 canary。
XSS Canary 测试页面系列事件
如果我们检查上图中的请求(4),我们会看到 POST 正文包含发送到Canary回调 Web 服务器的以下信息。
通过 POST 发送的 XSS Canary 信息
要查看回调服务器收集的 XSS Canary,请打开https://示例.com/dashboard,这将提示您输入用户名和密码。用户名是admin,您的密码是在安装过程中生成的。如果您忘记了密码,请重新登录回调服务器并执行以下命令。只有重新运行安装脚本,此密码才会更改。
echo$DASHBOARD_PASSWORD
登录后,您应该会看到以下信息。从Canary报告中,我们可以轻松识别出id GET 参数导致我们网站的/example.html端点出现 XSS 漏洞。现在您可以坐下来,让攻击者完成所有工作,同时您的监控系统会捕获 XSS 0day 漏洞。
XSS Canary 仪表板示例,其中突出显示了 XSS 负载
验证 XSS Canary正常运行后,只需将Canary代码添加到每个网页顶部,即可将其集成到您的网站中。将Canary放在每个网页顶部可确保我们的挂钩脚本在任何其他代码之前运行。
如果您已经做到了这一点,那么您已经成功实施了一个强大的监控系统,该系统可以检测并提醒您网站上的反射型、基于 DOM 和存储型跨站点脚本 (XSS) 漏洞。这种主动方法将帮助您领先于威胁,使您能够迅速做出反应并最大限度地缩短暴露时间。
结束语和思考
虽然 XSS canary 是一种用于实时检测跨站点脚本漏洞的高效工具,但在您的网站上实施它之前必须考虑一些关键因素。
- 如果您的网站频繁使用alert()函数,您可能会遇到大量误报。
- 您的回调服务器将是公开信息,因为它在每个网页上都会被引用。没有办法阻止用户手动提交虚假信息。
- 我强烈建议您在专用域和专用 VPS 上运行Canary回调服务器。
- 如果您运行漏洞赏金计划并计划实施 XSS Canary,请考虑等到漏洞被报告后再进行修补,以便研究人员能够获得他们努力的奖励。
- 如果您的网站处理敏感的医疗或财务数据,您可能不应该发送用户 DOM 的副本,因为它可能包含 PII。相反,请使用此脚本的替代版本,它省略了 DOM 信息xsscanary.com/canary_no_dom.js。
- 本博客由安全研究人员撰写,而非法律或 GDPR 专家。如果您受隐私法规约束,请在向您的网站添加 XSS Canary之前咨询法律专业人士。
- 如果攻击者使用print()而不是2021 年 James Kettle建议的alert(),则此 XSS Canary不会触发。XSS Canary代码可以轻松修改以挂钩print(),但我将其留给读者作为家庭作业。
如果攻击者在较长时间内仔细检查您的网站,他们可能会调查“XSS Canary”并阻止该域连接。但是,正如我们前面提到的,互联网上的大多数 XSS 单词表都使用alert()作为概念证明,理由很充分:攻击者和安全研究人员倾向于避免增加其有效负载的复杂性,因为这可能会导致漏洞利用失败。这些单词表通常会加载到扫描仪中并喷洒到网站参数上,而无需额外的解除挂钩逻辑。
如果您担心您的 Canary 被绕过,请考虑将 JavaScript Canary 的模糊版本直接添加到您的网站中,并使用经过修改的回调端点。这种方法使用户更难在 DNS 级别进行阻止,同时也使使用静态签名检测它的尝试变得复杂。
在撰写本文时,我相信 XSS Canary概念是原创的,但如果不是这样,请与我联系,我会很乐意提供对类似研究的参考。
References
- https://owasp.org/www-community/attacks/xss/
- https://github.com/danielmiessler/SecLists/tree/master/Fuzzing/XSS/human-friendly
- https://gist.github.com/ACK-J/9acef3f7d188de49d6ff7304328e168a
- https://github.com/ACK-J/XSS-Canary-Callback
- https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity
- https://portswigger.net/research/alert-is-dead-long-live-print
- https://discord.com/invite/bhis
- https://github.com/ACK-J/XSS-Canary-Callback
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论