Hidden in plain sight: Alternate Data Streams
Have you ever wondered how a file in a file listing is shown with size 0 bytes but can still contain data? Or maybe wondered where all that meta data is stored, how malware can infect files or just how you can “hide” stuff in a file?
Let’s talk about Alternate Data Streams to learn more.
ADS - Alternate Data Streams
When you hear “Alternate Data Streams” you may think about resource forks in Mac OS HFS. But we’re talking about Windows and NTFS. Back in the days of Windows NT 3.1 (ha!), NTFS streams were actually implemented to support the Mac resource forks.
Let’s fire up a cmd prompt and start with a practical example:
As you can see, we redirected our echo to the file, but specified another name after the colon. This is the name of the stream, which now contains our “Hello World!”. Note, though, that our created file is still being displayed as having a file size of 0 bytes.
Seeing the streams
What we just did, was to write data to a data stream other than the original data stream, a.k.a. the file itself.
We could go on and add another stream like this: echo more stuff > hello.txt:mystream
.
After creating our streams, how do we actually look at them?
We can (on Win >= 8) use the Get-Item
and Get-Content
Cmdlets in Powershell:
The above command will show us the streams we’ve created, plus the default one, also called unnamed data stream. When looking at the sizes/lengths, we can also see that the unnamed stream is still only 0 bytes (which can make it hard for users to detect disk usage in case of the presence of some large alternate data streams).
$DATA
is actually the stream type. So our unnamed default stream is literally just an unnamed stream, while our “hidden” stream would be an alternate stream with a name, which could also be written like hidden:$DATA
.
Fun fact: if we were to create a hash of our hello.txt
file (Get-FileHash hello.txt
), then added data to an ADS, the hash would not change.
Getting the contents out
To get the content out again, we can use the Get-Content
cmdlet:
What’s so interesting?
So what’s so interesting about the alternate data streams, being old and not very much used?
Well, besides storing simple text strings, we can also store executable code in a stream. The fact that it doesn’t show in the file size, won’t change the hash, doesn’t come up in explorer / in the UI and that ADS can’t be disabled, makes it an ideal hiding place for malicious software (and it has indeed been used by malware in the past).
Nowadays, AV software usually scans these alternate data streams too (just try it out).
Storing an executable in an ADS
Let’s see how it can be done by storing the calc.exe in an ADS called exestream
of our hello.txt
. We will use the Set-Content
Cmdlet and explicitly specify byte encoding and a readcount of 0, to read the file in one read operation. The Get-Command
is only used to not type out the path.
If you have some known malware on your system and store it in an ADS, you will notice that it is detected nonetheless. Well, let’s hope so :-).
Launching the hidden code
Now that we have a binary in our exestream
, we can launch it, e.g., via wmic
(Windows Management Instrumentation). I use Resolve-Path
, again, only not to type out the full path.
If it works, you should see the calculator pop up.
Finding alternate data streams
Now that we know what a data stream is and what we can do with it, let’s see how we can list all the files in and below a directory that actually have alternate data streams (and maybe check that everything is in order).
Note: one of the more common ADS that you will find is Zone.identifier
. These are usually for files that were downloaded and might be used to indicate this fact to the user; see info at the Microsoft docs.
Have fun playing around with Alternate Data Streams!
Like to comment? Feel free to send me an email or reach out on Twitter.
Did this or another article help you? If you like and can afford it, you can buy me a coffee (3 EUR) ☕️ to support me in writing more posts. In case you would like to contribute more or I helped you directly via email or coding/troubleshooting session, you can opt to give a higher amount through the following links or adjust the quantity: 50 EUR, 100 EUR, 500 EUR. All links redirect to Stripe.