Attackers are always looking for ways to blend into an environment, to establish a C2 channel undetected. Utilising legitimate tooling for their malicious activities is a common way that they achieve this.
From a defender’s standpoint, spotting activity that’s meant to blend in can be quite challenging. A SOC team, especially one experiencing alert fatigue, might often resort to checking a domain or hash on VirusTotal. If it’s a known domain or hash, particularly one signed or trusted by Microsoft, they might quickly label it as a false positive and move on.
Microsoft Dev Tunnels
Dev Tunnels allow developers to expose services running locally to remote hosts, across the internet and tunneled through Microsoft infrastructure. For example, let’s say I have a simple webserver running on my PC, I want a colleague to be able to test the web page I’ve created. I can use a Dev Tunnel to expose port 443 on my host. A Microsoft Dev Tunnel domain is generated *.devtunnels.ms, I provide the URL to my colleague, and they can access the web service on my local host, via the Microsoft URL.
I can expose any service via the Dev Tunnels, including RDP and SSH. With the ability to tunnel traffic using Microsoft domains, you can imagine why this would be an attractive concept to attackers.
There have been increased reports of threat actors utilising Microsoft Dev Tunnels and other legitimate remote tools in their campaigns. So, let’s walk through the different ways Dev Tunnels can be abused by attackers.
Scenario One: Using for C2
Firstly, using Dev Tunnels for C2. Take the diagram below as an example. A victim PC has executed a payload, and the C2 beacon is configured to connect to a Microsoft Dev Tunnels domain.
In most environments, the domain will pass through any proxy or firewall, due to the domain belonging to Microsoft. The traffic is then forwarded by Microsoft to the attacker’s machine.
Creating a tunnel
Meanwhile, if we look at the attacker’s perspective, all they need to do is install the dev tunnel binary, which is available on Mac, Linux, and Windows.
Some key details on establishing a tunnel below:
- Authentication: To create a tunnel, you need to authenticate to either a personal GitHub or Microsoft Account. Obviously setting up an account with either is trivial.
- Anonymous Access: You can configure a tunnel to allow anonymous access, allowing anyone to connect to your tunnel.
- Tunnel ID: Following the creation of a tunnel, a tunnel ID is generated, this is used to connect two endpoints together through a tunnel.
The highlighted URL below is what we will use for our C2 beacon.
Establishing C2
The process for utilising a Dev Tunnel domain for C2 traffic will be similar across different C2 frameworks. Below is an example of creating a listener in Cobalt Strike.
- Select Beacon Type, usually HTTPS.
- Define the HTTPS host to listen on, in this case, it would be our tunnel domain, 8kl69l904-443.uks1.devtunnels.ms
- Define the HTTPS host (stager), the IP of your team server.
- Select HTTPS Port, 443.
Example Event
In the event shown below, we can see connections to our tunnel domain, from our beacon process. The beacon process is a fake OneDrive binary, which has a forged Microsoft certificate.
@timestamp | dns.question.name | process.code_signature.subject_name | process.name | user.name | process.code_signature.trusted |
Sep 1, 2024 @ 11:44:50.169 | 8kl6l904-443.uks1.devtunnels.ms | www.microsoft.com | OneDrive.exe | paul | false |
Sep 1, 2024 @ 11:44:50.148 | 8kl6l904-443.uks1.devtunnels.ms | www.microsoft.com | OneDrive.exe | paul | false |
Aug 31, 2024 @ 23:00:18.669 | 8kl6l904-443.uks1.devtunnels.ms | www.microsoft.com | OneDrive.exe | paul | false |
Aug 31, 2024 @ 23:00:18.649 | 8kl6l904-443.uks1.devtunnels.ms | www.microsoft.com | OneDrive.exe | paul | false |
Detecting Dev Tunnel C2
Detecting the usage of Dev Tunnels for C2 is challenging, as the Dev Tunnels binary does not need to be present on the target host. In cases where EDR or endpoint controls fail to detect the initial payload, detecting on network-based telemetry would be challenging. As expected, the TLS connections have a valid certificate as they are after all legitimate Microsoft domains.
We can look to identifying C2 traffic by looking for constant connections to the Dev Tunnel domains over sustained periods. C2 traffic
Below is the query used in TimeLion to generate the above:
.es(
index='.ds-logs-network_traffic*',
q='destination.domain: *.uks1.devtunnels.ms',
timefield='@timestamp',
metric='count'
).bars(),
.es(
index='.ds-logs-network_traffic*',
q='destination.domain: *.uks1.devtunnels.ms',
timefield='@timestamp',
metric='cardinality:destination.domain'
).lines(width=2, stack=false).label("Unique Domains")
We can also use a more generic C2 detections methods, looking for high volume connections to single domains. The below shows count of connections to single domains over a 24 hour period. We can see clear outliers to our Dev Tunnel C2 domains, where constant connections are made.
Network with Endpoint Telemetry
Many EDR products now are able to join domain requests with the process responsible for making the request. You can also usually enrich the binary with certificate information, looking for connections to dev tunnel domains from a non-signed or untrusted certificate can also be a method of detection.
dns.question.name : *.uks1.devtunnels.ms and process.code_signature.trusted<br>: false
This however will not help for payloads running from a trusted source. For example sideloading or injecting a payload into a trusted process like Outlook will be more difficult to detect.
As shown below, loading a secondary payload into Outlook, shows C2 to a trusted Microsoft Domain from a trusted Microsoft Process.
In these cases, anomaly C2 on the Dev Tunnel domains would be required. With analysts paying focus to the surrounding and contextual activity, like looking for unusual loads or injections into the process making the requests.
Using for Persistent Access
Dev Tunnels can be used for more than just C2. As we said before, any port can be exposed for remote usage. Therefore, RDP and SSH are good candidates, allowing a threat actor to establish persistent remote access.
Ideally, we would want a scripted method of establishing and maintaining remote access via Dev Tunnels.
I created a simple PowerShell Script that does the following on a victim host.
- Downloads a copy of Dev Tunnels and places it in a Windows Start-Up Location.
- Download from:
https://aka.ms/TunnelsCliDownload/win-x64
- Place in:
C:\Users\paul\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\
- Download from:
- Authenticates the Dev Tunnel process to an account.
- More on this further down.
- Sets up a tunnel, exposing port 3389 to allow for anonymous access.
Github Device Authentication
When first using Dev Tunnels on a device, you must authenticate. To do this in a scripted manner, we can use GitHub Device authentication:
devtunnel user login -g -d
“Login with a GitHub account with device code login, if local interactive browser login isn’t possible“
When this command is run, a device login code is written to the terminal. Using our PowerShell script, we can capture this code by writing it to a .txt file. We can then pull the string from the file, and send it to ourselves.
We can use interactsh to receive the login code, but sending a POST request to our OAST domain from the PowerShell Script.
We can then use the device code to authenticate to a our attacker GitHub account, via https://github.com/login/device
Establishing Tunnel
Now that we’ve authenticated, our PowerShell script can continue, by setting up a tunnel.
This is as easy as running a command like the one below. Port 3389 is being exposed and allows for anonymous login.
.\devtunnel.exe host -p 3389 -allow-anonymous
Now that the tunnel is setup, we can authenticate to the same GitHub account from our attacker machine.
We can list out tunnels that are running within our account, and connect to the tunnel exposing RDP on our victim device by specifying the tunnel ID.
In order to connect via RDP we need to authenticate to a user on the device. If we do not already have a compromised user, we can brute force RDP via the tunnel.
Traffic is forwarded from local host, through the tunnel, to our victim host on port 3389. Therefor, to brute force, we specify local host as our target, and we’re able to brute force RDP on our victim host.
Once we have an account to utilise, we can use a RDP client to connect. Again specifying the local host address as the the remote host, and authenticating with our compromised user.
This means you could in theory expose a host remotely, via a single PowerShell script, making it a candidate in phishing. This can be a method of bypassing RDP restrictions on a network, exposing internal hosts, remotely.
Detections
I’m using Elastic SIEM and Endpoint agent to collect telemetry and build custom detections.
Dev Tunnel Binary Execution Detection
In circumstances where an attacker has brought the Dev Tunnel binary onto disk, you could catch them with a simple:
Process.Name:devtunnel.exe OR File.Name:devtunnel.exe
But any decent operator is going to rename the process to something else.
You could look for the initial download, via PowerShell or WinGet
CommandLine= "*winget install Microsoft.devtunnel*"
OR
URL= "*https://aka.ms/TunnelsCliDownload/win-x64*"
But again, these are easy to bypass, and an attacker will likely bring the tool down from their own infrastructure.
A more robust way of detection is took look for a certain DLL that the tunnel requires for operation. Looking at module load events and there’s a variety of DLLs loaded by the devtunnel.exe, but one sticks out as an easy detection, devtunnel.dll.
event.action: "load" and dll.name: "devtunnel.dll"
The DLL is loaded from a temp .net directory that is created on execution. This is a behaviour that an attacker would not be able to manipulate.
Tunnelling RDP Detection
You’ll notice the source IP in the logs for this activity is displayed as ::1, this is the loopback address for the host, or 127.0.0.1. As we are forwarding traffic from local host on our attacker machine from our victim machine, this is the address that is displayed.
Interestingly the logon type is captured as type 3, rather than type 10. The below query will show both successful and failed logons originating from a loopback address. Putting this into a detect would likely generate a lot of noise and FPs.
event.provider: "Microsoft-Windows-Security-Auditing" and event.code: 4625 or event.code : 4624 and (source.ip : "::1" or source.ip : 127.0.0.1)
Instead we can use a threshold detection to attempt to detect suspected brute forcing, from a loopback address.
Incident Response Triage & Logging
Unfortunately there is very little in the way of logging for Dev Tunnels. Unlike Microsoft VSCode Remote tunnels, which is a different implementation of the same underlying tunnel features, (more on how they can be abused another time), there is no dedicated logging for tunnel usage.
If you’re responding to an incident that involves the use of Dev Tunnels, and you do not have host or network telemetry, then your best option is to dump the Dev Tunnel process. We can get all the key information from the process dump.
Including the username the tunnel authenticated with.
Closing Thoughts
As shown, Dev Tunnels provide powerful capabilities that can be (and are) abused by Threat Actors, to make detection by Blue Teams more difficult. The best option is to outright block tunnel usage in the estate, this can be achieved by blocking the Dev Tunnel Domains listed below.
IOCs
File Hashes
FileName | SHA256 |
Devtunnel.exe | cb2d8470a77930221f23415a57bc5d6901b89de6c091a3cfbc563e4bf0e7b4eb |
devtunnel.dll | c0513783d569051bdc230587729b1da881f7032c2ad6e8fedbbdcc61d813da25 |
Domains
Description | Domain |
Dev Tunnel Domain | global.rel.tunnels.api.visualstudio.com |
Dev Tunnel Domain | *.rel.tunnels.api.visualstudio.com |
Dev Tunnel Domain | *-data.rel.tunnels.api.visualstudio.com |
Dev Tunnel Domain | *.devtunnels.ms |
GitHub Device Auth URL | https://github.com/login/device |
Tunnel Install URL | https://aka.ms/TunnelsCliDownload/win-x64 |