Blog

Cerbero Suite: The Hacker’s Multitool

We would like to thank InQuest for this interesting malware sample: it’s a great sample to show the power of Cerbero Suite!

  • MD5: 59C38D612200A9FDE900366872C1F051
  • SHA-1: 478454F501C1AD52F3D31E52D54DA514442E83C6
  • SHA-256: 6DD6C65DA87CE383B9B0D6CA39CFD76C90F6D65B4C09CFC88352ED60EC96809A

InQuest Labs, VirusTotal

The first thing we notice when opening the malicious document with Cerbero Suite is that it contains VBA code. If we glance over the code, we reach an interesting part.

A malicious document opened with Cerbero Suite that contains VBA code

This part of code dumps a VBE script to disk and executes it. VBE files are encoded VBS scripts and Cerbero Suite automatically decodes these scripts into readable VBS code.

In order to find the encoded script, we go to the “sdghfgjfgkgkghk.o” stream. If you don’t know where the encoded script begins, you can just advance the cursor in the hex view and keep on pressing “Ctrl+E” until Cerbero automatically detects the encoded script.

Code screenshot

Once we find the start of the VBE script, we press “OK” to load add the embedded object. Now we can inspect the child VBE object! As already mentioned, Cerbero automatically decodes the file so that we can analyze the VBS code.

VBS Code screenshot

We notice that the VBS code contains a very big string which looks like base64.

ewfwegwh = "VFZxUUFBTUFBQUFFQUFBQS8vOEFBTGdBQUFBQUFBQUFRQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUEwQUFBQUE0ZnVnNEF0QW5OSWJnQ" + _
"lRNMGhWR2hwY3lCd2NtOW5jbUZ0SUdOaGJtNXZkQ0JpWlNCeWRXNGdhVzRnUkU5VElHMXZaR1V1RFEwS0pBQUFBQUFBQUFEeXFwUVF0c3Y2UTdiTCtrTzJ5L3BEZGNTblE3dk" + _
"wra08yeS90RE1NdjZRM1hFcEVPM3kvcERkY1NsUTYzTCtrTjF4S0JEdDh2NlExSnBZMmkyeS9wREFBQUFBQUFBQUFBQUFBQUFBQUFBQUZCRkFBQk1BUU1BelZFQ1NBQUFBQUF" + _
"BQUFBQTRBQVBBUXNCQndvQW1nQUFBSDREQUFBQUFBQmNaQUFBQUJBQUFBQ3dBQUFBQUFBQkFCQUFBQUFDQUFBRkFBRUFCUUFCQUFRQUFBQUFBQUFBQUZBRUFBQUVBQUMzd1FR" + _
"QUFnQUFoQUFBQkFBQUVBQUFBQUFRQUFBUUFBQUFBQUFBRUFBQUFBQUFBQUFBQUFBQWdKMEFBSXdBQUFBQTBBQUFPSGtEQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQ" + _
"UFBREFTQUFBY0FBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBUUFBQXdBZ0FBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQU" + _
"FBQUFBQUFBQUFMblJsZUhRQUFBREltUUFBQUJBQUFBQ2FBQUFBQkFBQUFBQUFBQUFBQUFBQUFBQUFJQUFBWUM1a1lYUmhBQUFBNUJzQUFBQ3dBQUFBQkFBQUFKNEFBQUFBQUF" + _
"BQUFBQUFBQUFBQUVBQUFNQXVjbk55WXdBQUFBQ0FBd0FBMEFBQUFIb0RBQUNpQUFBQUFBQUFBQUFBQUFBQUFBQkFBQUJBc3FBQ1NFQUFBQUFzb1FKSVRRQUJBQ3loQWtoYUFB" + _
....

Following the huge base64 string, there are some lines of code which perform transformations on the string and dump the results to disk. We could use filters in Cerbero Suite to perform the same transformations and then decode the base64 string as a child object, but in this case it’s easier to just copy/paste the VBS code and let it decode the string.

ewfwegwh = [STRING]

ewfwegwh = Replace(ewfwegwh, Chr(45), Chr(43))
ewfwegwh = Replace(ewfwegwh, Chr(95), Chr(47))

Select Case (Len(ewfwegwh) Mod 4)
Case 0: ewfwegwh = ewfwegwh
Case 2: ewfwegwh = ewfwegwh + Chr(61) & Chr(61)
Case 3: ewfwegwh = ewfwegwh + Chr(61)

End Select
pfkewfnnwg = gewgsdrtyutyk(ewfwegwh)
pqwkdaiffewfgwerheejjej = gewgsdrtyutyk(pfkewfnnwg)

Function gewgsdrtyutyk(vsdvjtktk)
Const KMOgsBIDf = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
Dim rgfdb, sOut, groupBegin

vsdvjtktk = Replace(vsdvjtktk, vbCrLf, "")
vsdvjtktk = Replace(vsdvjtktk, vbTab, "")
vsdvjtktk = Replace(vsdvjtktk, Chr(32), "")

rgfdb = Len(vsdvjtktk)
If rgfdb Mod 4 <> 0 Then
Exit Function
End If

For groupBegin = 1 To rgfdb Step 4
Dim numDataBytes, CharCounter, thisChar, thisData, nGroup, pOut
numDataBytes = 3
nGroup = 0

For CharCounter = 0 To 3

thisChar = Mid(vsdvjtktk, groupBegin + CharCounter, 1)

If thisChar = Chr(61) Then
numDataBytes = numDataBytes - 1
thisData = 0
Else
thisData = InStr(1, KMOgsBIDf, thisChar, vbBinaryCompare) - 1
End If
If thisData = -1 Then
Exit Function
End If

nGroup = 64 * nGroup + thisData
Next

nGroup = Hex(nGroup)

nGroup = String(6 - Len(nGroup), Chr(48)) & nGroup

pOut = Chr(CByte(Chr(38) & Chr(72) & Mid(nGroup, 1, 2))) + _
Chr(CByte(Chr(38) & Chr(72) & Mid(nGroup, 3, 2))) + _
Chr(CByte(Chr(38) & Chr(72) & Mid(nGroup, 5, 2)))

sOut = sOut & Left(pOut, numDataBytes)
Next

gewgsdrtyutyk = sOut
End Function

Set fs = CreateObject("Scripting.FileSystemObject")
Set a = fs.CreateTextFile("malware.bin", True)
a.WriteLine(pqwkdaiffewfgwerheejjej)
a.Close

We execute our VBS script and after a bit of time we’ll have our “malware.bin” payload on disk. When we open the payload we can see that it claims to be the “Cabinet Self-Extractor” by Microsoft. By quickly looking at the code of an original “wextract.exe” binary, we can confirm that the file seems authentic.

Assembly code of the binary in Cerbero Suite

Looking at the “Hierarchy” view, we can see that Cerbero already found the CAB file to extract in the resources of the binary and lets us inspect its contents. However, we also need to know what the self-extractor does with the files once extracted. So we take a look at the assembly code of the binary in Cerbero Suite.

We notice that the disassembled code and its decompiled counter-part contain a lot of calls to access the resources of the binary.

Disassembled code screenshot

So we switch to the resources of the Portable Executable and notice the “RUNPROGRAM” resource (the “CABINET” resource is the one which contains the CAB file).

The “CABINET” resource is the one which contains the CAB file

The “RUNPROGRAM” resource contains the string executed after having extracted the contents of the cabinet file:

cmd /ccmd /cHELP&help&cmd /cru..................................jse&help&exit

What it does is to execute the "ru..................................jse" file. So we take a look at that file inside the cabinet.

Malware bin files

JSE files are the equivalent of VBE files: they are encoded JS files. However, they are not browser JavaScript files, but WScript files, meaning that they have at their disposal the same APIs as VBS scripts and therefore have access to the entire system.

Again, Cerbero Suite automatically decodes the JSE file for us.

Cerbero Suite automatically decodes the JSE file

The first thing we notice is that the script is lacking new-lines and is therefore completely unreadable. The first operation we perform is to beautify the code by running an action: we press “Ctrl+R” and then click on “Beautify JavaScript”.

Execute action box with beautify JavaScript selected

The code immediately becomes easier to read.

The code immediately becomes easier to read

However, the code is heavily obfuscated and consists of almost 50000 lines of unreadable JavaScript. We need to de-obfuscate it! In the obfuscated JavaScript we can see a repeated use of the following lines of code:

try
{
Jer56fh -= 0.7;
Jer56fh();
}
catch (Jer56fh)
{
Jer56fh += 0.82;
};

These lines do absolutely nothing, so we can remove them with a single regular expression.

s = re.sub(r"try\s*{\s*Jer56fh\s-=\s[0-9.;]+\s+Jer56fh[(][)][;]\s*}\s*catch [(]Jer56fh[)]\s*{\s*Jer56fh [+][=] [0-9.;]+\s*};", "", s)

Already this reduces the dimensions of the obfuscated JavaScript significantly. Now we can see that the most used construct in the code is the following:

(function(jhwes4)
{
jhwes4[Ju67gh] = 0;
jhwes4[Kio87.Ju67gh + Kio87.FiLo7] = 108;
return PjSbWnR(PjSbWnRW() + (jhwes4[Hr6gt] + jhwes4[Ju67gh]), fertu);
})(Nu0o.a ...

This inline function does nothing else than to compute a string character. It sums the value of "jhwes4[Ju67gh]" and "jhwes4[Kio87.Ju67gh + Kio87.FiLo7]" and calls the JavaScript function "fromCharCode" on the result.

Thus, what we do is to use another regular expression to find all instances of this type of code and convert them into strings containing a single character.

s = re.sub(r"[(]function[(][a-zA-Z0-9]+[)]\s*{\s*[a-zA-Z0-9]+[[][a-zA-Z0-9]+[]] = ([0-9]+);\s*[a-zA-Z0-9]+[[][a-zA-Z0-9+ .]+[]] = ([0-9]+);\s*return PjSbWnR[^;]+;\s*}[)][(]Nu0o[.]a[^)]*[)]", \
lambda m: "'" + chr(int(m.group(1)) + int(m.group(2))) + "'", s)

At this point the code is already much more readable, but we got a lot of expressions such as: 's' + 't' + 'r'. We want to simplify these expressions into single strings with yet another regular expression.

s = re.sub(r"('[^']'[ ]?[+]?[ ]?)+('[^']')", lambda m: '"' + eval(m.group()) + '"', s)

Now the JavaScript code is absolutely readable! As a last touch, we replace some garbled variable names with their readable aliases.

aliases = (
("dtXmUvery59", "WScript"),
("dtXmUrelated34", "ActiveXObject"),
("dtXmUform61", "WScript.ScriptFullName"),
("dtXmUwith55", "FileSystem"),
("dtXmUtheorem3", "ShellApp")
)
for (a, b) in aliases:
s = s.replace(a, b

If you want to do the deobfuscation yourself, just press “Ctrl+Alt+R” while on the view of the obfuscated JavaScript code and then paste the following Python code.

from Pro.UI import proContext
import re

s = proContext().getCurrentView().getText()

#
# use a regex to remove bogus clauses such as:
#
#     try
#     {
#         Jer56fh -= 0.7;
#         Jer56fh();
#     }
#     catch (Jer56fh)
#     {
#         Jer56fh += 0.82;
#     };  
#
#
s = re.sub(r"try\s*{\s*Jer56fh\s-=\s[0-9.;]+\s+Jer56fh[(][)][;]\s*}\s*catch [(]Jer56fh[)]\s*{\s*Jer56fh [+][=] [0-9.;]+\s*};", "", s)

#
# some logic to convert this syntax into a character
#
# (function(jhwes4)
#         {
#             
#             jhwes4[Ju67gh] = 0;
#             jhwes4[Kio87.Ju67gh + Kio87.FiLo7] = 108;
#             return PjSbWnR(PjSbWnRW() + (jhwes4[Hr6gt] + jhwes4[Ju67gh]), fertu);
#         })(Nu0o.a # <- more bogus parameters may follow
#
s = re.sub(r"[(]function[(][a-zA-Z0-9]+[)]\s*{\s*[a-zA-Z0-9]+[[][a-zA-Z0-9]+[]] = ([0-9]+);\s*[a-zA-Z0-9]+[[][a-zA-Z0-9+ .]+[]] = ([0-9]+);\s*return PjSbWnR[^;]+;\s*}[)][(]Nu0o[.]a[^)]*[)]", \
        lambda m: "'" + chr(int(m.group(1)) + int(m.group(2))) + "'", s)

#
# now simplify all 's' + 't' + 'r' expressions
#
s = re.sub(r"('[^']'[ ]?[+]?[ ]?)+('[^']')", lambda m: '"' + eval(m.group()) + '"', s)

#
# replace a few aliases
#
aliases = (
    ("dtXmUvery59", "WScript"),
    ("dtXmUrelated34", "ActiveXObject"),
    ("dtXmUform61", "WScript.ScriptFullName"),
    ("dtXmUwith55", "FileSystem"),
    ("dtXmUtheorem3", "ShellApp")
    )
for (a, b) in aliases:
    s = s.replace(a, b)

print(s)
Deobfuscated JavaScript

This script prints out the deobfuscated JavaScript which is now only 900 lines of code!

Among various things, the JavaScript code seems to download another JSE script from a URL, but when we tried to access the remote file we were unable to. So we stop here our write-up.

The JavaScript code seems to download another JSE script from a URL

We hope you enjoyed our analysis!

Free On-Demand Webinar: Think Before You Click

Whether sent as an email attachment, sitting in your cloud or traversing the Web, file-borne threats have become a proven favorite for delivering malware and phishing campaigns. View our webinar on-demand and get firsthand tips about how to safeguard your cybersecurity stack with File Detection and Response (FDR) and stop file-borne threats in their tracks.

View the Webinar On-Demand