Andromeda under the microscope

Andromeda is one of the longest running and most prevalent malware families to have existed.

Andromeda is one of the longest running and most prevalent malware families to have existed. Andromeda was first discovered in late 2011 and it probably evolved from ngrBot/DorkBot. Throughout its existence, the groups behind Andromeda have used various methods to spread the malware and infect users.

We have seen Andromeda spread via spam email campaigns with infected files attached (doc, xls, pdf, zip.), through illegal download sites, warez (infected cracks, keygens, ..), or infecting users via other phishing campaigns.

Infection vector

In recent months, the authors have mainly focused on spreading Andromeda via exploit kits (Neutrino, Nuclear, Angler,..) located on compromised websites or advertisement services. These exploit kits are mainly found on a dubious sites (p0rn, warez, video streaming sites, share sites etc.) but occasionally appear on trusted sites as well.

infection_scheme_Andromeda.png

Andromeda binary files are almost always stored on hacked websites, but we have also discovered files hosted on a few dedicated servers that only host malware. Not only have we seen Andromeda appear on hacked websites, but we have also seen its plugins being distributed on SourceForge.net, a repository that hosts 7zip, VLC player, OpenOffice, FileZilla and other popular open source projects.

Andromeda’s core anatomy

This analysis covers the latest variant of Andromeda samples, which began spreading since the beginning of this year. The authors have not made many changes to Andromeda’s core binary file, but  they are constantly changing the PE packer/obfuscator in the top most layer. Andromeda uses various PE packers of different quality to avoid AV detections. Some packers also contain other anti-vm/emul/debug tricks. We’ve seen a packer very similar to Zbot (based on its source code), obfuscated Visual Basic and .NET binaries and even a few custom packers reminiscent of Dridex included in the Andromeda variant.

Andromeda’s authors put a lot of effort into diversifying their portfolio of infection droppers and to disable, or at least complicate the sample submission and exchange between AV companies and their regular process used to scan and thoroughly analyze files. To achieve this, they update the custom packers daily and as a bonus, they bloat the binaries with more than 70 MB of garbage. This strategy can either significantly prolong the sample upload (on a slow connection) or cause an overflow of scan/submit limits of some antivirus scanning engines (or online scanning services respectively). On the other hand, this trick is suspicious and it can help to heuristically detect the file.

Zbot-like packer in detail

Andromeda’s top-layer packer is interesting and deserves a closer look. The packer is very similar to that of Zbot, based on the source code. The encrypted payload is stored inside the “.rsrc” section as the “raw data”.

zbot-rsrc.png

The Andromeda payload is twice encrypted with custom encryption and compressed by the RtlCompressBuffer API function with LZ compression (0x002 - COMPRESSION_FORMAT_LZNT1). The custom encryption uses random seed values and generic obfuscation with lots of SMC (self-modificated code) and junk instructions.

First payload custom encryption:

zbotpack-1layer.png

Second encryption:

zbotpack-2layer.png

The decrypted data is then ready for a decompression via the significant RtlDecompressBuffer API function.

zbotpack-before-rtldecompress.png

Payload Loader

Under all of the obfuscated layers, we found a typical Andromeda payload loader binary. The entire loader is very minimalistic (~20kB) and includes the final malware payload in compressed (Aplib) and encrypted (RC4) form and hardcoded config structure.

Loader config structure

The structure is hardcoded right before the encrypted payload that is 0x28h (40) bytes long and it contains seven values:

  • RC4 key for payload decryption (first 16 bytes).
  • Payload size (dword).
  • Payload CRC32 hash (dword).
  • Heap allocation size for decompressed payload data (dword).
  • Entry point of decompressed payload (dword).
  • Pointer to decompressed payload data section (dword).
  • Size of decompressed payload data section (dword). This value is unused by loader.

loader_config.png

Entire config structure is located at the beginning of  “.rdata” section (VA offset: 0x00402000h).

Loader API hashes

It’s interesting that Andromeda’s loader binary has no imports (in PE directories). The payload loader uses only the ntdll.dll library and all imported API functions are hardcoded as custom hash values.

The malware obtains a handle of the ntdll.dll library via a PEB_LDR_DATA (contains the base address of ntdll and kernel32) trick, well known from many shellcodes:

loader_peb_trick.png

Hashing algorithm is trivial and combines XOR and ROL operations over API names (ASCII).

custom_hash.png

All API hashes are stored at the beginning of “.text” section (VA offset 0x00401000h) as DWORD values.

loader_api_hashes.png

The authors seem to be very experienced native subsystem and low-level programmers and have deep knowledge of the AV detection methods. This malware uses very uncommon API functions in low-level form (Nt/Rtl), which is probably used to avoid standard API monitors/tracers, sandboxes and other dynamic analysis tools with predefined API lists or well known API combinations patterns.

List of all hashes and resolved API functions:

Hash value

API function

0AB48C65

LdrLoadDll

DE604C6A

RtlDosPathNameToNtPathName_U

925F5D71

RtlFreeAnsiString

EFD32EF6

LdrProcessRelocationBlock

B8E06C7D

RtlComputeCrc32

831D0FAA

RtlExitUserThread

A62BF608

NtSetInformationProcess

102DE0D9

NtAllocateVirtualMemory

7CD8E53D

NtFreeVirtualMemory

6815415A

NtOpenFile

E7F9919F

NtQueryDirectoryFile

64C4ACE4

NtClose

028C54D3

memcpy

82D84ED3

memset

Payload encryption & compression

The final Andromeda payload is compressed with Aplib and encrypted with RC4 stream cipher. The encrypted payload is verified with a hardcoded CRC32 hash and proceeds to decryption if this check passes.

payload_crc32_check.png

RC4 decryption followed by Aplib decompression:

payload_encryption.png

Final payload fixups

Once the payload is decrypted and unpacked, it’s necessary to relocate it to its new base address, because it is not a position independent code. This is done through another uncommon API call - LdrProcessRelocationBlock - which is a function used only internally by the system to relocate loaded PE modules.

payload_api_ldr.png

The API function takes a pointer to a relocation record and information about the old and new base address. First relocation record is stored at the beginning of payload data section.

drome.png

After processing each relocation record, the LdrProcessRelocationBlock function returns a pointer to the next record. This makes it possible to traverse to the end of relocations (there’s a terminating null, which signals that there’s nothing else to process).

The last step in the loader part is the API function preparation for the final Andromeda payload. All API functions are represented by the same custom hash form (XOR+ROL) described earlier.

There is also a little config structure located right after the relocation records. The first value of this structure is a custom hash (DWORD) of the DLL file name. The second value is offset to the final payload (DWORD), where resolved API functions will be stored.  The custom hashes (DWORD) of API functions from DLL terminated with 0x0000h are also stored.

payload_api_hash_struct.png

The algorithm for resolving the DLL file name from the hash is similar to resolving API hashes, but it also contains lower-case transformation.

loader_dll_name_algo.png

The loader uses a very uncommon method to search and load resolved DLL files. All steps are made through low-level API and the authors use the same method with PEB_LDR_DATA structure as described above. The loader uses returned UNICODE string from the FullDllName value this time.

loader_peb_trick_full.png

This unicode string with the full DLL path is used as an argument for the RtlDosPathNameToNtPathName_U API function, which transforms the unicode file path string into following unicode format:

“\??\C:\WINDOWS\system32\ntdll.dll"

This string is used to extract the fully qualified path and the “*.dll” file mask and pass them to the NtQueryDirectoryFile API function, which then enumerates libraries in the system directory. Each library name is hashed and compared with stored custom hashes. If the hashes are equal, the DLL file is directly loaded via the LdrLoadDll API function and the loader continues to resolve API function names from hard-coded hashes.

Finally, the loader writes all the resolved function pointers to the payload. The payload itself uses a more sophisticated API redirection method, which first copies an instruction from the particular API function to the final payload, then executes it and redirects back to the original API function’s second instruction. This technique is known as stolen bytes. The authors use JMP instructions 0xEB and 0xE9 for this trick.

payload_api_algo.png

Example of the API redirection:

api_redirect.png

These mangled calls of API functions made  our analysis harder, because the debugger cannot correctly identify/resolve the names of the API functions when they are called this way.

List of all used API functions inside final payload:

ntdll.dll

isdigit, memcpy, memset, NtDelayExecution, NtMapViewOfSection, NtQueryInformationProcess, NtQuerySection, NtUnmapViewOfSection, pow, RtlComputeCrc32, RtlImageHeader, RtlRandom, RtlWalkHeap, _allmul, _alloca_probe

ws2_32.dll

closesocket, connect, FreeAddrInfoW, getaddrinfo, getsockname, htonl, ioctlsocket, recv, sendto, socket, WSACloseEvent, WSACreateEvent, WSAEventSelect, WSAStartup

kernel32.dll

CloseHandle, CopyFileW, CreateEventW, CreateFileMappingA, CreateFileW, CreateProcessW, CreateThread, CreateToolhelp32Snapshot, DeleteFileW, ExitProcess, ExitThread, ExpandEnvironmentStringsW, FlushInstructionCache, FreeLibrary, GetCurrentProcess, GetEnvironmentVariableW, GetFileTime, GetModuleFileNameW, GetModuleHandleA, GetModuleHandleW, GetProcAddress, GetProcessHeap, GetSystemTimeAsFileTime, GetThreadContext, GetTickCount, GetVersionExW, GetVolumeInformationW, GetWindowsDirectoryW, GlobalAlloc, GlobalFree, GlobalLock, GlobalReAlloc, GlobalSize, GlobalUnlock, HeapDestroy, LoadLibraryA, LoadLibraryW, LocalFree, lstrcatW, lstrcmpiW, lstrcpy, lstrcpyW, lstrlen, lstrlenW, MapViewOfFile, Module32FirstW, Module32NextW, MoveFileExW, MultiByteToWideChar, NTDLL.RtlAllocateHeap, NTDLL.RtlFreeHeap, NTDLL.RtlGetLastWin32Error, NTDLL.RtlSizeHeap, OpenEvenW, Process32First, Process32Next, QueueUserAPC, ResumeThread, SetEnvironmentVariableW, SetErrorMode, SetEvent, SetFileAttributesW, SetFileTime, Sleep, TerminateProcess, UnmapViewOfFile, VirtualAlloc, VirtualFree, VirtualProtect, WaitForSingleObject, WriteFile

advapi32.dll

AdjustTokenPrivileges, CheckTockenMembership, ConvertStringSecurityDescriptorToSecurityDescriptorA, ConvertStringSidToSidA, GetSidSubAuthority, GetSidSubAuthorityCount, GetTokenInformation, LookupPrivilegeValueA, OpenProcessToken, RedEnumValueW, RegCloseKey, RegCreateKeyExW, RegDeleteValueW, RegFlushKey, RegOpenKeyExW, RegQueryValueExW, RegSetKeySecurity, RegSetValueExW

user32.dll

FindWindowA, GetKeyboardLayoutList, mouse_event, SendMessageA, wsprintfA, wsprintfW

shell32.dll

ShellExecuteExW

ole32.dll

CoInitialize, CreateStreamOnHGlobal

winhttp.dll

WinHttpCloseHandle, WinHttpConnect, WinHttpCrackUrl, WinHttpOpen, WinHttpOpenRequest, WinHttpQueryHeaders, WinHttpReadData, WinHttpRecieveResponse, WinHttpSendRequest, WinHttpSetOption

dnsapi.dll

DnsExtractRecordsFromMessage_W, DnsFree, DnsWriteQuestionToBuffer_W

shlwapi.dll

PathFindFileNameW, PathQuoteSpacesW, PathRemoveBackslashW, PathRemoveFileSpecsW, StrChrW, StrRChrW, StrToIntW


 
As you can see, the authors use many uncommon or undocumented API functions.

There are some special cases matched by RegEx, where the authors use NTDLL.Rtl functions from the kernel32.dll library and the Andromeda loader had to load the ntdll.dll again and use proper pointers for the Rtl API functions.

api_redirect_regexp.png

After resolving all hard-coded DLLs and API functions, the loader continues to final payload Entry Point.

 

jump_to_final_payload.png

Final Andromeda payload

Although the final payload is very small (~24 kb), the code is very complex and sophisticated. The authors, again, use a variety of anti-emul and anti-vm tricks.

At the very beginning, Andromeda disables Windows error notifications via the SetErrorMode API function with 0x8007h parameter, which means SEM_FAILCRITICALERRORS, SEM_NOALIGNMENTFAULTEXCEPT, SEM_NOGPFAULTERRORBOX, SEM_NOOPENFILEERRORBOX.

payload_errormode.png

Anti-VirtualMachine protection

Andromeda uses a simple and well-known anti-vm trick that compares the names of running processes with a “black list” of prohibited process names stored as CRC32 hashes.

List of forbidden process names:


99DD4432

vmwareuser.exe  

2D859DB4

vmwareservice.exe

64340DCE

vboxservice.exe  

63C54474

vboxtray.exe  

349C9C8B

sandboxiedcomlaunch.exe

3446EBCE

sandboxierpcss.exe  

5BA9B1FE

procmon.exe

3CE2BEF3

regmon.exe  

3D46F02B

filemon.exe

77AE10F7

wireshark.exe

0F344E95D

netmon.exe  

2DBE6D6F

prl_tools_service.exe

0A3D10244

prl_tools.exe

1D72ED91

prl_cc.exe

96936BBE

sharedintapp.exe

278CDF58

vmtoolsd.exe  

3BFFF885

vmsrvc.exe  

6D3323D9

vmusrvc.exe  

0D2EFC6C4

python.exe

0DE1BACD2

perl.exe

3044F7D4

avpui.exe


 

This procedure is implemented through the classic API functions, CreateToolhelp32Snapshot and Process32First / Process32Next. If the malware reveals a forbidden running process, the execution flow ends in an infinite loop.

anti-vm_protection.png

An interesting feature is the possibility of creating a special key in the registry, which allows Andromeda to infect the system even with a running blacklisted processes.

The process blacklisting functionality is ignored when “is_not_vm” key is present inside the "HKEY_LOCAL_MACHINE \ SOFTWARE \ Policies" registry and when the proper UserID (DWORD) is set.

isnotvm.png

Persistence

The techniques to persist the infection and to camouflage the Andromeda PE binary among regular system binaries are well designed. All communication goes through an injected system application - msiexec.exe, which is a part of the standard Windows Installer.

Andromeda copies itself to the %ALLUSERPROFILE% folder and renames the binary to "ms {random [az] {5}}.exe” where the UserID is used as a seed for the RtlRandom API function.

ms_random_name.png

Later, the resulting file’s attributes are set to “FILE_ATTRIBUTE_HIDDEN” and “FILE_ATTRIBUTE_SYSTEM” (+h +s) and the file time is set to the file time obtained from the original msiexec.exe file. The well known functions - GetFileTime and SetFileTime are used.

hiddenatrib.png

Another trick used by the authors is deleting the NTFS stream bound to the file. They call the DeleteFile API to remove the :Zone.Identifier flag from the newly created ms*.exe file (to bypass the “File Downloaded from the Internet” warning).

zoneidentifier.png

In the next step, Andromeda prevents the displaying of hidden files via the registry key "Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced" and sets proper “Hidden” and “ShowSuperHidden” values.

registry_hidden_set.png

Finally, Andromeda creates a new value (UserID) inside the “Software\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run” registry key and sets the path to the previously created “ms*.exe” file. After that, it protects the value by changing the permissions through Security Descriptors. Andromeda tries to avoid modifications or deleting of this value, however, modern AV engines are able to bypass this restriction.

registry_persistence.png

reg_persist_permissions.png

 

Injection of msiexec.exe and system API function hooks

The entire final payload is injected to a newly created msiexec.exe process and activated via the ResumeThread API function. The original payload process is terminated after a new thread activation and the malware only continues from the injected msiexec.exe process.

injected_msiexec.png

Andromeda also injects ntdll.dll and ws2_32.dll system libraries. Inside ntdll.dll Andromeda hooks the NtMapViewOfSection API function and replaces it with a jump to payload, and  also hooks the GetAddrInfoW API function inside the ws2_32.dll library.

injected_ntmapviewofsection.png

injected_ws2_32_getaddrinfow.png

Both API hooks are resolved inside the payload and then jump to the affected API functions after being replaced by jmp instructions.

Part of the resolved code for the GetAddrInfoW API function by payload:

getaddrinfow_resolved.png

Language exclusions

Another interesting feature is the detection of keyboard layout settings. If Andromeda detects the Russian, Ukrainian, Belarusian or Kazakh keyboard, it sets a special flag that disables the infection, persistence, NTP traffic and injection of ntdll and ws2_32 libraries.

keyboard_check_exclude.png

The malware is also completely removed from the infected machine if it detects one of these keyboard layouts.

NTP traffic

Andromeda uses hardcoded NTP (Network Time Protocol) domains to obtain the current time, which is  received by the “Transmit Timestamp”, if this connection isn’t successful the current time is obtained from infected computer.

pool_check_NTP.png

The hardcoded NTP domains are africa.pool.ntp.org, asia.pool.ntp.org, europe.pool.ntp.org, oceania.pool.ntp.org and pool.ntp.org as the last attempt if the other domains fail. NTP traffic uses port 123.

The malware verifies if the size of the received data is 0x30h (48) bytes and first parses DWORD from the “Transmit Timestamp” value.

ntp_luparam.png

This value is increased by 0x7C558180h and the result is used as an argument of the “aStart” function exported by a plugin.

plugin_ntp_astart.png

If all connections to the NTP domains fail, an argument for the aStart function is computed by the payload via the following algorithm based on the result of the GetSystemTimeAsFileTime API function, instead of the Transmit Timestamp value from the NTP request.

ntp_fake_1.png

The  “compute_aStart_arg” function algorithm:

ntp_fake_2.png

Obtain local IP via sockaddr struct

Andromeda uses a very uncommon method to obtain local IP addresses of infected machines.

The malware tries to connect various legal servers on port 80 with a crafted socket and obtain the infected machine’s IP address from the sockaddr structure via the getsockname API function.

sockaddr.png

The resolved value is used as “la” parameter for C&C requests.

List of domains that  Andromeda tries to connect to in the following order: update.microsoft.com, microsoft.com, bing.com, google.com, yahoo.com

C&C communication

All communication is RC4 encrypted and uses HTTP/1.1 in the raw data format “Content-Type: application/octet-stream” with predefined “Mozilla/4.0” User-Agent.

cac_traffic.png

Andromeda contains a hard-coded RC4 key, which is used for C&C server communication, for the downloaded plugin decryption and also for decrypting hard-coded C&C URLs where the key is used backwards.

All values are hardcoded to a structure located in the beginning of payload data. The first value is BID (Botnet/BuildID), which is also used as a parameter for C&C requests. RC4 key is hard-coded between random junk data and is followed by encrypted C&C URLs. The first byte of each encrypted URL is the length of data and it is used as a pointer to the next encrypted URL. Zero byte indicates the end of an encrypted URL data block.

encrypted_urls.png

C&C JSON requests

Andromeda uses JSON format for all communication with C&C servers encrypted with RC4.

parameters.png

The malware includes two types of JSON requests and one command object.

Infection report / Ask for action request

{"id":%lu,"bid":%lu,"os":%lu,"la":%lu,"rg":%lu}


JSON item

Name

Info

id

User ID

Computed from VolumeSerialNumber of infected machine HDD.

bid

Botnet/Build ID

Hard-coded inside Andromeda payload.

os

OS version

Version of current operating system.

la

Local IP address

Obtained from sockaddr structure.

rg

Administrator rights

Set 1 if malware process runs under an administrator account.


 

Live example:

{"id":1839815145,"bid":8384538,"os":65889,"la":168732589,"rg":0}

Received command object from C&C server

[sleep_before_request, {unused_object}, [TaskID, RequestType, URL,..]..]


Object item

Info

sleep_before_request

Sleep time in minutes before send next request to the C&C server, the most common value is 60.

{unused_object}

When this object is found, it is skipped. The most common value is {“klt:0”}.

TaskID

ID of a task provided by the C&C server. This ID is send back to server with status/error report request.

RequestType

Identifier of the task type (update plugin, download exe, install plugin, delete bot)

URL

URL for downloading plugin or other malware.


 

Live example of a command to download Andromeda plugins:

[60,{"klt":0},[15,2,"http:\/\/netcologne.dl.sourceforge.net\/project\/googlecodefork\/g11.pack"]]

Task report request

{“id”:%lu, “tid”:%lu, “err”:%lu, “w32”:%lu}

JSON item

Name

Info

id

User ID

Computed from VolumeSerialNumber of infected machine HDD.

tid

TaskID

ID of task provided by the C&C server.

err

Error

Set 0 if task is successfully completed.

w32

System error code

Obtained from RtlGetLastWin32Error API function.


 
Live example:

{"id":1839815145,"tid":15,"err":0,"w32":127}

C&C servers

The Andromeda payload uses two domains as C&C servers for a very long time period and requests are sent via POST method.

Server one:

hxxp://disorderstatus.ru/order.php

Server two:

hxxp://differentia.ru/diff.php

Both domains are connected to multiple DNS servers located throughout the world.

Below is the differentia.ru DNS graph up to the April 2016 hosted on pointhq.com servers:

differentia_graph.png

differentia_map.png

The above map shows where the servers are located.

List of “A” IP domain records:

IP

Hosted by

Location

46.4.114.61

Hetzner Online GmbH

Germany

95.213.192.71

Selectel Net

Russian Federation

176.9.48.86

Hetzner Online GmbH

Germany



 

The below shows a DNS graph of the differentia.ru domain hosted on Hurricane Electric servers, where the authors currenlty moved the entire network infrastructure.

he_graph.png

differentia_map_current.png

 

Complete current DNS record of differentia.ru:

NS

ns1.he.net

216.218.130.2

United States CA Fremont Hurricane Electric HURRICANE-1

SOA

ns1.he.net

216.218.130.2

 

NS

ns2.he.net

216.218.131.2

 

NS

ns3.he.net

216.218.132.2

 

NS

ns4.he.net

216.66.1.2

United States CA Fremont Hurricane Electric HURRICANE-6

NS

ns5.he.net

216.66.80.18

 

A

95.213.186.51

 

Russian Federation SELECTEL-NET SELECTEL OOO "Network of data-centers "S RU-SELECTEL-20090812

A

176.9.174.220

 

Germany HETZNER-RZ-FKS-BLK4 HETZNER-AS Hetzner Online GmbH DE-HETZNER-20110517


 

Statistics of blocked differentia.ru domain:

differentia_graph_guids.png

differentia_graph_hits.png

Downloaded plugins includes other C&C server domains:

atomictrivia.ru, designthefuture.ru, gvaq70s7he.ru, getuptateserv.eu,..

Andromeda Plugins

This malware is modular and Andromeda offers several plugins like Keylogger, Browser Formgrabber, Rootkit, Hidden TeamViewer remote control, etc. We are preparing a detailed analysis of the all modules which we will publish at a later date.

The plugins are hosted and downloaded from the Source Forge repository.

sf1.png

The authors recently updated the plugin files, repacked binaries with PE packers and changed their file names. This Source Forge project was registered on 2015-05-16 under “dofeedthetrolls” username.

sf2.png

Plugin encryption

The plugin binaries are twice encrypted with RC4 encryption and compressed by Aplib. Each plugin contains 43 bytes of config header, with a hard-coded RC4 key, CRC32 hashes and data length values for validation and a parameter for the case the plugin is stored in the registry.

Encrypted plugin header:

plugin_header_encrypted.png

Decrypted plugin header:

plugin_header_decrypted.png

Decrypting the plugin is a bit tricky:

  1. Decrypt header (43 bytes) with a RC4 encryption key from the Andromeda payload (used for C&C communication).
  2. The first DWORD value is the XOR key to decrypt the config header values.
  3. The first 16 bytes are the RC4 key to decrypt the plugin.
  4. Decompress (Aplib) decrypted data.

plugin_decrypt.png

Plugin persistence

Downloaded plugins are stored in the registry and  in the %TEMP% directory under two file names.

The first file name is saved in the following format: %TEMP%\KB{GetTickCount}.exe


plugin_kb.png

The second file name is %TEMP%\cdo*.dll

plugin_cdo.png

The Andromeda payload also searches for three plugin exports aStart, aUpdate and aReport via the GetProcAddress API function.

Conclusion

Andromeda malware has very long history. It’s one of the most prevalent malware families and nothing indicates that it will disappear anytime soon. The authors are skilled programmers and operators, recently updating plugins, maintaining entire systems and looking for new infected domains with Exploit Kits. Analyzing Andromeda's very complex ecosystem is a challenging task, but we're investigating it further. Stay tuned for the next blog post!

--> -->