Sunday, August 21, 2011

Reversing Stuxnet: 4 (File Filtering Rules)

In my previous post, I talked about Stuxnet's file hiding capabilities. In this post, I will cover the exact rules it follows to hide the files. Two of the files that Stuxnet hides are its payload files: "~WTR4132.tmp" and "~WTR4141.tmp". These files come preloaded on an infected USB drive, and contain executable code which extracts the rest of the malicious code and installs it in the system.

The rules for mrxnet's ".tmp" file hiding are as follows:
  1. Name must be prefixed by "~WTR"
  2. Name must have suffix ".TMP"
  3. Name must be exactly 12 characters long
  4. The sum of the 4 digits between the prefix and suffix of the name must be evenly divisible by 10.(sum%10 == 0)
  5. Filesize must be >= 4KB
  6. Filesize must be <= 8MB
NOTE: all the strings in the above rules are case insensitive-mrxnet converts all filenames to uppercase before evaluating its rules.

I found these rules using a combination of reading the disassembly, debugging the execution of the rules, and behavioral analysis. Below, we can see some of these rules implemented in the malware's code:
Implementation of file hiding rules in Stuxnet

 
In the above screenshot, we can see that the function I named "foundSubString" is called. This function seems to be logically equivalent to "!strncmp". It returns 1 if the string matches and 0 otherwise. I am guessing the programmers decided not to call any of the kernel-provided string manipulation functions(remember, the standard userspace assumptions and APIs are not available when running in the kernel) in order to make the malware harder to reverse engineer.

Saturday, August 20, 2011

Reversing Stuxnet: 3 (Filesystem hooking)


Stuxnet is a very sophisticated rootkit. Since it runs in the kernel, it has ultimate control over what userspace sees/experiences. Stuxnet exploits its privileges to hide certain payload files. When I first installed Stuxnet in my Virtual Machine, it came as 3 files: "~WTR4132.tmp", "~WTR4141.tmp" and "Copy of Shortcut to.lnk". These are the files that are supposed to come preloaded on the infected USB drive that is connected to the target machine.

Once Stuxnet is installed, it hides the aforementioned files from system calls that try to enumerate the files in the current directory. By doing so, a regular user would be unaware that his machine is infected with Stuxnet. For example, in the screenshot below, the displayed folder actually contains 2 additional files (~WTR4132.tmp and ~WTR4141.tmp), which have been filtered out of the result of the Windows shell's query to the filesystem for all files in that directory(see NTQueryDirectoryFile).

I set a breakpoint in the mrxnet module that was loaded into the windows kernel so we can trace the execution at certain points in time. The function in which I placed the breakpoint(let's call it EvaluateFileHidingRules) contains the logic that Stuxnet uses to figure out whether or not it should hide a file(more on this in a future post). Below is the call stack at the point in time that we try to navigate to the folder with the hidden files (folder screenshot above):
What's interesting is that we can see the Stuxnet code (anything marked with mrxnet in the above call stack) executing at different layers in the call stack. This is probably due to the fact that Stuxnet installs itself as a file system filter driver, and registers callbacks in the windows kernel.

Another occurrence I noticed (which is supported by the above call stack) is that the number of times that my breakpoint in EvaluateFileHidingRules was hit is linearly proportional to the number of files in the current directory when I run the dir command from the Windows commandline. This is due to fact that one of the functions that NtQueryDirectoryFile indirectly calls for each file in the folder, in turn calls EvaluateFileHidingRules. In general, the number of calls to EvaluateFileHidingRules is as follows:

#calls = (#files in Dir) + 2

NOTE: #files in Dir reflects what is actually on the disk, not what is returned to userspace after filtering.