Man
Professional
- Messages
- 3,222
- Reaction score
- 804
- Points
- 113
Hello!
My name is Vladislav Kormishkin, I am a cybersecurity threat research analyst at R-Vision. Today I would like to tell you about one of the most popular code editors — VSCode, and how attackers can use it for their own purposes. To date, 229 million extension installations through the built-in store are known to contain malicious code. Vulnerabilities in code editors have been used for a long time, including APT (Advanced Persistent Threat) using social engineering, as was the case with Visual Studio.
In this article, we will look at the architectural features of VSCode related to code execution security. We will also analyze the vulnerability CVE-2023-46944 and explain why, despite the fact that the developer GitKraken patched it in 2023, it can still be potentially dangerous due to the peculiarities of working with Git. In addition, we will explain how exactly this vulnerability was patched and propose a rule for its detection using the VRL language and the R-Object plugin.
VSCode Workspace Trust is a security feature introduced in Visual Studio Code in 2021. It helps prevent malicious code and potential vulnerabilities from running from an untrusted environment. An untrusted environment is a directory that has been opened for the first time or for which no trust type has been defined. You can select it in the pop-up window:
Workspace Trust popup when opening a new directory
In a trusted workspace, all application features and extensions are available, while in an untrusted workspace, some features and extensions may not be available. The restrictions apply to extensions that interact with the file system, run scripts, or access network resources. To prevent unintentional execution of potentially dangerous code, debugging, task launching, and terminal commands will be restricted in untrusted workspaces. The idea is that by marking an area as untrusted, you can inspect all the code in it and set the value to trusted after inspection.
Sounds a bit idealistic, doesn't it?
Let's take a closer look at how our vulnerability is related to this.
The first two types of settings either allow the use of extensions in untrusted environments or block their operation. The third type of settings provides the ability to limit the use of extensions. This type is specified in the project file package.json. In this case, certain parameters can be automatically limited using the property RestrictedConfigurations, where the Setting ID can be specified in the array, and this parameter will be limited in an untrusted environment:
Setting ID Example
The GitLens extension does not define such properties, but the screenshot shows that the limited type is supported, which means that some of the functionality will work in untrusted environments:
package.jsonGitlens Repository File
GitLens versions prior to 14.0.0 lacked a check for being in an untrusted environment. When opening a repository, the extension would contact Git to initiate the check, allowing malicious code to be run. Starting with Git 2.37.0, an interesting feature was added core.fsmonitor. If we access the documentation using Web Archive and look at the feature description for 2021, we see the following:
Git-config page in web.archive
It is assumed that the variable fsmonitorcan contain commands.
What is the function for core.fsmonitorand how to use it? To do this, let's figure out what Git config is, since it contains the function fsmonitor.
Git config is a configuration file for Git that stores settings specific to a particular repository. It is created when the repository is initialized git initand can be edited manually or using commands. Git supports configuration from three sources, and each level supersedes the previous one: system /etc/gitconfig, global ~/.gitconfig, and local repository .git/config. The file we are interested in at this point is .git/config.
The function fsmonitorallows you to use indexing when calling git status, git add, git diffand other commands that require information about files in the local repository, so as not to go through all the files. This significantly speeds up Git's work with large repositories. In fsmonitoryou can either write a type value booleanto enable or disable the function, or specify an executable file. To run an arbitrary file (the calculator in the example below), .git/configthe file should look like this:
Let's see how our vulnerability works.
Demonstration of running a calculator in VSCode. Vulnerability CVE-2023-46944.
The question arises, how does the command run from fsmonitor? When you open a workspace with files and folders in VSCode, Git tries to determine whether this area is a repository. And if it finds familiar folders and files in it, such as .git, HEADor config, then it tries to initialize it and check if there are changes in the open repository. That is, when you open a directory in VSCode (in our case, clicking on the file README.md), commands are launched to synchronize with the remote repository.
Let's see how this would look in events using malicious commands, not just running the calculator. For this, we'll take a Fetch Payload type, since payloads that don't use commands to run don't work in this case. In the file, .git/configit looks like this:
<payload>The utility launcher is used (curla command line tool for transferring data over various network protocols).
Let's analyze the sequence of events using R-Vision SIEM:
Event from R-Vision SIEM interface
Here we are interested in the selected command ls-files -- README.md, its execution triggers the function call fsmonitor. Similar events occur in Windows.
We see that the parent process for reading README.mdfrom Git is the process code:
Parent process for Git
The payload launch event looks like this:
Payload launch
spidThe (means parent process) field contains the value dpid(PID of the current process) from the Git startup event, which means the startup /bin/shis a child process of Git. Now we understand that this looks like a chain of processes.
This is a 2023 vulnerability, and you might think that since all extensions are updated by default in VSCode, there is nothing to worry about. However, this is not entirely true. Let's see how things are going with fixing this vulnerability.
An exported class has been added to the src/git/errors.tsWorkspaceUntrustedError file. It represents an error type that can be thrown and caught specifically to handle cases where Git operations are not allowed due to an untrusted workspace:
Adding error handling to close the vulnerability
The class WorkspaceUntrustedErroris also used in src/env/node/git/git.ts. This file is responsible for the interaction of the GitLens extension with Git itself. In the class Gitthat is used to process and run commands, a check for the type of workspace has been added. The check is performed using the VSCode API function isTrusted, which is used in extensions with the value limited. At the beginning of the article, we learned that the value limitedallows you to implement the operation of individual extension functions in an untrusted environment (more details in the documentation).
Added workspace type check
The src/git/gitProviderService.ts file abstracts interaction with Git, providing a consistent API for the rest of the GitLens extension and interaction with repositories, regardless of the specific implementation or environment. The following checks have been added:
Added checks for changing the workspace type to trusted
The checking algorithm is as follows:
Empty functionemptyDisposable
So, GitLens added a check for the type of active Workspace. Well, it seems like all the checks are added. But how are things now?
If we try to open our repository with malicious code in the updated GitLens, the code will indeed not run, but only if we select an untrusted environment. If we select the environment as trusted, the code will run successfully.
Let's be honest, how often do you check all the files in a repository for suspicious strings when downloading it? If you use git clone <repository>, you won't download .git/configthe file and most likely won't encounter this particular vulnerability. But what if it's a repository that was downloaded from another source?
Let's open a malicious repository in VSCode without the GitLens plugin, but in a trusted environment:
Demonstration of Reverse-Shell launch
We have malicious code running again:
The demonstrated PoC uses a similar command to run the code using PowerShell.
File contents .git/config:
Let's build a detection of the following chain of events:
Using R-Vision SIEM as an example, let's see how our detection rules detect such malicious activity:
Correlation Rule for Windows
Correlation Rule for Linux
We found that if malicious repositories are opened in a trusted environment, the patch will not help, since this activity is related to the functionality of the integrated Git extension. Correlation rules based on the chain of events can be used to track it.
Unfortunately, there is no simple solution to fully protect yourself in this situation. However, we can highlight the main recommendations:
I will be glad if the article was useful for you! Ask questions and write comments.
Source
My name is Vladislav Kormishkin, I am a cybersecurity threat research analyst at R-Vision. Today I would like to tell you about one of the most popular code editors — VSCode, and how attackers can use it for their own purposes. To date, 229 million extension installations through the built-in store are known to contain malicious code. Vulnerabilities in code editors have been used for a long time, including APT (Advanced Persistent Threat) using social engineering, as was the case with Visual Studio.
In this article, we will look at the architectural features of VSCode related to code execution security. We will also analyze the vulnerability CVE-2023-46944 and explain why, despite the fact that the developer GitKraken patched it in 2023, it can still be potentially dangerous due to the peculiarities of working with Git. In addition, we will explain how exactly this vulnerability was patched and propose a rule for its detection using the VRL language and the R-Object plugin.
VSCode Security Features
Visual Studio Code (VSCode) is a powerful and popular integrated development environment (IDE) built on the Electron platform. It combines the capabilities of Node.js and the Chromium browser, giving developers additional flexibility. VSCode inherits the process model from Electron, separating functionality into different processes for increased security. Thus, rendering processes running in the built-in Chromium browser have limited privileges, while the main process running on Node.js has elevated privileges and can directly interact with the operating system. Despite the extensive open source code (almost 800 thousand lines of code), official builds from Microsoft contain proprietary components.VSCode Workspace Trust is a security feature introduced in Visual Studio Code in 2021. It helps prevent malicious code and potential vulnerabilities from running from an untrusted environment. An untrusted environment is a directory that has been opened for the first time or for which no trust type has been defined. You can select it in the pop-up window:

Workspace Trust popup when opening a new directory
In a trusted workspace, all application features and extensions are available, while in an untrusted workspace, some features and extensions may not be available. The restrictions apply to extensions that interact with the file system, run scripts, or access network resources. To prevent unintentional execution of potentially dangerous code, debugging, task launching, and terminal commands will be restricted in untrusted workspaces. The idea is that by marking an area as untrusted, you can inspect all the code in it and set the value to trusted after inspection.
Sounds a bit idealistic, doesn't it?
Let's take a closer look at how our vulnerability is related to this.
GitLens Vulnerability (CVE-2023-46944)
In 2023, a vulnerability CVE-2023-46944 was discovered that uses the GitLens extension to run malicious code from an untrusted environment. But how does this happen? In the documentation, we see that VSCode extensions can have three types of work with environments - true, falseand limited:
Code:
capabilities:
untrustedWorkspaces:
{ supported: true } |
{ supported: false, description: string } |
{ supported: 'limited', description: string, restrictedConfigurations?: string[] }
The first two types of settings either allow the use of extensions in untrusted environments or block their operation. The third type of settings provides the ability to limit the use of extensions. This type is specified in the project file package.json. In this case, certain parameters can be automatically limited using the property RestrictedConfigurations, where the Setting ID can be specified in the array, and this parameter will be limited in an untrusted environment:

Setting ID Example
The GitLens extension does not define such properties, but the screenshot shows that the limited type is supported, which means that some of the functionality will work in untrusted environments:

package.jsonGitlens Repository File
GitLens versions prior to 14.0.0 lacked a check for being in an untrusted environment. When opening a repository, the extension would contact Git to initiate the check, allowing malicious code to be run. Starting with Git 2.37.0, an interesting feature was added core.fsmonitor. If we access the documentation using Web Archive and look at the feature description for 2021, we see the following:

Git-config page in web.archive
It is assumed that the variable fsmonitorcan contain commands.
What is the function for core.fsmonitorand how to use it? To do this, let's figure out what Git config is, since it contains the function fsmonitor.
Git config is a configuration file for Git that stores settings specific to a particular repository. It is created when the repository is initialized git initand can be edited manually or using commands. Git supports configuration from three sources, and each level supersedes the previous one: system /etc/gitconfig, global ~/.gitconfig, and local repository .git/config. The file we are interested in at this point is .git/config.
The function fsmonitorallows you to use indexing when calling git status, git add, git diffand other commands that require information about files in the local repository, so as not to go through all the files. This significantly speeds up Git's work with large repositories. In fsmonitoryou can either write a type value booleanto enable or disable the function, or specify an executable file. To run an arbitrary file (the calculator in the example below), .git/configthe file should look like this:
Code:
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
fsmonitor = calc.exe
Let's see how our vulnerability works.
Exploitation of vulnerability
Let's check what happens if we run illegitimate code in VSCode 19.0 using the GitLens 13.6.0 extension, as described in the PoC for CVE-2023-46944:
Demonstration of running a calculator in VSCode. Vulnerability CVE-2023-46944.
The question arises, how does the command run from fsmonitor? When you open a workspace with files and folders in VSCode, Git tries to determine whether this area is a repository. And if it finds familiar folders and files in it, such as .git, HEADor config, then it tries to initialize it and check if there are changes in the open repository. That is, when you open a directory in VSCode (in our case, clicking on the file README.md), commands are launched to synchronize with the remote repository.
Let's see how this would look in events using malicious commands, not just running the calculator. For this, we'll take a Fetch Payload type, since payloads that don't use commands to run don't work in this case. In the file, .git/configit looks like this:
Code:
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
fsmonitor = "powershell -w hidden -nop -e <payload> #
<payload>The utility launcher is used (curla command line tool for transferring data over various network protocols).
Event Analysis
When you open a file, a series of commands are executed to check for changes in the open repository.Let's analyze the sequence of events using R-Vision SIEM:

Event from R-Vision SIEM interface
Here we are interested in the selected command ls-files -- README.md, its execution triggers the function call fsmonitor. Similar events occur in Windows.
We see that the parent process for reading README.mdfrom Git is the process code:

Parent process for Git
The payload launch event looks like this:

Payload launch
spidThe (means parent process) field contains the value dpid(PID of the current process) from the Git startup event, which means the startup /bin/shis a child process of Git. Now we understand that this looks like a chain of processes.
This is a 2023 vulnerability, and you might think that since all extensions are updated by default in VSCode, there is nothing to worry about. However, this is not entirely true. Let's see how things are going with fixing this vulnerability.
Patching
The patch for the vulnerability can be found in this commit. The changes to the project were made in build 14.0.0. To eliminate the vulnerability, additional checks for being in an untrusted environment were added — Untrusted Workspace. Earlier in the article, we found out that Gitlens did not have a check for the environment type, and the extension launched Git to initialize the repository and perform file indexing operations.An exported class has been added to the src/git/errors.tsWorkspaceUntrustedError file. It represents an error type that can be thrown and caught specifically to handle cases where Git operations are not allowed due to an untrusted workspace:

Adding error handling to close the vulnerability
The class WorkspaceUntrustedErroris also used in src/env/node/git/git.ts. This file is responsible for the interaction of the GitLens extension with Git itself. In the class Gitthat is used to process and run commands, a check for the type of workspace has been added. The check is performed using the VSCode API function isTrusted, which is used in extensions with the value limited. At the beginning of the article, we learned that the value limitedallows you to implement the operation of individual extension functions in an untrusted environment (more details in the documentation).

Added workspace type check
The src/git/gitProviderService.ts file abstracts interaction with Git, providing a consistent API for the rest of the GitLens extension and interaction with repositories, regardless of the specific implementation or environment. The following checks have been added:

Added checks for changing the workspace type to trusted
The checking algorithm is as follows:
- Initially, any workspace is untrusted unless previously selected otherwise.
- The expression !workspace.isTrustedis used to check whether the workspace belongs to an untrusted area.
- The function onDidGrantWorkspaceTrustwaits for the workspace trust state to change.
- If the workspace is trusted and contains folders to initialize the repository, it initiates repository discovery.
- If the workspace is trusted and there are no folders in the workspace to initialize the repository, it returns a value emptyDisposablethat does not cause any operations and is a stub.

Empty functionemptyDisposable
So, GitLens added a check for the type of active Workspace. Well, it seems like all the checks are added. But how are things now?
If we try to open our repository with malicious code in the updated GitLens, the code will indeed not run, but only if we select an untrusted environment. If we select the environment as trusted, the code will run successfully.
Let's be honest, how often do you check all the files in a repository for suspicious strings when downloading it? If you use git clone <repository>, you won't download .git/configthe file and most likely won't encounter this particular vulnerability. But what if it's a repository that was downloaded from another source?
Let's open a malicious repository in VSCode without the GitLens plugin, but in a trusted environment:

Demonstration of Reverse-Shell launch
We have malicious code running again:
- on the left you can see that the launch occurs when you select an untrusted environment in the pop-up window;
- after opening the file, the command line README.mdis launched in the lower right window sh.exewith powershell.exe;
- in the upper right window the reverse-shell is obtained, in this case cmd/windows/http/x64/shell_reverse_tcp.
The demonstrated PoC uses a similar command to run the code using PowerShell.
File contents .git/config:
Code:
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
fsmonitor = "powershell -w hidden -nop -e <payload> #"
Detection
Since there is no simple solution to prevent this activity, you can use the correlation rules for Windows and Linux, which are built on the detection of the chain of events given earlier.Let's build a detection of the following chain of events:
- The first event is the launch of VSCode. It is the parent of the Git process.
- The second event is running Git with the file listing command ls-files.
- The third event is the launch of the shell shwith the execution of commands through -c.
Using R-Vision SIEM as an example, let's see how our detection rules detect such malicious activity:
Windows
Correlation rule for Windows written in R-Object language in R-Vision SIEM:Correlation Rule for Windows
Code:
id: e65f4f66-e0ee-4eef-8d98-513d184fae84
name: Exploitation of vulnerability CVE-2023-46944 in GitLens extension for VSCode on Windows
version: 1.0.0
date: 2024-05-29
author: Vladislav Kormishkin, R-Vision
status: stable
type: correlation_rule
severity: high
description: The rule is triggered when opening a Git repository in VSCode, both in a trusted and untrusted environment, where a malicious command is specified in the fsmonitor function in the .git/config file. This activity applies to a vulnerable version of the GitLens extension up to and including 13.6.0, it has been assigned the number CVE-2023-46944. This activity can also occur when opening a malicious Git repository in a trusted environment, but with a patched version of GitLens or without the extension installed at all, in editor versions older than 1.63.1.
reference:
- https://packetstormsecurity.com/files/178227/GitLens-Git-Local-Configuration-Execution.html
tags:
- Execution
- attack.T1203
data_source:
- Windows
- Security
- EventID_4688
- Sysmon_Operational
- EventID_1
known_false_positives:
- "It's still unknown"
group_by:
- dvchost
filter: !vrl |
.dvendor == "Microsoft" &&
includes(["1", "4688"], .externalId)
aliases:
event_code:
filter: !vrl |
oldFileName = downcase(to_string(.oldFileName) ?? "-")
sproc = downcase(to_string(.sproc) ?? "-")
flag = false
if ends_with(sproc, "\\code.exe") ||
oldFileName == "electron.exe" {
flag = true
}
flag
event_git:
filter: !vrl |
cmd = downcase(to_string(.cmd) ?? "-")
oldFileName = downcase(to_string(.oldFileName) ?? "-")
dproc = downcase(to_string(.dproc) ?? "-")
sproc = downcase(to_string(.sproc) ?? "-")
flag = false
if (ends_with(sproc, "\\git.exe") ||
oldFileName == "git.exe") &&
contains(cmd, "ls-files") {
flag = true
}
flag
event_sh:
filter: !vrl |
cmd = downcase(to_string(.cmd) ?? "-")
oldFileName = downcase(to_string(.oldFileName) ?? "-")
dproc = downcase(to_string(.dproc) ?? "-")
sproc = downcase(to_string(.sproc) ?? "-")
flag = false
if ((ends_with(sproc, "\\git.exe") ||
oldFileName == "git.exe") &&
ends_with(dproc, "\\sh.exe") &&
starts_with(cmd, "sh -c")) {
flag = true
}
flag
select:
alias: event_code
join:
alias: event_git
on:
- eq: {event_code: .dpid, event_git: .spid}
join:
alias: event_sh
on:
- eq: {event_git: .dpid, event_sh: .spid}
ttl: 60
on_correlate: !vrl |
. |= compact({
"rt" : %event_sh.rt,
"dvendor" : %event_sh.dvendor,
"dversion" : %event_sh.dversion,
"dhost" : %event_sh.dhost,
"dproc" : %event_sh.dproc,
"dvchost" : %event_sh.dvchost,
"oldFilePath" : %event_sh.oldFilePath,
"duser" : %event_sh.duser,
"suser" : %event_sh.suser,
"sntdom" : %event_sh.sntdom,
"sproc" : %event_sh.sproc,
"accessMask" : %event_sh.accessMask,
"externalId" : %event_sh.externalId,
"oldFileName" : %event_sh.oldFileName,
"fname": %event_sh.fname,
"dntdom" : %event_sh.dntdom,
"cmd" : %event_sh.cmd,
"sourceServiceName" : %event_sh.sourceServiceName,
})
.msg = "On node " + (to_string(.dvchost) ?? "-") + " user " + (to_string(.duser) ?? "-") + " domain " + (to_string(.dntdom) ?? "-") + " from Git process with parent VSCode process the command " + (to_string(.cmd) ?? "-") + " was run, this behavior may indicate exploitation of vulnerability CVE-2023-46944"
Linux
Correlation rule for Linux written in R-Object language in R-Vision SIEM:Correlation Rule for Linux
Code:
id: 62697bd1-1731-4437-bc31-572815389f29
name: Exploitation of CVE-2023-46944 vulnerability in GitLens extension for VSCode on Linux
version: 1.0.0
date: 2024-05-30
author: Vladislav Kormishkin, R-Vision
status: stable
type: correlation_rule
severity: high
description: The rule is triggered when opening a Git repository in VSCode, both in a trusted and untrusted environment, where a malicious command is specified in the fsmonitor function in the .git/config file. This activity applies to a vulnerable version of the GitLens extension up to and including 13.6.0, it has been assigned the number CVE-2023-46944. This activity can also occur when opening a malicious Git repository in a trusted environment, but with a patched version of GitLens or without the extension installed at all, in editor versions older than 1.63.1.
reference:
- https://packetstormsecurity.com/files/178227/GitLens-Git-Local-Configuration-Execution.html
tags:
- Execution
- attack.T1203
data_source:
- Linux
- Auditd
- Execve
known_false_positives:
- "It's still unknown"
group_by:
- dvchost
filter: !vrl |
.dvendor == "Linux" &&
.cs4 == "execve"
aliases:
event_code:
filter: !vrl |
dproc = downcase(to_string(.dproc) ?? "-")
flag = false
if ends_with(dproc, "/code/code") {
flag = true
}
flag
event_git:
filter: !vrl |
cmd = downcase(to_string(.cmd) ?? "-")
dproc = downcase(to_string(.dproc) ?? "-")
flag = false
if ends_with(dproc, "/bin/git") &&
contains(cmd, "ls-files") { #listing files
flag = true
}
flag
event_sh:
filter: !vrl |
filePath = downcase(to_string(.filePath) ?? "-")
dproc = downcase(to_string(.dproc) ?? "-")
cmd = downcase(to_string(.cmd) ?? "-")
flag = false
if contains(filePath, "/bin/sh") &&
contains(cmd, "-c"){
flag = true
}
flag
select:
alias: event_code
join:
alias: event_git
on:
- eq: {event_code: .spid, event_git: .dvcpid}
join:
alias: event_sh
on:
- eq: {event_git: .spid, event_sh: .dvcpid}
ttl: 60
on_correlate: !vrl |
. |= compact({
"rt" : %event_sh.rt,
"dvendor" : %event_sh.dvendor,
"dversion" : %event_sh.dversion,
"dhost" : %event_sh.dhost,
"dproc" : %event_sh.dproc,
"dvchost" : %event_sh.dvchost,
"oldFilePath" : %event_sh.oldFilePath,
"duser" : %event_sh.duser,
"suser" : %event_sh.suser,
"sntdom" : %event_sh.sntdom,
"sproc" : %event_sh.sproc,
"accessMask" : %event_sh.accessMask,
"externalId" : %event_sh.externalId,
"oldFileName" : %event_sh.oldFileName,
"fname": %event_sh.fname,
"dntdom" : %event_sh.dntdom,
"cmd" : %event_sh.cmd,
"sourceServiceName" : %event_sh.sourceServiceName,
})
.msg = "On node " + (to_string(.dvchost) ?? "-") + " user " + (to_string(.suser) ?? "-") + " from Git process with parent process VSCode ran command " + (to_string(.cmd) ?? "-") + ", this behavior may indicate exploitation of vulnerability CVE-2023-46944"
Conclusion
In this article, we looked at the capabilities of Workspace Trust and ways to prevent execution of potentially malicious code. We also discussed the causes of the CVE-2023-46944 vulnerability.We found that if malicious repositories are opened in a trusted environment, the patch will not help, since this activity is related to the functionality of the integrated Git extension. Correlation rules based on the chain of events can be used to track it.
Unfortunately, there is no simple solution to fully protect yourself in this situation. However, we can highlight the main recommendations:
- check everything you open in a code editor if it was downloaded from an unknown or untrusted source;
- Disable Git integration by default.
I will be glad if the article was useful for you! Ask questions and write comments.
Source