Z2A Custom Sample Part 1

image

Table of Contents

Introduction

After many theoretical chapters, we finally move to a practical session with a custom sample. This is part one of my analysis.

The “story”

Hi there,

During an ongoing investigation, one of our IR team members managed to locate an unknown sample on an infected machine belonging to one of our clients. We cannot pass that sample onto you currently as we are still analyzing it to determine what data was exfilatrated. However, one of our backend analysts developed a YARA rule based on the malware packer, and we were able to locate a similar binary that seemed to be an earlier version of the sample we’re dealing with. Would you be able to take a look at it? We’re all hands on deck here, dealing with this situation, and so we are unable to take a look at it ourselves.
We’re not too sure how much the binary has changed, though developing some automation tools might be a good idea, in case the threat actors behind it start utilizing something like Cutwail to push their samples.
I have uploaded the sample alongside this email.

Thanks, and Good Luck!

First look

The first step in malware analysis is to always make a quick triage report to know what kind of sample we are dealing with.

Looking at the sections inside pestudio we can see the entropy of .rsrc is quite high with 7.994

image

Beside that, the imported libraries include only one, which is KERNEL32.dll

image

These can be indicators that this sample is packed or has many stages, and we should pay attention to some parts related to the resource section.

Analysis

Let’s open the file in Binary Ninja.

Just a small tip: we can easily find the main function by searching for any function that has three push instructions before calling it.

image

At first glance, I can see that this stage uses sub_401300 to decrypt strings and then uses the result to resolve APIs.

image

Diving into sub_401300, this function tries to rotate the position of each character from the argument by 13 based on a charset. So I renamed it to rot_13.

image

Knowing what algorithm is used to decrypt the strings, I decided to write some lines of code to decrypt them with the Binary Ninja API.

bv.begin_undo_actions()

def rot_13(s):
    alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890./="
    return "".join([alphabet[(alphabet.index(c) + 13) % len(alphabet)] for c in s])

func = bv.get_functions_by_name("rot_13")[0]
caller_sites = [cs for cs in func.caller_sites]

for cs in caller_sites:
    s = cs.hlil.params[0].string[0]
    s = rot_13(s)
    print(f"String: {s}")
    address = cs.hlil.params[0].constant
    print(f"Address: {hex(address)}")
    bv.write(address, s.encode())

bv.commit_undo_actions()

Now I can see that it is doing some stub with the resource.

image

Just a small tip when dealing with API Resolving, you can get the type of the API from the lib and then override the call type to make the code more readable.

lib = bv.platform.get_type_libraries_by_name('kernel32.dll')[0]
type = bv.import_library_object(API_name, lib)

And here is the result, I can see that it is decrypt the resource with RC4, and the 15-byte key is inside the resource.

image

You can check the key with Resource Hacker.

image

If you try to decrypt the buffer you will get another PE file. The decrypted buffer is then passed to sub_401000

image

Injection part

After parsing the header and renaming some funcitons, I assume this sample uses Process Hollowing to inject the decrypted PE file into a new process.

image

It will first create a new process in suspended state

image

Then it starts to write the decrypted PE file to the new created process

image

After that it will resume the suspended process, so now our aim is to focus on reversing the new PE file, which is the stage 2 of this sample.

This is the end of the part 1 of my write-up for custom sample from Z2A. Here are some notes:

  • Stage 1 uses ROT13 to decrypt strings
  • It uses API resolving technique
  • The next stage is encrypted by RC4 and stored in resource section
  • The decrypted stage is then written to a new process which has the same name with the current process.

Python script to extract stage 2

import pefile
from Crypto.Cipher import ARC4
import sys


try:
    name = sys.argv[1]
    pe = pefile.PE(name)
    print(f"File {name} found")
except:
    print("Error: file not found")
    sys.exit(1)

def extract(pe):
    for entry in pe.DIRECTORY_ENTRY_RESOURCE.entries:
        resource_type = pefile.RESOURCE_TYPE.get(entry.struct.Id)
        if resource_type == "RT_RCDATA":
            for directory in entry.directory.entries:
                resource = directory.directory.entries[0]
                data_rva = resource.data.struct.OffsetToData
                size = resource.data.struct.Size
                data = pe.get_memory_mapped_image()[data_rva:data_rva+size]
                key = data[0xc:0x1b]
                enc = data[0x1c:]
                cipher = ARC4.new(key)
                return cipher.decrypt(enc)
    return None


with open(name.split(".")[0] + "_stage2.bin", "wb") as f:
    f.write(extract(pe))
    print(f"File extracted to {name.split(".")[0] + "_stage2.bin"}")

Refinery pipeline to extract stage 2

*I’m still new to this :)

emit main_bin.bin | perc [|put k cut:12:15 |snip 13: | rc4 eat:k ] | dump stage2.bin