Remote Code Execution on Element Desktop Application using Node Integration in Sub Frames Bypass - CVE-2022-23597
– by s1r1us and TheGrandPew
During our Electron Desktop Application hacking frenzy, Pew informed me on Discord about a Desktop Application called Element in which he was able to insert an external iframe. We began examining the Element source code, which is public here, and eventually succeeded in Remote Code Execution.
Let’s dig into the details of the bug right away!
Bug #1: IFrame Injection
This is rather a feature than a bug, Element supports jitsi for conference calls, which provides options for self-hosting your own server. According to docs, conferenceDomain
query parameter can be provided to embed an self-hosting conference server. Furthermore, the doc says The url is typically something we shove into an iframe with "sandboxing"
. As, it was “sandboxed” it won’t be an issue right? right?
PoC for Iframe Injection
The following URL can be used to embed an external site named pwn.af
.
|
|
Desktop Application PoC
|
|
By using the above PoC, we can get JavaScript Execution on the Desktop App. The issue is Element Desktop Applicaiton fully enables sandbox. As you can noticed in the below script sandbox is enabled via app.enableSandbox()
, also note that nodeIntegrationInSubFrames
is not explicitly enabled which is disabled by default.
|
|
This situation is different from previous Discord bug where sandbox is not fully enabled. There are few things we can look for, if sandbox is disabled on main window.
- Check if there are any
new-window
ornavigation
misconfiguration similar to Discord bug. - Check if there are any
postMessage
issues on main frame. - Find a XSS on subdomain of the parent window(app.element.io). To perform
same-origin
spoofing similar to the challenge I gave in BSides Ahmedabad CTF. - Finally, we can look for sensitive
ipcMain
handlers on main window which can be reached through CVE-2022-29247 we reported to Electron
Now, the only option we have is four as the app is fully sandboxed.
Bug? #2: Finding Remote Code Execution Sinks on Desktop App
After grepping for ipcMain.on
and ipcMain.handle
we came across to an interesting IPC handler defined to open user Downloaded files.
|
|
And, this is exposed to Main Window parent frame using preload scripts contextBridge as below.
|
|
So, by sending an following IPC from the main frame, we can achieve Remote Code Execution on Element Desktop.
|
|
Now, Let’s consider our options on how to get access to electron.send
from the iframe which we have XSS on.
- Get an XSS on Main window and access
electron.send
directly. - Use CVE-2022-29247 nodeIntegrationInSubFrames and get access to
electron.send
in our iframe.
We audited Main Window JavaScript for XSS sinks, we couldn’t find anything interesting. So, we decided to use second option which seems to be easily achieved as the Element Desktop is using an old version of Electron.
|
|
What is nodeIntegrationInSubFrames?
It is important to understand what nodeIntegrationSubFrames
is clearly, from the official Electron Documentation the nodeIntegrationInSubFrames
webPreference
is defined as follows.
nodeIntegrationInSubFrames: Experimental option for enabling Node.js support in sub-frames such as iframes and child windows. All your preloads will load for every iframe, you can use process.isMainFrame to determine if you are in the main frame or not.
The important thing to note in the above statement for our exploit is that the nodeIntegrationSubFrames
enables preloads in iframes, in other words it exposes contextBridge
APIs to the iframes and child windows. Which is what we exactly wanted to get access to electron.send
exposed by the Element Desktop Main window preload JS.
The situation we have can be described with the below picture.
As you, can see our frame doesn’t have access to electron.send
API.
Bug 3: Renderer Exploit to Enable nodeIntegrationInSubFrames CVE-2022-29247
Electron adds Electron-specific WebPreferences such as node_integration
, context_isolation
and node_integration_in_subframes
by patching the blink WebPreferences
. These preferences then later used to check if the specific RenderFrame
(a web frame) has access to Electron specific features such Node APIs, preload scripts, contextBridge and so on.
|
|
Let’s just concentrate on node_integration_in_sub_frames
which is needed for our Element RCE, the other WebPreferences exploitations will be described in coming blogs.
The decision to either allow preloads in child frames(RenderFrame
s) takes place in ElectronRenderFrameObserver:DidInstallConditionalFeatures
which is done in the same Renderer process instead of the Browser process.
|
|
As the check is done in the renderer process, using a renderer exploit the setting can be flipped which effectively enables nodeIntegrationInSubFrames
.
The only thing which is left is to write an exploit which flips the render_frame->GetBlinkPreferences().node_integration_in_sub_frames
somehow which is the hardest part for me.
Exploit Development with v8 exploit CVE-2021-37975
I decided to use CVE-2021-37975 to exploit the issue. Having not so much experience in exploit development, this was the very tiring and interesting part for me. Fun fact, I didn’t know nothing about v8 binary exploitation before our research and somehow was able to learn basic v8 exploitation thanks to my CTF mate ptr-yudai 😌. Even though, we usually use an public v8 exploit its not as easy as running it and popping the calculator. The hardest part I faced during this exploit writing is finding render_frame_
offset from window
object as it was not stable usually because of using hardcoded offsets dumbly. I used to spend days in lldb
to understand the v8 bug and find offsets to blink WebPreferences, but the popping calculator in the end made it worth doing.
Anywho, after trying for 2 days I was able to pull off full exploit. The following snippet shows the offset to render_frame->GetBlinkPreferences().node_integration_in_sub_frames
. You can find the full exploit in the end of the writeup.
|
|
And finally, after enabling the nodeIntegrationInSubFrames we just need to create a same-origin RenderFrame
which will have access to electron.send
🔥.
|
|
PoC
The final exploit looks like as below.
Here is the nice PoC which pop the calculator.
Want to secure your electron or JS Application? Reach out us at [email protected] or visit https://hacktron.ai to learn more
Here is the full exploit to get RCE on Element.
|
|