$tealer

7 minute read

  • Challenge Link:

Cyber Defender


Scenario

Your enterprise network is experiencing a malware infection, and your SOC L1 colleague escalated the case for you to investigate. As an experienced L2/L3 SOC analyst, analyze the malware sample, figure out what it does and extract the C2 server and other important IOCs.


Basic Analysis

$tealer is a PE32 executable (DLL) (console) Intel 80386, for MS Windows

It kinda has no interesting strings except:

ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
Sleep
OutputDebugStringA
KERNEL32.dll

It is unpacked with four sections and two imported API as noticed from PE-bear.


We knew that it is unpacked from PEID and there is more we can know, Using the KANAL plugin we can know if there is any cryptography and where:

aPLib :: 0000AC81 :: 005FAC81
  The reference is above.
BASE64 table :: 0003A260 :: 0062A260
  The reference is above.
CRC32 [poly] :: 0003A310 :: 0062A310
  Referenced at 0061D659
  Referenced at 0061D6B6
  Referenced at 0061D836
  Referenced at 0061DAA8
CRC32 [poly] :: 0003A314 :: 0062A314
  The reference is above.
CRC32 [poly] :: 0003A318 :: 0062A318
  The reference is above.
CRC32 [poly] :: 0003A31C :: 0062A31C
  The reference is above.

So now I know that malware uses CRC32(Cyclic Redundancy Check 32) and BASE64 table

here I tried to run the file but it seems to be using some evasion technique

Now it is IDA time.


Advanced Analysis

I noticed before that it only imports KERNEL32.dll which doesn’t make sense, so it must be hiding API calls.

The first thing to take your eye is the strange function calls at EP

and the most interesting is the arguments passed for fun_2

from decompiler noticed that function sub_6015C0 takes the same arguments twice and its return value will be called as a function, it sounds like API-Hashing. To be sure let’s dive into the function and see what is happening.

here, declare vars, assign some value to V2 then make some comparisons and search for the second argument.

then at label 18 first argument is passed to sub_607564 which in my assumption must be a dll name so this function should get the dll name corresponding to the given hash to use it to resolve the module name later.

in this function -ResolveDllName- again the hash is assigned to variable v2 then some noisy comparisons happen, after that, there is access to PEB (ProcessEnvironmentBlock).

from PE it accesses Ldr, as you seeida identified PEB & Ldr as structs but failed with Forward and Backward links so it must be reset.

now it gives a better view, so I now know it’s trying to access BaseDllName which is struct as well

//0x8 bytes (size)
struct _UNICODE_STRING
{
    USHORT Length;                                                          //0x0
    USHORT MaximumLength;                                                   //0x2
    WCHAR* Buffer;                                                          //0x4
}; 

here we notice that the name is not accessed directly but after 0x4 b as there are 2 struct members before it. now malware access buffer -to get the name- and the length in the infinite loob while(1), then it will loop again on dll name and convert to lower case by subtracting 32 from each character.

After that it checks the hash dll with the generated hash if equals breaks the infinite loop, so basically the function sub_61D620 is the one responsible for hashing.

Here we need to notice 2 things:

  • The returned value is XORed with 0x38BA5C7B as the second stage encryption.
  • The hashing algorithm is CRC32 as known from KANAL previouslly.

now after resolving the dll name malware will return the dll base address. If this case faces a false condition, the malware will access the next dll using Flink and repeat the loop.

now back to our function -ResolveDllName- we find that it returns the value of function sub_6067C8 which takes two args, we can assume those are (Dll_Base, APIHash). Need to get deeper into this function.

it started the comparisons and then went to the access export table.

To do that it:

  • goes to e_lfanew in _IMAGE_DOS_HEADER
  • from e_lfanew goes to _IMAGE_NT_HEADERS
  • from NT it moves 0x18 to access _IMAGE_OPTIONAL_HEADER
  • from optional it moves0x60 to access _IMAGE_DATA_DIRECTORY that make 0x78
  • finally access VirtualAddress where our desired EXPORT_DIRECTORY

finally, xor with the same key.

now, using hashdb with CRC32 algorithm and 0x38BA5C7B XOR key you can resolve any API and DLL as you want.

now after getting how the API is resolved, we need to analyse the functionality. let’s start from the entry point with our first call sub_607980. Diving into this function find that: this function simply resolves ntdll and imports RtlAddVectoredExceptionHandler API which Registers a vectored exception handler. That means our malware tries to achieve the Vector Exception Handle technique.

next go to KERNEL32DLL and import

  • CreateThread
  • TerminateProcess
  • FreeConsole
  • VirtualFree
  • ExitThread

using CAPA to give a quick hint on its capablties found that:

ATT&CK Techniques and MBC Behaviors

ATT&CK Tactic ATT&CK Technique MBC Objective MBC Behavior
DEFENSE EVASION Obfuscated Files or Info CRYPTOGRAPHY Encrypt Data::RC4 [C0027.009]
  [T1027]   Encryption Key::RC4 KSA [C0028.002]
      Generate Pseudo-random Sequence::RC4 PRGA [C0021.004]
DISCOVERY Process Discovery [T1057] DATA Check String [C0019]
      Encoding::Base64 [C0026.001]
EXECUTION Shared Modules [T1129]   Encoding::XOR [C0026.002]
      Disable or Evade Security Tools::Heavens Gate [F0004.008]
      Obfuscated Files or Info::Encoding-Standard Algorithm [E1027.m02]

CAPABILITIES

  • 64-bit execution via heavens Gate (7 matches) [anti-analysis/anti-disasm]
  • reference Base64 string [data-manipulation/encoding/base64]
  • encode data using XOR (8 matches) [data-manipulation/encoding/xor]
  • encrypt data using RC4 KSA [data-manipulation/encryption/rc4]
  • encrypt data using RC4 PRGA [data-manipulation/encryption/rc4]
  • print debug messages [host-interaction/log/debug/write-event]
  • get process heap force flags [host-interaction/process]
  • access PEB ldr_data [linking/runtime-linking]
  • enumerate PE sections (3 matches) [load-code/pe]
  • parse PE exports (4 matches) [load-code/pe]
  • parse PE header (21 matches) [load-code/pe]

first, for RC4 we need a key, sub_61E5D0 is one using RC4 using Xref we find that the argument function takes always data stored in .rdata where the first 40 byte is the key.

now the entry point has ended but there may be another hidden call, so I used Xref one more time to check where is api_resolving function is called and get all API calls. I found two functions remain without resolving sub_623370 and sub_623820

after resolving them found WININET_DLL:

  • InternetCloseHandle
  • InternetConnectW
  • InternetQueryOptionW
  • HttpQueryInfoW
  • HttpSendRequestW

so there must be a connection with the c2 server, to get all information about network-based indicators found first that InternetConnectW is resolved in function sub_623370. This one especially accepts the lpszServerName parameter which points to the host name or IP address. So now I need to find where the sub_623370 is called using Xref then search for arguments passed and get ip.

following these functions in reversed order till I found ip extraction:

that do while loop os where IPs are extracted from the .data section. going to word_62B02B in memory found:

As known, IPv4 is 4-byte so all we need now is to convert from int to ip. Use any converter you will get :

In the same way get the port from the next 2-bytes : 0x0BB1 => 443

In the same way with the rest memory you will find four IPs.

No IP Address Port Number
1 192.46.210.220 443
2 143.244.140.214 808
3 45.77.0.96 6891
4 185.56.219.47 8116

tools

  • PE-bear
  • peid & KANAL plugin
  • floss.exe
  • IDAPRO7.7
  • capa plugin
  • hashdp plugin

Resourses


Written by

Karim Gomaa