Skip to main content

Cerbero Suite: The Hacker’s Multitool

Posted on 2020-10-13 by Erik Pistelli

Cerbero Suite

Cebero

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.

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.

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.

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.

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.

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 "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.

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.

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".

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)

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.

We hope you enjoyed our analysis!

Bio

Paris

Erik Pistelli is the CEO of Cerbero and lead developer of Cerbero Suite. Erik has worked in the security industry his entire professional career. He has previously worked for Hex-Rays on IDA Pro and created popular tools such as CFF Explorer and 4GB Patch.

Cerbero is the company behind Cerbero Suite. Our job is to hand the perfect multitool to low-level professionals. Cerbero Suite offers state-of-the-art analysis tools for a wide variety of experts in the security and forensic fields. The research at Cerbero has directly benefitted the static analysis capabilities in popular AntiVirus engines. We encourage you to try out Cerbero Suite: it may soon become your most used tool!

Tags
labs in-the-wild