PortSwigger靶场之DOM XSS in jQuery selector sink using a hashchange event通关秘籍

发布于:2025-09-09 ⋅ 阅读:(17) ⋅ 点赞:(0)

一、靶场原理分析

(1)理论分析

  1. 漏洞类型: DOM 型 XSS。攻击的全部逻辑都发生在受害者的浏览器中,与服务器无关。

  2. 数据来源 (Source): location.hash

    • location.hash 是 URL 中 # (井号/哈希符号) 及其后面的部分。例如,在 https://example.com/page#section1 中, location.hash 的值就是 #section1

    • 它的主要用途是在页面内部实现导航(锚点链接),并且改变 hash 值不会导致整个页面重新加载

  3. 危险函数 (Sink): jQuery 的 $ 选择器函数

    • $() 函数通常用来选取页面元素,例如 $('#header') 会选中 id 为 header 的元素。

    • 漏洞关键点:在某些(尤其是旧版本的)jQuery 中,如果你给 $ 函数传入一个包含 HTML 标签的字符串,比如 $('<div>Hello</div>'),它不会去选择元素,而是会尝试创建这个 HTML 元素。

    • 如果创建的这个 HTML 元素带有可以执行脚本的事件处理器(如 onerror),脚本就会被执行。这个靶场的代码很可能就是这样:$(location.hash)

  4. 触发机制 (Trigger): hashchange 事件。

    • 这意味着有漏洞的代码并不是在页面加载时立即执行的,而是在 URL 的 hash 值发生改变时才会被触发。

    • 例如,用户从 .../page 跳转到 .../page#section1,或者从 .../page#section1 跳转到 .../page#section2,都会触发 hashchange 事件。

总结一下漏洞流程: 网站首页有一段 JavaScript 代码,它会监听 URL hash 的变化。当 hash 改变时,它会获取新的 hash 值,并将其直接传递给 jQuery 的 $ 选择器函数,试图滚动到页面上对应的博客文章。由于 $ 函数可以解析并创建 HTML,攻击者可以通过构造一个恶意的 hash 值来注入并执行任意 JavaScript。


这段代码实现页面内“自动滚动到指定文章功能”的脚本,但它包含了一个典型的基于 DOM 的跨站脚本(DOM-XSS)漏洞。

(2)靶场分析

1.代码详解

<script>
    // 1. 当 URL 的 hash 部分发生变化时,执行一个函数
    $(window).on('hashchange', function(){
        // 2. 构造一个 jQuery 选择器来查找文章标题
        var post = $('section.blog-list h2:contains("' + decodeURIComponent(window.location.hash.slice(1)) + '")');
        // 3. 如果找到了对应的文章
        if (post.get(0)) {
            // 4. 就滚动到那个文章的位置
            post.get(0).scrollIntoView();
        }
    });
</script>

第 1 行: $(window).on('hashchange', function(){ ... });

  • 这是一个事件监听器。

  • hashchange 事件:当浏览器地址栏中 # 号后面的部分发生改变时,这个事件就会被触发。例如,从 .../index.html 变为 .../index.html#welcome-post

  • 功能: 监听 URL hash 的变化,一旦变化就执行括号里的代码。

第 2 行: var post = ...

这是最核心、也是漏洞所在的一行。我们从里到外看:

  • window.location.hash: 获取 URL 中从 # 开始的部分 (例如: #welcome-post)。

  • .slice(1): 去掉第一个字符(也就是 # 号),得到 welcome-post

  • decodeURIComponent(...): 将 URL 编码的字符解码回原始字符(例如,%20 会变回空格)。这就是不安全的数据来源 (Source)

  • 'section.blog-list h2:contains(" ... ")': 这是一个 jQuery 选择器字符串。它的意思是:

    “请找到 class 为 blog-list<section> 元素内部的、包含了特定文本的 <h2> 标签。”

  • 整个这行代码的作用是,将从 URL 中获取的、用户可控的文本,直接拼接成一个 jQuery 选择器,然后用这个选择器去页面上寻找元素。这就是危险操作 (Sink)

第 3 & 4 行: if (post.get(0)) { ... }

  • post.get(0): 检查 jQuery 是否真的找到了至少一个匹配的元素。

  • scrollIntoView(): 如果找到了,就调用这个浏览器原生函数,将页面滚动到该元素,使其在用户视野内可见。

2.总结与漏洞分析

代码的正常功能

当用户访问 https://[网址]/#Some-Post-Title 时,这段代码会找到页面上标题为 “Some Post Title” 的 <h2> 标签,并自动把页面滚动到这个标题的位置,方便用户阅读。

漏洞所在

代码完全信任了来自 location.hash 的用户输入,并将其直接拼接到 jQuery 选择器字符串中。攻击者可以构造一个恶意的 hash 值,这个值在拼接后不再是一个有效的选择器,而是一段可以执行的 HTML 代码。

例如,一个攻击载荷可能是:

#")<img src=x οnerrοr=print()>

当这个 hash 被代码处理时,第 2 行构造出的选择器字符串会变成:

$('section.blog-list h2:contains("")<img src=x οnerrοr=print()>")')

jQuery 在解析这个混乱的字符串时,会先执行前面的选择器 ...:contains(""),然后它会看到后面的 <img ...> 标签,并尝试创建这个 HTML 元素。创建过程中,图片因 src=x 加载失败,从而触发 onerror 事件,执行了恶意的 print() 函数,导致 XSS 攻击成功。

直接拼接到URL后确实可以,但是通常用户不会自己去改到这个参数,所以需要攻击者在自己服务器上修改 hash ,通过钓鱼邮件、社交工程、论坛发帖等方式,将这个恶意页面 URL 发给受害者,受害者访问后,浏览器加载恶意页面,这也是本题通关的方式。

注意:

此时我们尝试在url后面修改 hash (需要注意进入网页时并没有 hash ,所以在第一次在url后面加上#hash 不会触发hasnchange事件,也就是不会滚动到指定位置;而在我们第二次改动 hash 时就会触发事件)。因此当我们随便在#后面加一个值,然后第二次在#后面加入我们想要滚动的位置,就会滚动到指定位置。

二、解决靶场详细步骤

我们的目标是在 hash 中放入一段能够调用 print() 函数的 HTML 代码。

关键点:和之前的靶场类似,直接用 <script> 标签可能因为安全限制而无法执行。因此,最好的方法是使用带有事件处理器的 HTML 标签。

第一步:构造攻击载荷 (Payload)

我们将使用 <img> 标签和它的 onerror 事件,因为这个方法非常可靠。

Payload:<img src=x onerror="print()">

  • <img>: 创建一个图像标签。

  • src=x: 尝试加载一个名为 x 的图片。这显然是一个无效的地址,所以图片加载一定会失败。

  • onerror="print()": 当图片加载失败时,就会触发 onerror 事件,从而执行我们预设的 print() 函数。

第二步:构建攻击 URL

你需要将上面构造的 payload 附加到靶场主页 URL 的 # 后面。

最终的攻击 URL:https://[靶场URL]/#<img src=x onerror="print()">

第三步:实施攻击

  1. 直接在浏览器中测试: 将上述 URL 粘贴到你自己的浏览器地址栏并访问。如果页面立即弹出了打印窗口,说明你的 payload 是有效的,上面漏洞漏洞所在也证明。

  2. 交付给受害者 (完成靶场)

    • 进入 PortSwigger 的靶场中的 “Go to exploit server” (进入攻击服务器) 。

    • 在攻击服务器的 body 部分,你需要创建一个能将受害者浏览器重定向到你恶意 URL 的方式。最简单的方法是使用一个 iframe

    • 在攻击服务器的 body 输入框中填入以下代码。其中,<iframe> 标签是HTML 中用来在当前页面嵌入另一个网页的标签。这里 src="https://YOUR-LAB-ID.web-security-academy.net/#" 指定了这个内嵌页面的初始 URL。这个 URL 带有一个空的 hash(#),相当于打开了目标页面,但 hash 为空;this 指向当前的 iframe 元素,给 iframe 的 src 属性追加字符串 '<img src=x οnerrοr=print()>' ;onload 是当 <iframe> 加载完内容后会执行的事件处理器(JavaScript 代码)。

      <iframe src="https://YOUR-LAB-ID.web-security-academy.net/#" onload="this.src+='<img src=x onerror=print()>'"></iframe>
    • 点击 “Deliver exploit to victim” (向受害者交付漏洞利用)。靶场的模拟受害者会访问你的攻击页面,加载这个 iframe,从而触发漏洞并调用 print() 函数,最终判定你成功解决了这个靶场。


网站公告

今日签到

点亮在社区的每一天
去签到