When looking through my Spam folder, I have run across a few messages with ".bat" files attached to them. Most messages have had different content in the message to entice a victim to open the attachment. I started to investigate each of the attachments and found they were Windows Binaries, and at least two had PNG files in the resources. After doing this initial triage, I wanted to see if the payload of these pieces of malware is encoded in this PNG data and how it was encoded.
I started with a sample named "Bank Statement.bat" with the .NET code that is the least obfuscated and will visit another sample in a later post. In this post, I will reverse engineer the .NET code and uncover the process to extract out the payload encoded in a PNG file embedded in the binary.
First thing, I took a look at the properties of the attached file and determined it was a .NET compiled binary with some suspicious properties such as having a copyright field listing "Apple, Inc." Some more of the metadata details are shown below.
Architecture: IMAGE_FILE_MACHINE_I386 Subsystem: IMAGE_SUBSYSTEM_WINDOWS_GUI Compilation Date: 2020-Apr-20 14:27:35 Comments: QuartzCore 227 CompanyName: Apple Inc FileDescription: QuartzCore FileVersion: 220.127.116.11 InternalName: Ly2kW4nOksU0vgv.exe LegalCopyright: © 2020 Apple Inc. All rights reserved. OriginalFilename: Ly2kW4nOksU0vgv.exe ProductName: QuartzCore ProductVersion: 18.104.22.168 Assembly Version: 22.214.171.124 Matching compiler(s): Microsoft Visual C# v7.0 / Basic .NET Microsoft Visual C++ 8.0 .NET executable -> Microsoft
Next I ran a binwalk to see if there are is any other obvious hidden content within this file and found there is a PNG file embedded within the binary.
DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 0 0x0 Microsoft executable, portable (PE) 19329 0x4B81 PNG image, 290 x 290, 8-bit/color RGBA, non-interlaced 19407 0x4BCF Zlib compressed data, compressed 357216 0x57360 Copyright string: "CopyrightAttribute"
I opened the file in ilSpy and extracted the PNG file from the resources of the binary. When looking at the extracted PNG file I found visually it looks like encoded data. After seeing this image I started to investigate the original binary file to find routines used to decode the PNG file into what I assumed is the payload of the malware. I started to look at the file further in dnSpy and started at the entry point of the binary.
Starting at the entry point method and following the flow through a few more methods, finally finding the start of the decoder functionality. The method below shows the initial routines that load the decoder.
The first items I noticed were the variables text and test2 are references to the PNG resource data. The next variable of note is test3 which looks like it could be a password. This method also contains a blob of encoded data (shown in the HexToString() call on line 9) that has various bytes swapped. Once the blob of data is decoded and returned to its original values then transformed into a string that is next decoded from Base64 into is DLL. The DLL when loaded is named CoreFunctions.dll.
After CoreFunctions.dll is loaded the method "CoreFunctions.Main" is executed. There are four parameters passed to this method, the first two references the PNG data, third what looks like a password, and finally the path to the full binary file. These are the variables I made a note of earlier. This method runs a few routines that decode the PNG data. Next, let's walk through these method calls:
- Read_R reads the PNG file resource into a bitmap object.
- Reverse creates an array of each column's BRGA (Blue, Red, Green, Alpha) color values.
- XOR_DEC decodes the values using XOR rotating through the key "XAdgWkK" that is XOR'ed against the last byte of the PNG data.
The image below shows the calls to these methods. They are high lighted in red by the breakpoints.
Once the PNG resource data is decoded into its executable binary data, it is loaded and executed in memory without writing any data to disk.
I have written a python script (that is at the end of this post), that recreates the decoding process and takes in the export of the resource's PNG data and the key to decodes the payload.
Once this process is completed the decoded payload is named "ReZer0V2" in the metadata of the binary data. I have not done much analysis on the main payload yet other than executing the sample in a sandbox. The sandbox run can be viewed at the following Anyrun link:
I may do further analysis of this sample however this appears to be a few posts out there about this payload:
I found this an interesting sample to dissect and understand the method used to encode the PNG data and in the future to see if it can be used to decode a second sample I have with a similarly encoded PNG file. The follow-up post about that sample "W.H.O.bat" will be posted up soon. A theory I have about this sample is that it was sent out prematurely and was not fully obfuscated nor was the phishing content of the message fully completed for the campaign, however, it is just a guess.
import png import struct def print_list(thelist, quantity): if (quantity == 0): quantity = len(thelist) #print ("Decimal: ", end="") #for i in range(0, quantity): # print (thelist[i], ", ", end="") #print ("") print ("Hex: ", end="") for i in range(0, quantity): print (hex(thelist[i]), ", ", end="") print ("") print ("ASCII: ", end="") for i in range(0, quantity): print (chr(thelist[i]), ", ", end="") print ("") ############ filename = "79fb5.bmp" # 0x0 , 0x58 , 0x0 , 0x41 , 0x0 , 0x64 , 0x0 , 0x67 , 0x0 , 0x57 , 0x0 , 0x6b , 0x0 , 0x4b plain_key = "XAdgWkK" key = bytearray(plain_key.encode("utf-16be")) print_len = 50 print ("Key: ") print_list(key, 0) print ("Key len: ", len(key)) ## #### Load PNG bmp_full_data = png.Reader(filename=filename).read() bmp_img_data = list(bmp_full_data) ## #### Reverse Start data_array =  output_array =  print ("") print ("Loading Image data") print ("IMG height: ", len(bmp_img_data)) row_count = 0 #print ("Row: ", i, len(bmp_img_data[i]), end='') while (row_count < len(bmp_img_data)-4): #print (row_count, " ", end="") for i in range(0,len(bmp_img_data)): # AARRGGBB R = bmp_img_data[i][row_count] # 05 G = bmp_img_data[i][row_count+1] # 16 B = bmp_img_data[i][row_count+2] # 01 A = bmp_img_data[i][row_count+3] # 00 data_array.append(B) data_array.append(G) data_array.append(R) data_array.append(A) row_count += 4 #print (".. row loaded") print ("1st bytes") print_list(data_array[:4], 4) first_bytes_value = struct.unpack("<I", bytearray(data_array[0:4])) decode_data = data_array[4:] ## #### XOR_DEC Start key_counter = 0 print ("Data Length: ", len(data_array)) print ("") print ("Pre XORed data") print_list(decode_data, 50) outfile = open ("test-prexor.bin", 'wb') outfile.write(bytearray(decode_data)) outfile.close() print ("") print ("XORing Image data") # below is either B5 or 00 ^ 112 key_modifier = 0xb5 ^ 112 # 0xc5 #key_modifier = decode_data[len(decode_data)-1] ^ 112 #key_modifier = 0 print(len(decode_data)-1, hex(decode_data[len(decode_data)-1]), key_modifier) key_counter = 0 for xor_i in range(0,len(decode_data)): key_value = key[key_counter] output_array.append(decode_data[xor_i] ^ key_modifier ^ key_value) if (key_counter < len(plain_key)-1): key_counter += 1 else: key_counter = 0 print ("Final Output") print_list(output_array, print_len) outfile = open ("test-postxor.bin", 'wb') outfile.write(bytearray(output_array)) outfile.close()