[Loki-Bot] Stealing Credentials

11 minute read

File Summary

Wiper  
MD5 3F4A16B29F2F0532B7CE3E7656799125
File Type PE32 executable
Driver  
MD5 4578b188645f157291b8081faf680a4a
File Type PE32 executable
Loki-Bot  
MD5 1d53235d615474053d0520c249adb4d5
File Type PE32 executable

Analysis Summary

  • This sample uses CVE-2017-11882 which is a buffer overflow in Microsoft Equation Editor (EQNEDT32.EXE) and acts as a downloader.
  • The downloaded executable uses the process injection technique to inject itself into a process called vbc.exe.
  • It moves itself into a specific folder inside %AppData% and then modifies the registry with the folder’s path to establish persistence, it also makes this folder invisible.
  • It uses Import By Hash technique to provide a layer of obfuscation.
  • It creates a Mutex with a unique name which is the MD5 hash of MachineGuid value to avoid reinfecting the machine.
  • It steals a huge amount of data that victim’s machine stores such as browser credentials, Windows credentials …. etc
  • It sends extra data such as OS version, Username, Machine Name ……

Delivery

LokiBot can be delivered via a phishing email that contains a malicious document attachment.

phishing

Behavior Analysis

0

When this document is launched, it will download an executable called regasm.exe from thdyrusschine2mapanxmenischangednethnbc.ydns[.]eu

2

Then it will save at C:\Users\Public and rename it to vbc.exe and execute it.

1

Then, It sends some data to the C&C domain http://begadi[.]ga/. It looks like the username, PC-name of the victim’s machine, and other encrypted data.

3

Unfortunately, the C2 domain is down but I still could explain some details inside.

Stage 1: Downloader

This sample uses CVE-2017-11882 which is a buffer overflow in Microsoft Equation Editor (EQNEDT32.EXE) and acts as a downloader. Let’s represent a little explanation of buffer overflow.

When a normal function is called, the EIP of the next instruction is saved into the stack so after returning to the main function, the program knows where to go next.

This sample uses this technique to change the EIP to redirect the flow of execution to another way.

This is a simple example of BOF (The program’s flow is redirected to 0x00443015).

4

We can add a key named EQNEDT32.EXE at Image File Execution Options in the registry to intercept calls to an executable for debugging.

5

Here on our debugger, it redirects the flow to execute URLDownloadToFileW function to download the LokiBot and then execute it.

6

Stage 2: Process Injection

Now, it is packed but we can unpack it easily as it writes the PE file of the unpacked payload into memory. So we just need a breakpoint at WriteProcessMemory

7

Stage 3: Loki-Bot

Import By Hash

First, it executes a function labeled GetCommandLine that returns the full command line of the executable. Then, It gets some arguments that this command line has been using GetCommandLineToArgV.

8

It doesn’t call these APIs directly but uses Import By Hash technique so let’s dig a little inside GetCommandLine. Here it passes 4 arguments to a function labeled Get_DLL_API_From_Hash_Index. We focus on the first 2 arguments.

Argument Description
Arg 1 Index of DLL that contains the function (0 in this example)
Arg 2 Hash of API to be called (0x0EEF0D05E )

9

Now let’s dig inside this function. Here it passes the hash and index inside another function labeled Get_API_From_Hash. Then It calls 2 functions labeled Get_DLL_From_Index & Search_API_From_Hash

10 11

Briefly, It gets the required DLL, hashes every API inside it using its name, and then compares between every hash and desired hash. We will explain that in detail.

Let’s dig inside Get_DLL_From_Index. Here it builds an array of DLLs dynamically and I noticed that it leaves 2 elements at the beginning of the array.

12

Here it passes a hash to function labels Get_DLL_By_Hash, so It gets the first 2 elements by hashing technique. In this case, it will pass 0x0F96AF9CE to get KERNEL32.dll.

13

Now, let’s dig more inside Get_DLL_By_Hash. First, It gets the address of PEB (Process Environment Block). Then, it moves to offset 0xC that is called _PEB_LDR_DATA structure. Finally, it gets the DLL name from a structure called InLoadOrderModuleList.

The Process Environment Block (PEB) is a user-mode data structure that can be used by applications (and by extend by malware) to get information such as the list of loaded modules, process startup arguments, heap address, check whether the program is being debugged, find image base address of imported DLLs,…etc

14

For more information here

Here, It loops through all loaded DLLs inside the executable, hashes them and compares them with the passed hash (0x0F96AF9CE), passes DLL name and its length to function labeled Calculate_Hash, and compares the result with 0x0F96AF9CE

15

Now, it has KERNEL32.dll address. It passes 2 arguments to Search_API_From_Hash.

Argument Description
Arg 1 DLL DOS HEADER (DLL Address)
Arg 2 Hash of API to be called (0x0EEF0D05E)

According to PE structure, It gets the offset of Export Table of DLL by adding 0x3C (e_lfanew offset) and 0x78 (pointer to export table offset) to the DOS HEADER address.

Export_Table = DOS_HEADER_Address + 0x3C + 0x78

Then, It uses Calculate_Hash function again to get all API hashes and then compares them with the desired API hash 0x0EEF0D05E.

16

Creating Mutex

As we know, lots of Malwares create a Mutex for the first time on the victim’s machine to avoid reinfecting it again. The Mutex name is unique from one machine to another that depends on MachineGuid value.

17

Let’s have more details. It executes a function labeled Get_Mutex_Name at 0x413982 that returns 48 bytes of MD5-hash value of MachineGuid in Unicode form. Then creates a Mutex using CreateMutexW.

If this Mutex already exists, the malware terminates itself.

18

Now, lets examine Get_Mutex_Name function. It executes function at 0x413DA0 labeled Get_MachineGuid_Hash_unicode.

Querying Registry

Inside, it executes another function labeled Reg_Open_Query_Close_Key that opens, queries, closes a key in registry HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography that has the MachineGuid value.

19 20

More deeper, It uses RegOpenKeyExA, RegQueryValueExA, RegCloseKey.

21

Generating Hash

Now, It passes the MachineGuid value and its length to a function labeled Get_MachineGuid_MD5_Hash at 0x4065D2 to generate the hash value.

22

First, It calls CryptAcquireContextW to acquire a handle to a particular key container within a particular cryptographic service provider (CSP).

23

In Microsoft Windows, a Cryptographic Service Provider (CSP) is a software library that implements the Microsoft CryptoAPI.

Then, it creates an MD5-hashing handle using CryptCreateHash that will be used in CryptHashData to hash data.

24

Finally, it retrieves the actual MD5-hash using CryptGetHashParam.

25

78

Converting To UTF-8

After getting the hash value, it calls convert_Ascii_2_UTF8 at 0x4065E4 that converts the hash value to UTF-8 form

UTF-8 is a variable-width character encoding that represents a character in 8 bits (2 bytes). Unlike ASCII that uses 1 byte only.

26

Here, it uses MultiByteToWideChar to convert ASCII to UTF-8.

27

77

Final Name

Finally, It gets 48 bytes only of the generated MD5-hash.

28

79

mov esi, [Hash]    |
mov ecx, 0xC       | ===> memcpy(Dst, Hash, 48)
rep movsd          |

Stealing Data

Now, the malware has successfully checked for initialization data and created its Mutex, it is time to start doing some bad things. This malware steals a huge amount of data such as browser credentials, Windows credentials …. etc

As shown, each function is responsible for stealing specific application credentials. It passes the function address and its ID to a labeled function execute.

29 30

This is a list of some applications that Loki-Bot steals:

    Application        
Mozilla Firefox BlackHawk QupZilla SuperPutty FTPNow Cyberduck NETFile
Comodo IceDragon Lunascape Internet Explorer FTPShell Xftp fullsync GoFTP
Safari Comodo Dragon Cyberfox NppFTP JaSFtp LinasFTP ALFTP
K-Meleon OperaOLD PaleMoon MyFTP EasyFTP FileZilla DeluxeFTP
SeaMonkey OperaNEW Waterfox FTPBox SftpNetDrive StaffFTP FTPGetter
Flock QtWeb Pidgin SherrodFTP AbleFtp BlazeFtp WS_FTP
ExpandDrive Steed FlashFXP NovaFTP NetDrive SmartFTP BitviseSSH
Google Chrome Outlook 1Password        

I tried on Mozilla Firefox only so let’s dig into it.

Getting Version

First, it retrieves the Firefox version from the registry using a function labeled Get_Reg_Value0.

31

32

According to this article The Secrets of Firefox Credentials, credentials are stored using different methods depending on Firefox Version. So the malware needs to know its version to get the stored credentials.

Then, it checks for the architecture that Firefox is running on by searching for x64 using StrStrW, Here, I’m using Windows (32-bit) version.

33 34

And now, it uses a function labeled Concatenate_With to concatenate strings together so here it gets another path inside the registry.

36

Then, It retrieves a value called Install Directory to know where firefox is installed using Get_Reg_Value1.

35

37

Getting APIs

After that, it calls a function labeled Get_NSS_APIs that will be used to load NSS3.DLL. Let’s dig inside

NSS3.DLL provides a complete open-source implementation of the crypto libraries used by Firefox. They are used to decrypt passwords stored in Mozilla-based browsers

38

First, it adds the Firefox Install Directory to the system’s PATH using GetEnvironmentVariable and SetEnvironmentVariable

39

Then, it gets the full path of NSS3.DLL by concatenating its name and Firefox path using Concatenate_With and checks if it exists using PathFileExistsW. Finally, it loads this DLL using LoadLibraryW.

40

Now, the malware calls GetProcAddress to get the addresses of the following APIs:

NSS_Init            PK11_GetInternalKeySlot
NSS_Shutdown        PK11_CheckUserPassword
PK11_FreeSlot       PK11_Authenticate
PK11SDR_Decrypt

41

These APIs will be used later to decrypt stored credentials.

Getting Profile Path

The malware has all the needed APIs to decrypt credentials, it will extract them from Firefox’s database (Profiles). Let’s dig into Get_Profile_INI_Credentials.

42

It gets %APPDATA% path and then it builds an array of paths/filenames which are related to the profile locations of several different types of Mozilla-based software.

43

Profile paths  
%s\Mozilla\Firefox\profiles.ini %s\Flock\Browser\profiles.ini
%s\Mozilla\Firefox\Profiles\%s %s\Flock\Browser\Profiles\%s
%s\Mozilla\SeaMonkey\profiles.ini %s\Thunderbird\profiles.ini
%s\Mozilla\SeaMonkey\Profiles\%s %s\Thunderbird\Profiles\%s
%s\K-Meleon\profiles.ini %s\Comodo\IceDragon\profiles.ini
%s\K-Meleon\%s %s\Comodo\IceDragon\Profiles\%s
%s\NETGATE Technologies\BlackHawk\profiles.ini %s\Postbox\profiles.ini
%s\NETGATE Technologies\BlackHawk\Profiles\%s %s\Postbox\Profiles\%s
%s\8pecxstudios\Cyberfox\profiles.ini %s\Moonchild Productions\Pale Moon\profiles.ini
%s\8pecxstudios\Cyberfox\Profiles\%s %s\Moonchild Productions\Pale Moon\Profiles\%
%s\FossaMail\profiles.ini  
%s\FossaMail\Profiles\%s  

Here, it will deals with %s\\Mozilla\\Firefox\\profiles.ini, %s\\Mozilla\\Firefox\\Profiles\\%s

It calls Concatenate_With to concatenate %APPDATA% path and profiles.ini. Then it will check if it exists using PathFileExistsW

44

Each profile is actually a folder that contains all credentials inside, so the malware will search for the profile path using the initialization file ini.

45

It starts with Profile0 and then passes 3 arguments to Get_INI_ProfileName that will use GetPrivateProfileStringW to retrieve the path of profile0.

Argument Description
arg1 ini file
arg2 Section to be read from
arg3 Key name which retrieves a value from
46 47

Then, it will concatenate the result with the firefox Installation Directory to get the full path of the profile.

48 49

Getting Browser Credentials

It’s time to get credentials. The malware passes the profile full path to a function labeled Get_ALL_Credentials at 0x408FF4. Let’s go deeper inside.

50

It will search for 2 files and check if they exist (Depending on Firefox Version) as they contain encrypted credentials:

signons.sqlite
logins.json
  • Starting in Firefox 32.0, signons.sqlite is no longer used and the file logins.json is used instead.
  • The primary difference is the malware makes a SQL query first as signons.sqlite is actually a database. Unlike logins.json, it parses its contents and decrypts the credentials directly.

It passes the full path of logins.json file to another function labeled Get_LoginJSON_Credentials at 0x40A2AB.

51

Getting inside, it parses the contents of logins.json using a function labeled Get_LoginJSON_File_Contents

52

53

Now, it will search for encryptedUsername and encryptedPassword and passes them to a function labeled Decrypt_Credential to decrypt.

54 55 56

It will use NSS3 APIs such as: PK11_GetInternalKeySlot, PK11_Authenticate, PK11SDR_Decrypt, PK11_FreeSlot

57

Sending Data

After getting all credentials, It executes a function labeled Get_Data_To_Send at 0x414325 to send the following to C2 server:

  • OS Version
  • Username - MachineName
  • Screen Resolution
  • Verify if Local Admin
  • Mutex Name
  • Stolen Credentials

Let’s represent a little explanation of how to collect these data:

First, it allocates 5000 bytes of memory using Heap_alloc and starts with OS Version using a function labeled Get_OS_Ver

58

Go deeper inside, It executes a function called RtlGetVersion that gets all information about OS such as Major and Minor version, Build Number and CSDVersion

59

60

Next, it gets the Username and machine name via Get_UserName at 0x4143E3 and GetComputerNameW at 0x414414.

I noticed that it uses a function labeled Add_Inf_To_Send to add this information into the buffer that it’s allocated previously.

61

Now, it adds the domain name by calling a function labeled Get_Domain_Name

62

For the Malware to obtain the user’s domain, it first obtains a handle to the current thread or process via calls to either:

  • GetCurrentThread, OpenThreadToken

  • GetCurrentProcess, OpenProcessToken

Then, it gets the domain name via LookupAccountSidW. My machine is not connected to any domain so the return value will be the Computer Name and added to the buffer using Add_Inf_To_Send.

63

64

It gets the current screen resolution using a function labeled Get_Screen_Resolution that executes:

  • GetDesktopWindow
  • GetWindowRect
65 66

67

Now it is time to determine whether the current user is a local administrator via a function labeled Is_Local_Admin. It gets the current username and passes it to NetUserGetInfo that determines the privilege level of the current user.

Then it compares the 4th element (privilege level) with 2 that represents Administrator

Finally, returns 1 if it’s admin or 0 if not.

68

After collecting the required data into a buffer, it passes this buffer and its length to a function labeled Send_And_Recv at 0x414570 that briefly decodes:

  • Hostname
  • Port Number
  • User-Agent

and then, it sends data to the C2 server.

69

Getting little details. First, it decrypts the hostname using a function labeled Get_C2_Hostname at 0x414286

70 71

Then, it decrypts the port number and User-Agent using Get_Port_Number, Get_UserAgent

74

72 73

Finally, it sends 2 packets of data using a function labeled SendData at 0x414136 and 0x414136:

  • HTTP Header
  • Required data (Username - Computer Name - Domain Name - Mutex Name - stolen credentials)

75

76

I think this enough for Loki-bot

IOCs

Type Data
Hash 4de7a80dd324e365cd1e23ebfc0cd844
  4578b188645f157291b8081faf680a4a
  1d53235d615474053d0520c249adb4d5
URL thdyrusschine2mapanxmenischangednethnbc.ydns[.]eu
C2 Domain begadi[.]ga
File C:\Users\Public\vbc.exe

References

Corkami

Finding DLL Name from the Process Environment Block

The Secrets of Firefox Credentials

MSDN: RtlGetVersion GetWindowRect NetUserGetInfo