A lot of people will hate on Microsoft or Windows or anything else that comes out the doors of 157th Avenue NE in Redmond, Washington for doing…well…just about anything. Nonetheless, I wanna say that I love Windows. I love it so much that I’ll actually thank Microsoft. I’ll thank them for making it so damn easy to completely and utterly compromise their systems with little to no effort at all. Thank you Microsoft for making my life that much easier.
Over the years, the list of “Microsoft mistakes” has grown longer and longer with each release. One such screw-up that I’d like to talk about is Alternate Data Streams (ADS). “What are Alternate Data Streams” you say? You say you’ve never heard of them before? Most people haven’t because it’s another one of their highly under-documented technologies. I don’t blame them for trying to hide it either because ADS is just downright stupid!
What is ADS?
Alternate Data Streams allows for one or more data streams to be associated with a file. It’s primarily meant for storing file attributes and other metadata. However, the size of the alternate stream in no way affects to total file size. Say you’ve got a 60 kB file with a 30 kB ADS associated with it. If you were to take a look at it with Windows Explorer or the command-line, they would still report the file as being only 60 kB. Because of this “feature”, it allows super evil bad guys to effortlessly hide data that can remain concealed until very close inspection.
You might be wondering, “Well what the hell Microsoft!? Why!?” The truth is that ADS was Microsoft’s attempt at implementing filesystem forks in order to maintain compatibility with other filesystems like Apple’s HFS+ and Novell’s NWFS and NSS. It was first introduced to NTFS in Windows NT 3.1 and was meant to store metadata like file attributes, icons, image thumbnails, that kinda thing. Sure it’s a reasonable idea but you have to wonder: given the choice between maintaining compatibility with a competitor’s technology versus introducing yet another means to exploit your product, which is more important? Apparently, the fools professionals at Microsoft are more interested in dancing pigs.
Using ADS With Text Files
Adding a data stream to a file is practically child’s play. An ADS is referenced using the notation filename:stream
anywhere you’d normally use a regular filename.
You can use any existing file but I’m gonna start out making a new one.
C:\foo\bar>echo "They mostly come out at night...mostly." > newt.txt C:\foo\bar>type newt.txt "They mostly come out at night...mostly." C:\foo\bar>dir Volume in drive C has no label. Volume Serial Number is FCE9-528D Directory of C:\foo\bar 08/30/2012 05:13 PM . 08/30/2012 05:13 PM .. 08/30/2012 05:15 PM 44 newt.txt 1 Files(s) 44 bytes 2 Dir(s) 7,113,324,588 bytes free
Note the current file size of 44 bytes. Using the notation described above, tack on some text to an alternate stream.
C:\foo\bar>echo "Now all we need is a deck of cards." > newt.txt:hicks C:\foo\bar>dir Volume in drive C has no label. Volume Serial Number is FCE9-528D Directory of C:\foo\bar 08/30/2012 05:13 PM . 08/30/2012 05:13 PM .. 08/30/2012 05:15 PM 44 newt.txt 1 Files(s) 44 bytes 2 Dir(s) 7,113,324,588 bytes free
It’s still 44 bytes! If hicks
were a normal file, it’d be 40 bytes so you’d think the total size of newt.txt
would be 84 bytes. Nope. As far as we’re concerned, there’s nothing there but a plain ol’ text file. But just in case, let’s make sure our ADS is actually there.
C:\foo\bar>type newt.txt:hicks The filename, directory name, or volume label syntax is incorrect.
Uh oh. Time out. What happened here? The ADS was created just fine but it couldn’t be read from. As the error message indicates, some commands tend to vomit all over the place when a colon is used anywhere but the drive letter (e.g. C:
or D:
).
The best way to read from an ADS is to use the more
command.
C:\foo\bar>more < .\newt.txt:hicks "Now all we need is a deck of cards."
Ah, that’s much better.
Hiding Media Files
This ADS stuff might not seem all that much of a threat since we’re just dealing with text files here. However, ADS can be used with any type of file. It’s just raw binary data.
Imagine that you’re really embarrassed about the fact that you like Nickelback but all your friends are huge Limp Bizkit fans instead. To make sure that everybody still thinks your cool, just hide your Nickelback MP3’s in an ADS. By the way, you should both be embarrassed and find a new group of friends!
C:\foo\bar>dir Volume in drive C has no label. Volume Serial Number is FCE9-528D Directory of C:\foo\bar 08/30/2012 05:22 PM . 08/30/2012 05:22 PM .. 08/30/2012 05:22 PM 4,198,147 limp_bizkit.mp3 08/30/2012 05:22 PM 4,031,881 nickelback.mp3 1 Files(s) 8,230,028 bytes 2 Dir(s) 7,121,554,572 bytes free C:\foo\bar>type nickelback.mp3 > limp_bizkit.mp3:nickelback.mp3 C:\foo\bar>cat limp_bizkit.mp3:nickelback.mp3 > nb.mp3
You may have noticed my use of the cat
tool even though we’re using Windows. Yes, there is such a thing. I decided to use CoreUtils for Windows instead of using double redirection since that can take a while.
You could do the same thing with video files if you wanted. The point is, it’s just raw data so any combination of filetypes can be used; they don’t even have to be the same.
Compatibility Issues
ADS compatibility between Windows versions is limited at best. Since Windows Vista, Microsoft has taken out some of the major security concerns with ADS but has left some features in tact for…wait for it…compatibility reasons! That’s right!
Up until this point, everything will work just fine on post-Vista versions (at the time of this writing, Windows 7 is still the hot product.) However, the next section on executables will not. Windows XP was the last version to support running executables from ADS streams. But that’s really not a big deal considering the large amount of individuals and businesses that are still reluctant to upgrade their machines (my mom’s computer is still running XP because she “doesn’t understand all those new fancy icons and just wants to be able to read her email.”)
There are a few more compatibility issues that I’ll mention as we come to them.
Using ADS With Executables
The real fun with ADS begins when you start using it to hide executables. There’s no need hide-and-unhide them as we did with the MP3 files. They can be run directly from the ADS.
Let’s try an example that hides notepad.exe
within calc.exe
. Remember, we’re talking about Windows XP (at least) right now. No more “fancy icons.”
C:\foo\bar>copy %windir%\System32\calc.exe . 1 file(s) copied. C:\foo\bar>dir Volume in drive C has no label. Volume Serial Number is FCE9-528D Directory of C:\foo\bar 08/30/2012 06:21 PM . 08/30/2012 06:21 PM .. 08/23/2001 07:00 AM 114,688 calc.exe 1 Files(s) 114,688 bytes 2 Dir(s) 7,114,243,072 bytes free
At this point, note that calc.exe
is 114,688 bytes. This value is not going to change even after we tack on notepad.exe
which is 69,120 bytes.
C:\foo\bar>type %windir%\System32\notepad.exe > calc.exe:notepad.exe C:\foo\bar>dir Volume in drive C has no label. Volume Serial Number is FCE9-528D Directory of C:\foo\bar 08/30/2012 06:33 PM . 08/30/2012 06:33 PM .. 08/30/2012 06:33 PM 114,688 calc.exe 1 Files(s) 114,688 bytes 2 Dir(s) 7,114,243,072 bytes free
The file size hasn’t changed but if you look closely, something else has: the last modified timestamp. It now shows that calc.exe
was last modified just now. Although very subtle, any potential evidence like this presents a problem for covering your tracks.
Modifying the Timestamp
There’s gotta be a way around this, right? Of course!
For the lazy and less ambitious, there’s plenty of third party tools. A popular one is SKTimeStamp. It’s a small shell extension that adds an extra tab to the Properties dialog in Windows Explorer which allows you to modify the Created, Last Modified, and Last Accessed timestamps.
I’m sure Stefan Küng’s little tool works just fine so I won’t tell you not to use it. However, we’re all hackers here (I hope) so let’s do it ourselves.
So what language should we choose? Well, we could always use a batch file or some VBScript but that’s just not ugly enough. I think PowerShell will satisfy my need for ugliness for now.
So start up vim
and…<sigh>…I mean Notepad and write a little something like this:
# chtime.ps1 if ($args.Length -ne 2) { Write-Output @" Usage: chtime.ps1 Must be of the form MM/DD/YYYY HH:MM AM/PM Name of file to modify "@ exit(1) } $date = Get-Date $args[0] $file = Get-ChildItem $args[1] $msg = "File {0}: changing timestamp to {1}" ` -f [IO.Path]::GetFileName($file), $date.DateTime Write-Output $msg $file.LastWriteTime = $date
Awww yeah. Now that’s good and ugly alright. Seriously, sometimes I wonder if 3 – 5 years experience of being in a brainless stupor is a job requirement to work at Microsoft because whoever helped develop PowerShell and thought something like this actually looks nice must’ve been an indubitable moron.
Alright, alright…enough trash talk. Start up PowerShell (XP require the KB968930 update here) and run the following commands:
PS C:\foo\bar> ls Directory: C:\foo\bar Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 8/30/2012 6:33 PM 114688 calc.exe -a--- 8/30/2012 6:41 PM 850 chtime.ps1 PS C:\foo\bar> .\chtime.ps1 Usage: chtime.ps1 Must be of the form MM/DD/YYYY HH:MM AM/PM Name of file to modify PS C:\foo\bar> .\chtime.ps1 "08/23/2001 07:00 AM" .\calc.exe File calc.exe: changing timestamp to Thursday, August 23, 2001 7:00:00 AM PS C:\foo\bar> ls Directory: C:\foo\bar Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 7/13/2009 9:38 PM 114688 calc.exe -a--- 8/30/2012 6:42 PM 850 chtime.ps1
See? All better.
Having properly covered up our tracks, we can now run the executable from within the ADS.
C:\foo\bar>start .\calc.exe:notepad.exe
Notepad should start up just fine but if you take a look at the Task Manager, you’ll see that we didn’t achieve true stealth. :(
ADS as Hidden Processes
The advantage of using an executable as an ADS is that it allows you to hide the fact that it’s even running. When you run the ADS executable, the Task Manager reports the process as the original filename.
However, this too is dependent on certain Windows versions. Windows 2000 will not show the ADS process; only the original executable.
On the other hand, Windows XP will show the ADS process in the Task Manager as filename:stream
as you can see below.
Sure it’s not completely invisible anymore but this doesn’t present that much of a problem though. Considering that most people have never even heard of ADS, we can simply change the executable name to look like something more legitimate. Windows Service Host is always a favorite so let’s try that.
C:\foo\bar>copy %windir%\System32\svchost.exe . 1 file(s) copied. C:\foo\bar>dir Volume in drive C has no label. Volume Serial Number is FCE9-528D Directory of C:\foo\bar 08/30/2012 07:00 PM . 08/30/2012 07:00 PM .. 04/14/2008 12:42 AM 14,336 svchost.exe 1 File(s) 14,336 bytes 2 Dir(s) 7,113,338,880 bytes free C:\foo\bar>type %windir%\System32\notepad.exe > svchost.exe:svchost.exe C:\foo\bar>start .\svchost.exe:svchost.exe
Not so bad anymore, is it? You could even run it as Administrator for an extra layer of obscurity. It’s always fun making lemonade out of lemons.
Detecting ADS
The truth is, there’s no way to make ADS 100% invisible. You can minimize its footprint but, nevertheless, there’s still a footprint. Because of that, there are a few ways to search for ADS.
Starting with Windows Vista, Microsoft introduced the /R
switch to the dir
command that will display any ADS streams associated with a file. For example:
C:\foo\bar>dir /R Volume in drive C has no label. Volume Serial Number is BE91-B378 Directory of C:\foo\bar 08/30/2012 07:24 PM . 08/30/2012 07:24 PM .. 08/30/2012 07:24 PM 44 newt.txt 40 newt.txt:hicks:$DATA 1 File(s) 44 bytes 2 Dir(s) 58,964,307,968 bytes free C:\foo\bar>rename newt.txt delete_me.txt C:\foo\bar>type delete_me.txt > newt.txt C:\foo\bar>del delete_me.txt
Practically speaking, this is only useful unless you already suspect some type of suspicious behavior coming from a particular directory. (If you’re curious what $DATA
is, take a look here.)
Something a little more practical is to use a tool like LADS. Unlike the /R
switch, LADS lists only the files with ADS streams associated with them; not the entire directory contents. It also has more features like recursing through subdirectories and displaying a verbose report. The nice thing about it is that it works on Windows NT4, Windows 2000, Windows XP, Windows Server 2003, Windows Vista, and Windows 7.
C:\foo\bar>lads.exe . LADS - Freeware version 4.10 (C) Copyright 1998-2007 Frank Heyne Software (http://www.heysoft.de) This program lists files with alternate data streams (ADS) Use LADS on your own risk! Scanning directory C:\foo\bar\ size ADS in file ---------- --------------------------------- 193536 C:\foo\bar\calc.exe:notepad.exe 40 C:\foo\bar\newt.txt:hicks 27136 C:\foo\bar\svchost.exe:svchost.exe 220712 bytes in 3 ADS listed
But if you’re like me and are surrounded by people who wouldn’t know what a command-line even was, there are a few GUI tools as well. I’d recommend ADS Spy. Even though it doesn’t support the old Windows versions that LADS does, it does have the neat option of removing any ADS streams it finds. It can also calculate the MD5 checksum of the stream.
LADS and ADS Spy are both great tools and I give them my full seal of approval. :)
Programming With ADS
Up until now, all examples involving the creation of ADS streams have been using the command-line. Additionally, you can also use ADS in the Windows C API.
// ads.cpp #include <iostream> #include <windows.h> using namespace std; #define FILENAME "fling.txt" #define ADS FILENAME ## ":flang.txt" BOOL Write(HANDLE *hHandle, char *lpczFile, char *lpczMsg, DWORD nByteSize) { DWORD dwWritten; *hHandle = CreateFileA(lpczFile, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, NULL); if (*hHandle == INVALID_HANDLE_VALUE) return FALSE; else WriteFile(*hHandle, lpczMsg, nByteSize, &dwWritten, NULL); return TRUE; } int main(void) { char *lpczFileMsg, *lpczStreamMsg; BOOL bStatus; DWORD dwFileSize, dwStreamSize; HANDLE hFile, hStream; lpczFileMsg = "Yap flopping flabrizzle!"; dwFileSize = strlen(lpczFileMsg); lpczStreamMsg = "Da woogle booshizzle!"; dwStreamSize = strlen(lpczStreamMsg); // Write to original file. bStatus = Write(&hFile, FILENAME, lpczFileMsg, dwFileSize); if (bStatus) cout << "Wrote '" << lpczFileMsg << "' to " << FILENAME << endl; else { cerr << "[ERROR] Failed to open " << FILENAME << endl; exit(EXIT_FAILURE); } // Write to alternate data stream. bStatus = Write(&hStream, ADS, lpczStreamMsg, dwStreamSize); if (bStatus) cout << "Wrote '" << lpczFileMsg << "' to " << ADS << endl; else { cerr << "[ERROR] Failed to open " << ADS << endl; exit(EXIT_FAILURE); } CloseHandle(hFile); CloseHandle(hStream); return 0; }
Even if you’re unfamiliar with the Windows C API, this should still be fairly self-explanatory. You call CreateFile()
as you normally would except using the filename:stream
notation like we did on the command-line.
Just to double check that it works:
C:\foo\bar>cl /nologo ads.cpp ads.cpp C:\foo\bar>ads.exe Wrote 'Yap flopping flabrizzle!' to fling.txt Wrote 'Da woogle booshizzle!' to fling.txt:flang.txt C:\foo\bar>type fling.txt Yap flopping flabrizzle! C:\foo\bar>more < .\fling.txt:flang.txt Da woogle booshizzle!
Perfect. Additionally, you can use the same method when reading from an ADS with the ReadFile()
function. You get the idea though. It’s simple stuff, really.
There’s one gotcha that’s worth noting: ADS cannot be used in .NET languages. This is due to the fact that the colon character is only allowed to appear directly after the drive letter.
Data Loss
The downside to ADS is that they’re only safe on NTFS drives. If you move a file that has an associated ADS to another location that does not support filesystem forks, you will lose everything in the ADS. Not only does that mean simply copying to something like a FAT32 formatted flash drive but it also includes communications like email or FTP. It gets even worse when you consider what would happen when transferring files between forks-aware filesystems but using a program that does not support them. The same can occur when compressing files that aren’t fork-aware either.
The bottom line, keep your ADS files put. Even though the whole notion of ADS was to be compatible with other fork-aware filesystems, there are just too many variables in between that can result in data loss.
The Future of ADS
As a legitimate solution to storing metadata, ADS has a very dull future. Because of all the compatibility problems and security concerns, manufacturers have largely moved over to extended file attributes. Extended file attributes are a much more practical solution to organizing metadata since data is organized into key-value pairs instead of just raw binary data and cannot exceed the size of the original file. It’s nice to see companies starting to realize that the security implications of filesystem forks far outweighs their usefulness. However, as long as filesystem forks like ADS are at least 1% supported, they’ll always pose some type of risk. And I’ll always be there to exploit it. :P