Published in


The tale of CVE-2021–34479 (VSCode XSS)

This April, I finally decided to take some time to study the Electron framework and the security considerations around it. After learning the basics, building a sample application myself, and reading a ton of old CVE write-ups, I decided it was time to assess a real-world project with my recently learned skills.

Visual Studio Code has been my editor of choice for a while now. It's fast, customizable, intuitive, has a massive collection of useful extensions, and is built using the Electron framework. I had to choose a target, so why not choose an app I was already pretty familiar with?

Electron is a framework that allows a developer to write desktop applications using JavaScript. As with any JavaScript application, Electron apps can be vulnerable to Cross-Site Scripting (XSS). If an attacker can inject arbitrary JavaScript code into an Electron application, this can potentially escalate to a remote code execution (RCE) vulnerability. To prevent this scenario, Electron provides secure-by-default configurations that prevent its renderer processes from accessing the NodeJS libraries. More specifically, its nodeIntegration flag.

I started my code review process by cloning the vscode project from Github and searching for places where the nodeIntegration option was enabled.

Figure 1: nodeIntegration flag enabled

The nodeIntegration value in window.ts seemed to be controlled by a command-line argument, and I could not wrap my head around how sharedProcess.ts was used at the time. Thus, I went for an empirical approach and decided to look for XSS vulnerabilities right away. As expected, the vscode development team does an excellent job when it comes to preventing XSS. At the time, almost every possible exploitation scenario I could think of seemed to have a control already in place. I tried different threat profiles. For example, could an adversary publish a malicious extension whose details page contains untrusted Javascript?

Extension details page example

The answer is no. But how is that prevented? Vscode uses a simple yet effective solution. Every application page that needs to render user-provided HTML-capable code (e.g., Markdown content) will implement a restrictive Content-Security-Policy (CSP). Using the extensionEditor.ts as an example, only script tags with the correct nonce are allowed to run JavaScript.


In the case of extensionEditor.ts, to perform an XSS attack, an attacker would have to publish an extension whose details markdown contained a script tag with a nonce that matched the UUID generated by vscode. Synchronizing this attack is almost impossible. Therefore, although UUID should not be considered cryptographically secure, it is an effective solution for this specific case.

I continued my code review process, and eventually, the extensions/markdown-language-features/src/features/previewContentProvider.ts file caught my eye. Vscode allows you to edit and preview Markdown files in real-time, as shown in the picture below.

Markdown preview sample

The previewContentProvider.ts provideTextDocumentContent method is responsible for building the HTML content displayed on the Markdown preview WebView. The code makes use of CSP to prevent arbitrary JavaScript execution. The CSP uses a nonce generated by the following piece of code.

nonce generation snippet

As can be seen, the generated nonce is also not cryptographically secure. Furthermore, the concatenation of getMilliseconds() adds no entropy to the nonce, as its results will match the last three characters of getTime() in virtually all cases. Therefore, if an attacker can constantly populate a Markdown file with script blocks with every possible nonce candidate for the next few seconds, a victim who previews the file using the built-in Visual Studio Code Markdown extension would be vulnerable to XSS.

For a proof-of-concept (PoC), I used the following NodeJS script to generate the malicious Markdown file.

I also used the following bash one-liner to constantly update the Markdown file with nonces for the next 2 seconds every 4 seconds. Generating too many nonces would freeze vscode, so I had to find a good balance between update interval and nonce count.

The following video demonstrates how to use the before-mentioned scripts to replicate the issue.

In summary, arbitrary JavaScript execution was possible if an attacker had control over a Markdown file previewed by a victim. Unfortunately, the Markdown renderer process is sandboxed, and I couldn't find a way to access NodeJS libraries. Thus, as of now, this is just a simple XSS, and there is nothing much you can do to exploit a Desktop user unless you can find a sandbox bypass (let me know if you do 😉). Nonetheless, the vulnerability probably still applied to the VSCode web version. I say probably cause I was never able to make the web version run locally. Furthermore, I wasn't aware of any web applications like at the time.

I reported the issue to Microsoft, and a fix was released as part of the July Patch Tuesday (2021). CVE-2021-34479 was assigned to the vulnerability.

Although this is not as exciting as an RCE, I learned a lot about Electron during the code review process and had the chance to exploit an interesting CSP bypass to achieve XSS. I wonder how widespread this type of vulnerability is, but that is a subject for another time.

Do Follow Techiepedia for more Interesting write-ups.



Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Daniel Santos

Daniel Santos

Security researcher and penetration tester