Arc Browser UXSS, Local File Read, Arbitrary File Creation and Path Traversal to RCE
After announcing the bug bounty program I downloaded the browser and started looking for vulnerabilities, using my reverse engineering skills I looked at the browser binary
I found some interesting endpoints
Opening the URL inside the browser
It’s a nice UI for creating boosts which is just extensions with some more custom configuration, There is multiple types of boosts you can create and edit them inside there, the boosts will also be installed automatically as an extension, now let’s look at any code inside there and available paths
The /play endpoint was interesting so I started to look at it, let’s visit arc://boost/play/test
Looking at the code
The value is taken from the URL then it’s decompressed using the decompressFromEncodedURIComponent() function (Let’s say it’s just like Base64) then passed to JSON.parse() later it will load the boost
The compression mechanism luckily it also had compressToEncodedURIComponent() function to test out our payloads, now let’s create a simple Object to test
We run into another problem that the JSON must be first an array, let’t fix that u=’[{“x”:”y”}]’
Finally we got something working this looks like a lot of work creating a boost config let’s create a boost from the UI and add a breakpoint on the code to see the configuration
The configuration for a replace boost consist of 3 objects which they are 3 files, content.js is the typical Content Script of an extension, boost.config.json is the settings for the boost which contain information of the boost such as name, it’s scope and description this info is later used by the UI, and lastly manifest.json is just Manifest. Let’s make our first boost
Nice we have it working let’s Click Install Boost
The boost will get installed and a new folder with all the 3 files is created inside our Library files, playing with parameters I found out that the file boost.config.json is used just by the UI and Arc to display information about the boost and every permission and access is declared inside manifest.json meaning we can spoof the UI to show an innocent looking boost that will just change background color of example.com and inside the manifest we declare all the permissions and have access to everything.
This itself a big vulnerability that allows us to spoof the boosts UI and install malicious boosts with access to everything even file:// URI meaning not just UXSS but also local file read which is not available to normal extensions but I saw that when we install an extension with full permission to all URLs it will have access to all files on the system
I still wanted to show more impact and get RCE on the machine so let’s rewind a little and look for other stuff.
When we set the configuration array it has 2 interesting values inside each object which is name and path, as an attacker the first thing that comes to our mind is trying path traversal so let’s try it and change both too ../styles.css and install the boost
Do you see what I see :) our file is created inside the parent directory of the extension folder meaning using ../ the file is back traversed and we can create arbitrary files with arbitrary content and paths.
Playing with this I found out that we can also override files and display our full content for example overriding /etc/passwd to break the machine or override binary executable files to get code execution when running or other .py and .js files which is used by another program
I didn’t want to break my machine while testing so I used a safer path which is LaunchAgent it’s a folder with bunch of plist configs that will run when the user logs in or the system starts, now let’s create malicious plist file that will download an image, sets it to victim wallpaper, open terminal with bunch of commands and the classic Calculator
Inserting into the UI with the compression and stuff along with the path traversal so it will be inserted into LaunchAgents folder
Clicking install
Nice, we have inserted our malicious plist then when the system restarts we have our code execution
We got we want but there is still one issue which is how to send the arc:// uri to the victim and he clicks on Install, The URI is special and can’t be navigate to it with normal HTTP navigation the user must paste into the address bar and presses enter or any other component inside the browser which have a higher permission.
We can create an extension and upload it to Chrome webstore which has permission to tabs and navigate to the URI using chrome.tabs.create then when the user clicks install we have our code execution this is also requires much user interaction and I didn’t like it.
While browsing Arc I saw a nice feature called Easels which allow us to embed other sites inside a whiteboard and share it with others, the embed functionality is somehow like iframes but it allows us to embed every page inclusing arc:// let’s try it
We can share the URL which is like this https://arc.net/e/ED548B06-…. Victim clicks the frame then Install boost meaning only 2 clicks required and to mention that inside the UI it will say this boost will only change contents of example.com not knowing it has full permissions. This diagram sums up our attack
Video POC:
Later I found out this endpoint arc://boost was used for creating and installing boosts in first version but the /play endpoint is not mentioned anywhere maybe it was deprecated or never meant for public use.
I also found an easier way to get the special URI open in victim browser which will minimize our attack to one click user interaction, victim visits attacker.com -> redirect to arc://boost/play/… -> Click install -> pwn
October 7 — Report sent
October 10 — Patched by removing the legacy boost builder
October 10–10k bounty awarded
Thanks