A Fun CTF-Like Malware

From a Twitter post by InQuest, we analyzed an interesting malware:

Encrypted MS Office Document, VBA, Windows Link File (LNK), OLE objects, Windows Help Files (CHM), PNG steganography and Powershell.

SHA256: 46AFA83E0B43FDB9062DD3E5FB7805997C432DD96F09DDF81F2162781DAAF834

The analysis should take about 15-20 minutes in Cerbero Suite.

Highly recommended!

SPOILER ALERT: The images below show all the steps of our analysis.

Video: Emotet MS Office Malware 150-Seconds Analysis

This Microsoft Office document belongs to the Emotet malware campaign and as part of its obfuscation strategy uses the content of text boxes from its VBA code. In the upcoming Cerbero Suite 5.1 we have simplified the analysis of text controls by previewing their name in the format view.

The script below deobfuscates the VBA code.

from Pro.UI import *

v = proContext().findView("Analysis [VBA code]")
if v.isValid():
    s = v.getText()
    lines = s.split("\n")
    new_lines = []
    for line in lines:
        if line.strip().startswith(("'", "Debug.Print")):
            continue
        while True:
            i = line.rfind("'")
            if i == -1:
                break
            line = line[:i]
        new_lines.append(line)
    print("\n".join(new_lines))

Go Binaries (part 2)

We’re currently working on making Go binaries easier to understand using our ultra-fast Carbon disassembler. In the upcoming weeks we’ll keep on posting progress updates.

In the previous part we focused on resolving function names. In this part we focus on resolving strings literals.

Let’s take the following decompiler output for a Go function:

void __stdcall sub_47D590(void)
{
    uint32_t *puVar1;
    int32_t in_FS_OFFSET;
    unk32_t uStack8;
    unk32_t uStack4;
    
    while (puVar1 = (uint32_t *)(**(int32_t **)(in_FS_OFFSET + 0x14) + 8),
          *(BADSPACEBASE **)0x10 < (unk8_t *)*puVar1 ||
          (unk8_t *)*(BADSPACEBASE **)0x10 == (unk8_t *)*puVar1) {
        uStack4 = 0x47D638;
        sub_446900();
    }
    sub_44B9D0("syntax error scanning complex numberuncaching span but s.allocCount == 0) is smaller than minimum pa", 0x24);
    *(unk32_t *)0x532DC8 = uStack8;
    if (*(int32_t *)0x542C70 == 0) {
        *(unk32_t *)0x532DCC = uStack4;
    }
    else {
        sub_448050();
    }
    sub_44B9D0("syntax error scanning booleantimeBegin/EndPeriod not foundtoo many open files in systemtraceback has", 0x1D);
    *(unk32_t *)0x532DC0 = uStack8;
    if (*(int32_t *)0x542C70 == 0) {
        *(unk32_t *)0x532DC4 = uStack4;
    }
    else {
        sub_448050();
    }
    return;
}

It lacks function names and although referenced strings are visible thanks to the heuristics implemented in our decompiler, the size of the strings is incorrect, as the strings are not null-terminated.

By correctly resolving the Go strings, we obtain an improved output:

void __stdcall fmt.init.ializers(void)
{
    uint32_t *puVar1;
    int32_t in_FS_OFFSET;
    unk32_t uStack8;
    unk8_t *puStack4;
    
    while (puVar1 = (uint32_t *)(**(int32_t **)(in_FS_OFFSET + 0x14) + 8),
          *(BADSPACEBASE **)0x10 < (unk8_t *)*puVar1 ||
          (unk8_t *)*(BADSPACEBASE **)0x10 == (unk8_t *)*puVar1) {
        puStack4 = &fmt.init.ializers;
        runtime.morestack_noctxt();
    }
    errors.New("syntax error scanning complex number", 0x24);
    *(unk32_t *)0x532DC8 = uStack8;
    if (*(int32_t *)0x542C70 == 0) {
        *(unk8_t **)0x532DCC = puStack4;
    }
    else {
        runtime.gcWriteBarrier();
    }
    errors.New("syntax error scanning boolean", 0x1D);
    *(unk32_t *)0x532DC0 = uStack8;
    if (*(int32_t *)0x542C70 == 0) {
        *(unk8_t **)0x532DC4 = puStack4;
    }
    else {
        runtime.gcWriteBarrier();
    }
    return;

The same is true for our initial hello world example:

package main

import "fmt"

func main() {
    fmt.Println("Hello, World!")
}

The original decompiler output would be:

void __stdcall sub_47D7B0(void)
{
    uint32_t *puVar1;
    int32_t in_FS_OFFSET;
    unk32_t uStack8;
    unk32_t uStack4;
    
    while (puVar1 = (uint32_t *)(**(int32_t **)(in_FS_OFFSET + 0x14) + 8),
          *(BADSPACEBASE **)0x10 < (unk8_t *)*puVar1 ||
          (unk8_t *)*(BADSPACEBASE **)0x10 == (unk8_t *)*puVar1) {
        uStack4 = 0x47D823;
        sub_446900();
    }
    uStack8 = 0x491320;
    uStack4 = &"Hello, World!MapViewOfFileMasaram_GondiMende_KikakuiOld_HungarianRegDeleteKeyWRegEnumKeyExWRegEnumVa";
    sub_478270(0x4BCEC0, *(unk32_t *)0x532A8C, &uStack8, 1, 1);
    return;
}

While the newly introduced feature of our decompiler already detects the indirect string literal reference, again the size of the string is wrong.

The improved decompiler output is:

void __stdcall main.main(void)
{
    uint32_t *puVar1;
    int32_t in_FS_OFFSET;
    unk32_t uStack8;
    unk8_t *puStack4;
    
    while (puVar1 = (uint32_t *)(**(int32_t **)(in_FS_OFFSET + 0x14) + 8),
          *(BADSPACEBASE **)0x10 < (unk8_t *)*puVar1 ||
          (unk8_t *)*(BADSPACEBASE **)0x10 == (unk8_t *)*puVar1) {
        puStack4 = &main.main;
        runtime.morestack_noctxt();
    }
    uStack8 = 0x491320;
    puStack4 = (unk8_t *)&"Hello, World!";
    fmt.Fprintln(0x4BCEC0, *(unk32_t *)0x532A8C, &uStack8, 1, 1);
    return;
}

While it’s still far from being easy to read, it’s definitely easier to read than before.

To be continued…

Decompiler: Indirect String References

The upcoming 5.1 version of Cerbero Suite Advanced introduces improvements in the output of the decompiler.

One of the improvements is the detection and display of indirect string literal references. These type of references are already correctly handled by our ultra-fast Carbon disassembler.

Let’s take for instance the following code example:

#include <stdio.h>

void foo(const char **ref)
{
    puts(*ref);
}

int main ()
{
    static const char *s = "Referenced string";
    foo(&s);
    return 0;
}

Our Carbon disassembler already detects the indirect reference:

RefString:.text:0x140001000 sub_140001000 proc start
RefString:.text:0x140001000                                 ; CODE XREF: 0x14000128E
RefString:.text:0x140001000                                 ; DATA XREF: 0x140004000
RefString:.text:0x140001000 ; unwind {
RefString:.text:0x140001000        sub    rsp, 0x28
RefString:.text:0x140001004        mov    rcx, qword ptr [0x140003020] ; ptr:"Referenced string"
RefString:.text:0x14000100B        call   qword ptr [0x140002118] -> puts
RefString:.text:0x140001011        xor    eax, eax
RefString:.text:0x140001013        add    rsp, 0x28
RefString:.text:0x140001017        ret
RefString:.text:0x140001017 ; } // starts at sub_140001000
RefString:.text:0x140001017
RefString:.text:0x140001017 sub_140001000 proc end

However, up until now the decompiler would produce the following output:

undefined64 __fastcall sub_140001000(void)
{
    (*_puts)(*(undefined64 *)0x140003020);
    return 0;
}

While, in the upcoming version the output is:

undefined64 __fastcall sub_140001000(void)
{
    (*_puts)(*(undefined64 *)&"Referenced string");
    return 0;
}

More decompiler improvements will be introduced in the upcoming version!

Go Binaries (part 1)

We’re currently working on making Go binaries easier to understand using our ultra-fast Carbon disassembler. In the upcoming weeks we’ll be posting progress updates.

Let’s start with a basic hello world example.

package main

import "fmt"

func main() {
    fmt.Println("Hello, World!")
}

Without additional logic, functions name are not available.

We can see the referenced string, although it is not null-terminated, so that we don’t know its length.

The ‘main.main’ function is not treated as a function and even if we define it as such by pressing ‘P’, the decompiled result is hardly intelligible.

void __stdcall sub_47D7B0(void)
{
    uint32_t *puVar1;
    int32_t in_FS_OFFSET;
    undefined32 uStack8;
    undefined32 uStack4;
    
    while (puVar1 = (uint32_t *)(**(int32_t **)(in_FS_OFFSET + 0x14) + 8),
          *(BADSPACEBASE **)0x10 < (undefined *)*puVar1 ||
          (undefined *)*(BADSPACEBASE **)0x10 == (undefined *)*puVar1) {
        uStack4 = 0x47D823;
        sub_446900();
    }
    uStack8 = 0x491320;
    uStack4 = 0x4BC178;
    sub_478270(0x4BCEC0, *(undefined32 *)0x532A8C, &uStack8, 1, 1);
    return;
}

So the first step is recognizing functions and retrieving their names.

void __stdcall main.main(void)
{
    uint32_t *puVar1;
    int32_t in_FS_OFFSET;
    undefined32 uStack8;
    undefined *puStack4;
    
    while (puVar1 = (uint32_t *)(**(int32_t **)(in_FS_OFFSET + 0x14) + 8),
          *(BADSPACEBASE **)0x10 < (undefined *)*puVar1 ||
          (undefined *)*(BADSPACEBASE **)0x10 == (undefined *)*puVar1) {
        puStack4 = &main.main;
        runtime.morestack_noctxt();
    }
    uStack8 = 0x491320;
    puStack4 = (undefined *)0x4BC178;
    fmt.Fprintln(0x4BCEC0, *(undefined32 *)0x532A8C, &uStack8, 1, 1);
    return;

While it's still not easy to read, we can grasp a bit more of its meaning.

To be continued...

Cerbero Suite 5 Commercial Discounts Month

To celebrate the launch of Cerbero Suite 5 we’ll be offering commercial discounts for the next 30 days!

By purchasing 3 commercial licenses, you pay only 2!

Or:

By purchasing 7 commercial licenses, you pay only 4!

Or:

By purchasing 10 commercial licenses, you pay only 6!

Or:

If you already have a commercial license, you can purchase new licenses at a 50% discount!

Or:

If you have a home/academic license and would like to upgrade to a commercial license, you get a 50% discount!

To receive a discount coupon or for any question, please contact us at: sales@cerbero.io.

Please notice that this is a limited time offer and it won’t be extended over the announced period of time!

Cerbero Suite 5 is out!

We’re proud to announce the release of Cerbero Suite 5 and Cerbero Enterprise Engine 2!

All of our customers can upgrade at a 50% discount their licenses for the next 3 months! We value our customers and everyone who has bought a license in August should have already received a free upgrade for Cerbero Suite 5! If you fall in that category and haven’t received a new license, please check your spam folder and in case contact us at sales@cerbero.io. Everyone who has acquired a license before August, but in the last 3 months, will get an additional discount.

Starting today we’ll be contacting all of our existing customers and provide them with a discount coupon. If you don’t get an email from us in the next two days, please contact us at sales@cerbero.io!

Speed

We introduced many core optimizations, while maintaining the same level of security.

Cerbero Suite has always been fast, so these changes may not be too apparent. They are, however, noticeable in our benchmarks!

The scanning of certain file formats like PE and the disassembly of binaries using Carbon show a decent performance boost. However, in the case of certain file formats like PDF the performance boost is massive!

Documentation

For this release we created beautiful documentation for our SDK, which can be found at: https://sdk.cerbero.io/latest/.

The documentation of each module comes with an introduction detailing essential concepts.

Other sections provide code examples with explanations.

The API documentation contains the prototype of each method and function and it comes with code examples.

Related constants, classes, methods and functions all contain references to each other.

The documentation contains notes and hints in case there are things to be aware of.

The documentation is searchable. Entering the name of a constant, class, method or function directly brings to its documentation.

The documentation of the UI module will enable you to create complex user interfaces.

It even explains how to create entire workspaces with dock views, menus and toolbars.

While there remain dozens of modules to document, the Core and UI module represent a great part of the functionality of Cerbero Suite and Cerbero Enterprise Engine. We will release the documentation of more modules and topics over the course of the 5.x series.

Python

This release comes with the latest Python 3.9.6!

We update Python only between major versions and for the release of Cerbero Suite 4 we didn’t have the time to upgrade. So the previous series remained with Python 3.6.

This series not only comes with the very latest Python version, but we also managed to keep compatibility with all our older supported systems, including Windows XP!

Scan Data Hooks

We introduced a new type of hook extension: scan data hooks.

Using this type of hooks, it’s trivial to customize the scan results of existing scan providers.

For example, adding a custom entry during the scan of a PE file and then provide the view to display it in the workspace.

The following is small example.

Add these lines to your user ‘hooks.cfg’ file.

[ExtScanDataTest_1]
label = External scan data test
file = ext_data_test.py
scanning = scanning
scandata = scandata

Create the file ‘ext_data_test.py’ in your ‘plugins/python’ directory and paste the following code into it.

from Pro.Core import *

def scanning(sp, ud):
    e = ScanEntryData()
    e.category = SEC_Info
    e.type = CT_VersionInfo
    e.otarget = "This is a test"
    sp.addHookEntry("ExtScanDataTest_1", e)
    
def scandata(sp, xml, dnode, sdata):
    sdata.setViews(SCANVIEW_TEXT)
    sdata.data.setData("Hello, world!")
    return True

Activate the extension from Extensions -> Hooks.

Now when scanning a file an additional entry will be shown in the report.

Clicking on the entry will display the data provided by the extension!

This type of extension is extremely powerful and we’ll show some real use cases soon.

What Next?

Among the many things we introduced over the course of the previous 4.x series there was:

  • ARM32/ARM64 disassembly and decompiling.
  • Decompiling and emulation of Excel macros.
  • Support for Microsoft Office document decryption.
  • Disassembly of Windows user address space.
  • Disassembly of Windows DMP files.
  • Support of XLSB and XLSM formats.
  • Support of CAB format.
  • Hex editing of processes, disk and drives on Windows.
  • Updated native UI for Ghidra 10.
  • Improved decompiler.
  • Improved macOS support.

So in the last series we spent a lot of time focusing on Microsoft technology.

In particular, Excel malware required supporting its decryption, the various file formats used to deliver it (XLS, XLSB, XLSM) and creating a decompiler and an emulator for its macros.

Also, in June we launched our Cerbero Enterprise Engine, which detracted some of our development resources, but it gave us the opportunity to clean up and improve our SDK.

This series will be focused mostly on non-Microsoft specific technology and hence will appeal to a broader audience.

We can’t wait to show you some of the things we have planned and we hope you enjoy this new release!

Happy hacking!