https://ipfyx.fr/post/visual-studio-code-tunnel/
ipfyx blog
Home
About
[ ]
Blocking Visual Studio Code embedded reverse shell before it's too
late
Sep 17, 2023 * 10 min read * blueteam defense Windows Splunk SOC
Powershell
[vscode]
Overview
* Introduction
* Mitigation
+ Domains blacklist
+ Applocker
+ GPO
* Detection
+ Process
+ File creation
+ Web traffic monitoring
* Conclusion
Visual studio code tunnel
Introduction
Since July 2023, Microsoft is offering the perfect reverse shell,
embedded inside Visual Studio Code, a widely used development tool.
With just a few clicks, any user with a github account can share
their visual studio desktop on the web. VS code tunnel is almost
considered a lolbin (Living Of the Land Binary).
I am so glad that my users now have the ability to expose their
computer with highly sensitive data right on the web, through an
authentication I nor control, nor supervise. My internal network is
now accessible from anywhere !
The worse part is that this tunnel can be triggered from the cmdline
with the portable version of code.exe. An attacker just has to upload
the binary, which won't be detected by any anti-virus since it is
legitimate and singed windows binary.
It is therefore something to watch out for.
Execute it, open the link mentioned, log in to your github account
and there is your reverse shell.
1.\code.exe tunnel
2*
3* Visual Studio Code Server
4*
5* By using the software, you agree to
6* the Visual Studio Code Server License Terms (https://aka.ms/vscode-server-license) and
7* the Microsoft Privacy Statement (https://privacy.microsoft.com/en-US/privacystatement).
8*
9
10Open this link in your browser https://vscode.dev/tunnel/sauceTomate/C:/Users/ipfyx/Downloads/vscode_cli_win32_x64_cli
The binary is definitely signed :
1Get-AppLockerFileInformation 'C:\Users\ipfyx\AppData\Local\Programs\Microsoft VS Code\bin\code-tunnel.exe'|Select-Object -ExpandProperty Publisher
2PublisherName : O=MICROSOFT CORPORATION, L=REDMOND, S=WASHINGTON, C=US
3ProductName :
4BinaryName :
5BinaryVersion : 0.0.0.0
6HasPublisherName : True
7HasProductName : False
8HasBinaryName : False
Notice how there are nor ProductName nor BinaryName value, we will
use them later.
pfiatde blog post already did a great job at describing what an
attacker can do so I won't say much more. Go check his awesome blog
post.
Mitigation
But now, as a defender, how do we block or detect its usage ?
Domains blacklist
Microsoft documentation states :
1If you're part of an organization who wants to control access to Remote Tunnels, you can do so by allowing or denying access to the domain global.rel.tunnels.api.visualstudio.com.
Yeah it's not that simple. It will indeed block most users but, from
my testing, it won't block an attacker that has already established a
tunnel once. Meaning the tunnel can be kept active or restarted at
will despite this domain being blacklisted.
From my understanding, VScode contacts
global.rel.tunnels.api.visualstudio.com to get its "clusters" :
https://global.rel.tunnels.api.visualstudio.com/api/v1/clusters
Here is a sample of the resulting json :
1[
2 {
3 "clusterId": "auc1",
4 "uri": "https://auc1.rel.tunnels.api.visualstudio.com",
5 "azureLocation": "AustraliaCentral"
6 },
7 {
8 "clusterId": "aue",
9 "uri": "https://aue.rel.tunnels.api.visualstudio.com",
10 "azureLocation": "AustraliaEast"
11 },
12 etc.
13]
Those domains are also mentioned in another Microsoft documentation :
1- Dev Tunnels
2 - global.rel.tunnels.api.visualstudio.com
3 - [clusterId].rel.tunnels.api.visualstudio.com
4 - [clusterId]-data.rel.tunnels.api.visualstudio.com
5 - *.[clusterId].devtunnels.ms
6 - *.devtunnels.ms
Blocking those domains will block and cut off any VS code tunnel :
1*.tunnels.api.visualstudio.com
2*.devtunnels.ms
Applocker
Applocker is Microsoft application whitelisting technology. When
enabled and configured, with default rules for example, everything is
blocked by default except for the executables and scripts defined in
the preceding rules. Let's pretend we use Microsoft default rules for
demo purpose.
To generate them, in Group Policy Management Editor, go to Computer
Configuration -> Windows Settings -> Security Settings -> Application
Control Policies -> AppLocker. Right Click on Create Default Rules.
Then right click on AppLocker and Export Policy.
MS Applocker default rules generation MS Applocker default rules
generation
Here is the resulting xml :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
When using those rules, everything that is in Program Files and
Windows is allowed, everything else is blocked. Administrators are
allowed to execute anything, VSCode for instance. But if you were to
define a rule that blocks VSCode, this rule would apply first, since
"Deny rules" are applied before "Allowed rules".
Let's build a rule to block VScode altogether. The rule applies to
Everyone (SID S-1-1-0), therefore to Administrators (SID
S-1-5-32-544).
1Get-AppLockerFileInformation 'C:\Users\ipfyx\AppData\Local\Programs\Microsoft VS Code\Code.exe'| New-AppLockerPolicy -RuleType Publisher -User S-1-1-0 -Optimize -Xml
In the resulting xml, I replaced Action="Allow" with Action="Deny". I
also removed my current VSCode version from BinaryVersionRange to
match any VScode version.
Warning : Do not import the following xml in Applocker. Imported
alone, this rule will completely block your system. Add a Allow *
rule beforehand.
1
2
3
4
5
6
7
8
9
10
11
Included in MS default rules, here is the resulting xml (called
vscode.xml from now on) :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Unfortunately, this rule won't apply to code tunnel because this
binary doesn't have a product name (thanks Microsoft !) :
1Get-AppLockerFileInformation 'C:\Users\ipfyx\AppData\Local\Programs\Microsoft VS Code\bin\code-tunnel.exe' | New-AppLockerPolicy -RuleType Publisher -User S-1-1-0 -Optimize -Xml
2New-AppLockerPolicy : Les regles ne peuvent pas etre creees. Les informations de fichier requises sont absentes dans le fichier suivant:
3%OSDRIVE%\USERS\ipfyx\DOWNLOADS\VSCODE_CLI_WIN32_X64_CLI\CODE.EXE
To be sure, let's test the previously generated vscode rule.
1# Portable code tunnel is blocked but not by the rule
2Test-AppLockerPolicy -XmlPolicy .\vscode.xml -Path .\code.exe -User S-1-1-0|fl
3FilePath : C:\Users\ipfyx\Downloads\vscode_cli_win32_x64_cli\code.exe
4PolicyDecision : DeniedByDefault
5MatchingRule :
6
7# Code tunnel is blocked but not by the rule
8Test-AppLockerPolicy -XmlPolicy .\vscode.xml -Path 'C:\Users\ipfyx\AppData\Local\Programs\Microsoft VS Code\bin\code-tunnel.exe' -User S-1-1-0|fl
9FilePath : C:\Users\ipfyx\AppData\Local\Programs\Microsoft VS Code\bin\code-tunnel.exe
10PolicyDecision : DeniedByDefault
11MatchingRule :
12
13# VScode is blocked by the rule
14Test-AppLockerPolicy -XmlPolicy .\vscode.xml -Path 'C:\Users\ipfyx\AppData\Local\Programs\Microsoft VS Code\Code.exe' -User S-1-1-0|fl
15FilePath : C:\Users\ipfyx\AppData\Local\Programs\Microsoft VS Code\Code.exe
16PolicyDecision : Denied
17MatchingRule : VScode
The PolicyDecision is DeniedByDefault, which means the rule we just
built from the vscode binary does not apply to code-tunnel. If no
rules matches, the default behaviour for applocker is to deny any
execution. Code-tunnel is denied there, but it would not be if it was
placed in the allowed directories. It could be placed there by an
attacker or by a legitimate vscode, installed by an admin.
A solution could be to use a Hash Condition.
1# Export
2Get-AppLockerFileInformation .\code.exe | New-AppLockerPolicy -RuleType Hash -User S-1-1-0 -Optimize -Xml > .\vscode-tunnel-hash.xml
3
4# Code tunnel is blocked
5Test-AppLockerPolicy -XmlPolicy .\vscode-tunnel-hash.xml -Path 'C:\Users\ipfyx\AppData\Local\Programs\Microsoft VS Code\bin\code-tunnel.exe' -User S-1-1-0|fl
6FilePath : C:\Users\ipfyx\AppData\Local\Programs\Microsoft VS Code\bin\code-tunnel.exe
7PolicyDecision : Denied
8MatchingRule : code.exe
Warning : Do not import the following xml in Applocker. Imported
alone, this rule will completely block your system. Add a Allow *
rule beforehand
1
2
3
4
5
6
7
8
9
10
11
But this solution is nor resilient nor sustainable since the hash can
change at the first update.
GPO
Visual Studio has the so wanted features :
1- Dev Tunnels - controls test functionality
But as of today (1.8.82), VScode doesn't. You can force automatic
updates though ! UpdateMode_default would be my goto.
1gc 'C:\Users\ipfyx\AppData\Local\Programs\Microsoft VS Code\policies\en-us\VSCode.adml'
1
2
3
4
5
6
7 Visual Studio Code
8 Visual Studio Code >= 1.67
9 Update
10 UpdateMode
11 Configure whether you receive automatic updates. Requires a restart after change. The updates are fetched from a Microsoft online service.
12 Disable updates.
13 Disable automatic background update checks. Updates will be available if you manually check for updates.
14 Check for updates only on startup. Disable automatic background update checks.
15 Enable automatic update checks. Code will check for updates automatically and periodically.
16
17
18
19
20
21
Detection
Process
Looking for code-tunnel execution
A simple detection could be :
1index=win sourcetype="XmlWinEventLog" EventCode=4688 code tunnel cmdline="*code*.exe*tunnel*"
2| stats min(_time) as time_min max(_time) as time_max count as occurence values(NewProcess) as NewProcess values(ParentProcess) as ParentProcess values(CommandLine) as CommandLine by host, SubjectUser file_name
host SubjectUser cmdline file_name time_min time_max occurence NewProcess ParentProcess
"c:\Users\ipfyx\AppData\Local C:\Users\ipfyx\
\Programs\Microsoft VS Code\ AppData\Local\ C:\Windows\
sauteTomate ipfyx bin\code-tunnel" tunnel code-tunnel.exe 2023-09-18 2023-09-18 1 Programs\ System32\
--accept-server-license-terms 11:31:54 11:31:54 Microsoft VS cmd.exe
--name totallyNotSuspicious Code\
code-tunnel.exe
But the binary code.exe could be renamed. However, what an attacker
can't change are the tunnel and --accept-server-license-terms options
:
1index=win sourcetype="XmlWinEventLog" EventCode=4688 tunnel accept server license terms cmdline="*.exe*tunnel*--accept-server-license-terms*"
2| stats min(_time) as time_min max(_time) as time_max count as occurence values(NewProcess) as NewProcess values(ParentProcess) as ParentProcess values(CommandLine) as CommandLine by host, SubjectUser file_name
host SubjectUser cmdline file_name time_min time_max occurence NewProcess ParentProcess
"c:\Users\ipfyx\AppData\Local C:\Users\ipfyx\
\Programs\Microsoft VS Code\ 2023-09-18 2023-09-18 AppData\Local\ C:\Windows\
sauteTomate ipfyx bin\c0de.exe" tunnel c0de.exe 11:35:54 11:35:54 1 Programs\ System32\
--accept-server-license-terms Microsoft VS cmd.exe
--name totallyNotSuspicious Code\c0de.exe
"c:\Users\ipfyx\AppData\Local C:\Users\ipfyx\
\Programs\Microsoft VS Code\ AppData\Local\ C:\Windows\
sauteTomate ipfyx bin\code-tunnel" tunnel code-tunnel.exe 2023-09-18 2023-09-18 1 Programs\ System32\
--accept-server-license-terms 11:31:54 11:31:54 Microsoft VS cmd.exe
--name totallyNotSuspicious Code\
code-tunnel.exe
If you use sysmon, you can switch to EventCode=1 and Sysmon
sourcetype instead of EventCode 4688.
Looking for suspicious child process
Great idea from lobas :
1IOC: Process tree: code.exe -> cmd.exe -> node.exe -> winpty-agent.exe
A straightforward search would be :
1index=win sourcetype="XmlWinEventLog" EventCode=4688 code (cmd OR powershell) ParentProcess="*code-tunnel.exe" NewProcess IN ("*cmd*", "*powershell*")
2| table _time, host, NewProcess, ParentProcess
But once again, code.exe could be renamed
We could use the search above to look for suspicious child process
from our process with accept-server-license-terms option :
1index=win sourcetype="XmlWinEventLog" EventCode=4688 (cmd OR powershell) ParentProcess="*code*.exe" NewProcess IN ("*cmd*", "*powershell*")
2 [search index=win sourcetype="XmlWinEventLog" EventCode=4688 tunnel accept server license terms CommandLine="*.exe*tunnel*--accept-server-license-terms*"
3 | table host NewProcessId
4 | rename NewProcessId as ParentProcessId
5 | format]
6| table _time, host, NewProcess, ParentProcess
_time host NewProcess ParentProcess
2023-09-18 C:\Windows\System32\ c:\Users\ipfyx\AppData\
11:36:21 sauteTomate WindowsPowerShell\ Local\Programs\Microsoft
v1.0\powershell.exe VS Code\bin\c0de.exe
File creation
Another great idea from lobas :
IOC: File write of code_tunnel.json which is parametizable, but
defaults to: %UserProfile%\.vscode-cli\code_tunnel.json
license_consent.json file could also be watched.
1PS > gc C:\Users\ipfyx\.vscode\cli\code_tunnel.json
2{"name":"sauceTomate","id":"9s43zAc9","cluster":"uks1"}
3PS > gc C:\Users\ipfyx\.vscode\cli\license_consent.json
4{"consented":true}
I do not have sysmon EventCode 11 yet, so I will leave the SPL search
as an exercise for the user. Watching for those files creation inside
the UserProfile directory is not enough since it can be changed with
the --cli-data-dir option.
1.\code.exe tunnel help
2...
3GLOBAL OPTIONS:
4 --cli-data-dir Directory where CLI metadata should be stored [env: VSCODE_CLI_DATA_DIR=]
Web traffic monitoring
If you haven't blocked the domains I mentioned earlier, watch out for
any HTTP traffic toward those domains.
Conclusion
A GPO parameter would be awesome but it is yet to be seen. An
Applocker hash rule is not sustainable. Moreover, your
non-domain-joined computers would not be concerned by neither of
them. Therefore, for now, we are only left to blocking those two bad
boys :
1*.tunnels.api.visualstudio.com
2*.devtunnels.ms
[ ]
ipfyx
Yet another cybersecurity blog
Read More
Featured Posts
* Blocking Visual Studio Code embedded reverse shell before it's
too late
Recent Posts
* D LINK DNS323 Fan Speed Control
* Bluekeep, why would you still be vulnerable ? SHA2 signing
Categories
DEFENSE 2 ADMIN 1 CYBERSECURITY 1
Tags
POWERSHELL 2 WINDOWS 2 BASH 1 BLUEKEEP 1 BLUETEAM 1 CVE 1 DEFENSE 1
LINUX 1 RDP 1 SOC 1 SPLUNK 1 WSUS 1
ipfyx blog
Copyright 2019- IPFYX BLOG. All Rights Reserved