Z2A Custom Sample Part 1
Table of Contents
- Introduction
- The “story”
- First look
- Analysis
- Injection part
- Python script to extract stage 2
- Refinery pipeline to extract stage 2
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
Beside that, the imported libraries include only one, which is KERNEL32.dll
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.
At first glance, I can see that this stage uses sub_401300
to decrypt strings and then uses the result to resolve APIs.
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
.
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.
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.
You can check the key with Resource Hacker.
If you try to decrypt the buffer you will get another PE file. The decrypted buffer is then passed to sub_401000
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.
It will first create a new process in suspended state
Then it starts to write the decrypted PE file to the new created process
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