Reversing Revil Malware – Part 2 – String Obfuscation and Configuration Setup

This is the second in a series looking at part of the Revil malware. The first post covered a triage and unpacking of the first stage. The post will look at the high-level flow and look in-depth at the configuration embedded in the sample and some options.

Looking at the flow diagram (pictured on the left), there is a pretty straightforward flow to the sample. It initially sets itself up by resolving the import table, reading the embedded configuration data, and command-line arguments. After the initial configuration is loaded and processed, the sample starts to execute the encryption and beaconing activities. Finally, it cleans up after itself clearing itself from memory, deleting itself from disk, and exiting. Now that we have an overview of this stage, we will look at how strings are obscured and how the configuration is loaded and processed.

String Encryption

When you run a string identification tool on this binary, you find there are not many readable strings. This sample obscures the vast majority of its strings. When analyzing it, you see many calls similar to this example.

This function located at 0x0040575B uses RC4 to decrypt the strings from a data block located at either 0x0040F270 or 004101B0. These blocks contain both the key and the encrypted data itself. The function is passed a pointer to the data block, offsets of the key and encrypted data, key size, and data size. It returns the clear string as the last parameter of the function call.

In the function that I labeled “mw_run_rc4_decrypt” (0x0040646A), you find a fairly standard RC4 decryption set of routines. I have recreated this functionally in python, which I used heavily when analyzing this sample to label the string variables.

!pip3 install arc4
from arc4 import ARC4
import pefile
import binascii

secondstage = "file1.bin"

pe = pefile.PE(secondstage)
section_offest = 0xf000

for section in pe.sections:
    if b".data" in section.Name:
        hex_data_1 = section.get_data()[(0x101b0-section_offest):]
        hex_data_2 = section.get_data()[(0xf270-section_offest):(0xf17)]

def decrypt(data, position, keylen, datasize):
        
    key = data[position:position+keylen]
    cipher = ARC4(key)
    rc4_data = cipher.decrypt(data[position+keylen:position+keylen+datasize])
    
    # Convert to string
    string_data = ""
    for byte in rc4_data:
        string_data += chr(byte)
    
    return string_data

Revil Configuration

The encrypted configuration is stored in the .7tdlvx section of the binary. The data is RC4 encrypted like string data was. It also includes some tamper protection; there is a CRC32 value stored with the data. Below is the structure of the configuration section. I have labeled the data segments with numbers.

  1. Decryption Key
  2. crc32 Checksum
  3. Configuration Size
  4. Start of Encrypted configuration

The function shown in the image below is used to decrypt the data from the 7tdlvx section. When executed, the CRC32 value of the data is checked, and if it matches, the function is called to run the RC4 decryption. Pointers to the key, key length, address of the encrypted data, and size of the encrypted data are passed into the function to decrypt the data.

After the RC4 decryption, it returns a block of JSON data to a variable for further processing. Below is an abbreviated version of the configuration for readability. I put a full copy of it at the end of this post.

{'arn': False,
 'dbg': False,
 'dmn': '',
 'et': 0,
 'exp': False,
 'img': 'QQBsAGwAIABvAGYAIAB5AG8AdQByACAAZgBpAGwAZQBzACAAYQByAGUAIABlAG... AHMAdAB1AGMAdABpAG8AbgBzAAAA',
 'nbody': 'LQAtAC0APQA9AD0AIABXAGUAbABjAG8AbQBlAC4AIABBAGcAYQBpAG4ALgAgAD ... ACEAIAAhACEAIQAgACEAIQAAAA==',
 'net': False,
 'nname': '{EXT}-README.txt',
 'pid': '$2b$13$wz1reRfdLg.aiStLDqg5JeqqySemSPatWKHdwbpWVrC3ty7Akscg6',
 'pk': 'SrxAOJ8RkDIIb7jurGu3kJGcui9QRzgmLyRe3dUxNSI=',
 'prc': ['vsnapvss',
         'EnterpriseClient',
         'firefox',
         ..
         'excel',
         'msaccess',
         'agntsvc'],
 'spsize': 1,
 'sub': '58',
 'svc': ['QBCFMonitorService',
         ..
         'saphostexec'],
 'wfld': ['backup', 'bkp', 'archive'],
 'wht': {'ext': ['dll',
                 ..
                 'cur'],
         'fld': ['program files',
                 ..
                 '$recycle.bin'],
         'fls': ['ntuser.ini',
                 ..
                 'thumbs.db']},
 'wipe': True}

To assist in processing and analysis of the configuration I created the following python script to extract and parse the configuration file from the sample.

!pip3 install arc4
from arc4 import ARC4
import pefile
import binascii
import json
import pprint as pp

secondstage = "file1.bin"

try:
   pe
except NameError:
    pe = pefile.PE(secondstage)

key_len = 0x20
section_name = ".7tdlvx"

# located in the .7tdlvx section
for section in pe.sections:
    if bytes(section_name, 'utf-8') in section.Name:
        section_data = section.get_data()

key = section_data[:key_len]
crc = section_data[key_len:key_len+0x4]
config_len = int.from_bytes(section_data[key_len+0x4:key_len+0x8], "little")
print(hex(config_len))
data_3_hex = section_data[key_len+0x8:config_len+key_len+0x8]

print (len(data_3_hex))

cipher = ARC4(key)
dump = cipher.decrypt(data_3_hex)

# Store JSON to file
f = open("config_decoded.txt", "wb")
f.write(bytearray(dump))
f.close()

cfg = json.loads(dump[:-1])
pp.pprint(cfg)

As shown in the configuration example, the configuration is in a JSON-like format that needs to be parsed further to be used by the malware. In the first part of the parsing process, an array is built out, defining the elements and how to process them. The three elements in the example below are and string for the JSON key, an integer for the data type, and a function pointer to the function to parse the data.

// String with configuration Name
configuration_structure[0] = (int)&str_pk;
// Data Type
configuration_structure[1] = 5;
// Funcation to handle the data and write it to a Global Variable
configuration_structure[2] = (int)mw_cfg_pk_decoder;

The parser array along and decrypted configuration are passed into a function the walks through the JSON configuration. The function searches for the keys in the JSON configuration, and the parser function is called to process the configuration content.

Some examples of configuration values that take further processing are ‘pk’, ‘img’, and ‘nbody’. These are all base64 encoded strings that are decoded before being stored in memory. Using the following python code we can see the values stored in these keys.

import base64
import binascii

print("pk: " + str(binascii.hexlify(base64.b64decode(cfg['pk']))))
print("img: " + str(base64.b64decode(cfg['img']).decode('utf-16')))
print("nbody: " + str(base64.b64decode(cfg['nbody']).decode('utf-16'))
pk: b'4abc40389f119032086fb8eeac6bb790919cba2f504738262f245eddd5313522'
img: All of your files are encrypted!

Find {EXT}-README.txt and follow instuctions
nbody: ---=== Welcome. Again. ===---

[+] What's Happened? [+]

Your files have been encrypted and currently unavailable. You can check it. All files in your system have {EXT} extension. By the way, everything is possible to recover (restore) but you should follow our instructions. Otherwise you can NEVER return your data.

[+] What are our guarantees? [+]

It's just a business and we care only about getting benefits. If we don't meet our obligations, nobody will deal with us. It doesn't hold our interest. So you can check the ability to restore your files. For this purpose you should visit our website where you can decrypt one file for free. That is our guarantee.
It doesn't metter for us whether you cooperate with us or not. But if you don't, you'll lose your time and data cause only we have the private key to decrypt your files. In practice - time is much more valuable than money.

[+] How to get access to our website? [+]

Use TOR browser:
  1. Download and install TOR browser from this site: https://torproject.org/
  2. Visit our website: http://4to43yp4mng2gdc3jgnep5bt7lkhqvjqiritbv4x2ebj3qun7wz4y2id.onion

When you visit our website, put the following data into the input form:
Key:


{KEY}


!!! DANGER !!!
DON'T try to change files by yourself, DON'T use any third party software or antivirus solutions to  restore your data - it may entail the private key damage and as a result all your data loss!
!!! !!! !!!
ONE MORE TIME: It's in your best interests to get your files back. From our side we (the best specialists in this sphere) ready to make everything for restoring but please do not interfere.
!!! !!! !!

Conclusion

We covered a couple of the obfuscation functions in this stage of the malware, the use of RC4 in many places to hide plain text data making various functions harder to detect and reverse engineer. The configuration section allows for a lot of flexibility. I can imagine allowing for a fair amount of automation in the build system, simplifying the building and deploy time. The next post will cover the file encryption function section of the code.

Configuration Keys

The key below is a select list of some of the configuration options that affect the flow or functionality of the sample. There are many more keys shown in the full configuration.

Config KeyUsage
dbgDebug mode?
etFast or Full Encryption
dmnDomain to Beacon
netDo HTTP beaconing?
arnAdd Run Key?
nbodyRansom note text
nnameRansom note filename
imgDesktop Background Text
Configuration keys

Full configuration

{'arn': False,
 'dbg': False,
 'dmn': '',
 'et': 0,
 'exp': False,
 'img': 'QQBsAGwAIABvAGYAIAB5AG8AdQByACAAZgBpAGwAZQBzACAAYQByAGUAIABlAG4AYwByAHkAcAB0AGUAZAAhAA0ACgANAAoARgBpAG4AZAAgAHsARQBYAFQAfQAtAFIARQBBAEQATQBFAC4AdAB4AHQAIABhAG4AZAAgAGYAbwBsAGwAbwB3ACAAaQBuAHMAdAB1AGMAdABpAG8AbgBzAAAA',
 'nbody': 'LQAtAC0APQA9AD0AIABXAGUAbABjAG8AbQBlAC4AIABBAGcAYQBpAG4ALgAgAD0APQA9AC0ALQAtAA0ACgANAAoAWwArAF0AIABXAGgAYQB0ACcAcwAgAEgAYQBwAHAAZQBuAGUAZAA/ACAAWwArAF0ADQAKAA0ACgBZAG8AdQByACAAZgBpAGwAZQBzACAAaABhAHYAZQAgAGIAZQBlAG4AIABlAG4AYwByAHkAcAB0AGUAZAAgAGEAbgBkACAAYwB1AHIAcgBlAG4AdABsAHkAIAB1AG4AYQB2AGEAaQBsAGEAYgBsAGUALgAgAFkAbwB1ACAAYwBhAG4AIABjAGgAZQBjAGsAIABpAHQALgAgAEEAbABsACAAZgBpAGwAZQBzACAAaQBuACAAeQBvAHUAcgAgAHMAeQBzAHQAZQBtACAAaABhAHYAZQAgAHsARQBYAFQAfQAgAGUAeAB0AGUAbgBzAGkAbwBuAC4AIABCAHkAIAB0AGgAZQAgAHcAYQB5ACwAIABlAHYAZQByAHkAdABoAGkAbgBnACAAaQBzACAAcABvAHMAcwBpAGIAbABlACAAdABvACAAcgBlAGMAbwB2AGUAcgAgACgAcgBlAHMAdABvAHIAZQApACAAYgB1AHQAIAB5AG8AdQAgAHMAaABvAHUAbABkACAAZgBvAGwAbABvAHcAIABvAHUAcgAgAGkAbgBzAHQAcgB1AGMAdABpAG8AbgBzAC4AIABPAHQAaABlAHIAdwBpAHMAZQAgAHkAbwB1ACAAYwBhAG4AIABOAEUAVgBFAFIAIAByAGUAdAB1AHIAbgAgAHkAbwB1AHIAIABkAGEAdABhAC4ADQAKAA0ACgBbACsAXQAgAFcAaABhAHQAIABhAHIAZQAgAG8AdQByACAAZwB1AGEAcgBhAG4AdABlAGUAcwA/ACAAWwArAF0ADQAKAA0ACgBJAHQAJwBzACAAagB1AHMAdAAgAGEAIABiAHUAcwBpAG4AZQBzAHMAIABhAG4AZAAgAHcAZQAgAGMAYQByAGUAIABvAG4AbAB5ACAAYQBiAG8AdQB0ACAAZwBlAHQAdABpAG4AZwAgAGIAZQBuAGUAZgBpAHQAcwAuACAASQBmACAAdwBlACAAZABvAG4AJwB0ACAAbQBlAGUAdAAgAG8AdQByACAAbwBiAGwAaQBnAGEAdABpAG8AbgBzACwAIABuAG8AYgBvAGQAeQAgAHcAaQBsAGwAIABkAGUAYQBsACAAdwBpAHQAaAAgAHUAcwAuACAASQB0ACAAZABvAGUAcwBuACcAdAAgAGgAbwBsAGQAIABvAHUAcgAgAGkAbgB0AGUAcgBlAHMAdAAuACAAUwBvACAAeQBvAHUAIABjAGEAbgAgAGMAaABlAGMAawAgAHQAaABlACAAYQBiAGkAbABpAHQAeQAgAHQAbwAgAHIAZQBzAHQAbwByAGUAIAB5AG8AdQByACAAZgBpAGwAZQBzAC4AIABGAG8AcgAgAHQAaABpAHMAIABwAHUAcgBwAG8AcwBlACAAeQBvAHUAIABzAGgAbwB1AGwAZAAgAHYAaQBzAGkAdAAgAG8AdQByACAAdwBlAGIAcwBpAHQAZQAgAHcAaABlAHIAZQAgAHkAbwB1ACAAYwBhAG4AIABkAGUAYwByAHkAcAB0ACAAbwBuAGUAIABmAGkAbABlACAAZgBvAHIAIABmAHIAZQBlAC4AIABUAGgAYQB0ACAAaQBzACAAbwB1AHIAIABnAHUAYQByAGEAbgB0AGUAZQAuAA0ACgBJAHQAIABkAG8AZQBzAG4AJwB0ACAAbQBlAHQAdABlAHIAIABmAG8AcgAgAHUAcwAgAHcAaABlAHQAaABlAHIAIAB5AG8AdQAgAGMAbwBvAHAAZQByAGEAdABlACAAdwBpAHQAaAAgAHUAcwAgAG8AcgAgAG4AbwB0AC4AIABCAHUAdAAgAGkAZgAgAHkAbwB1ACAAZABvAG4AJwB0ACwAIAB5AG8AdQAnAGwAbAAgAGwAbwBzAGUAIAB5AG8AdQByACAAdABpAG0AZQAgAGEAbgBkACAAZABhAHQAYQAgAGMAYQB1AHMAZQAgAG8AbgBsAHkAIAB3AGUAIABoAGEAdgBlACAAdABoAGUAIABwAHIAaQB2AGEAdABlACAAawBlAHkAIAB0AG8AIABkAGUAYwByAHkAcAB0ACAAeQBvAHUAcgAgAGYAaQBsAGUAcwAuACAASQBuACAAcAByAGEAYwB0AGkAYwBlACAALQAgAHQAaQBtAGUAIABpAHMAIABtAHUAYwBoACAAbQBvAHIAZQAgAHYAYQBsAHUAYQBiAGwAZQAgAHQAaABhAG4AIABtAG8AbgBlAHkALgANAAoADQAKAFsAKwBdACAASABvAHcAIAB0AG8AIABnAGUAdAAgAGEAYwBjAGUAcwBzACAAdABvACAAbwB1AHIAIAB3AGUAYgBzAGkAdABlAD8AIABbACsAXQANAAoADQAKAFUAcwBlACAAVABPAFIAIABiAHIAbwB3AHMAZQByADoADQAKACAAIAAxAC4AIABEAG8AdwBuAGwAbwBhAGQAIABhAG4AZAAgAGkAbgBzAHQAYQBsAGwAIABUAE8AUgAgAGIAcgBvAHcAcwBlAHIAIABmAHIAbwBtACAAdABoAGkAcwAgAHMAaQB0AGUAOgAgAGgAdAB0AHAAcwA6AC8ALwB0AG8AcgBwAHIAbwBqAGUAYwB0AC4AbwByAGcALwANAAoAIAAgADIALgAgAFYAaQBzAGkAdAAgAG8AdQByACAAdwBlAGIAcwBpAHQAZQA6ACAAaAB0AHQAcAA6AC8ALwA0AHQAbwA0ADMAeQBwADQAbQBuAGcAMgBnAGQAYwAzAGoAZwBuAGUAcAA1AGIAdAA3AGwAawBoAHEAdgBqAHEAaQByAGkAdABiAHYANAB4ADIAZQBiAGoAMwBxAHUAbgA3AHcAegA0AHkAMgBpAGQALgBvAG4AaQBvAG4ADQAKAA0ACgBXAGgAZQBuACAAeQBvAHUAIAB2AGkAcwBpAHQAIABvAHUAcgAgAHcAZQBiAHMAaQB0AGUALAAgAHAAdQB0ACAAdABoAGUAIABmAG8AbABsAG8AdwBpAG4AZwAgAGQAYQB0AGEAIABpAG4AdABvACAAdABoAGUAIABpAG4AcAB1AHQAIABmAG8AcgBtADoADQAKAEsAZQB5ADoADQAKAA0ACgANAAoAewBLAEUAWQB9AA0ACgANAAoADQAKACEAIQAhACAARABBAE4ARwBFAFIAIAAhACEAIQANAAoARABPAE4AJwBUACAAdAByAHkAIAB0AG8AIABjAGgAYQBuAGcAZQAgAGYAaQBsAGUAcwAgAGIAeQAgAHkAbwB1AHIAcwBlAGwAZgAsACAARABPAE4AJwBUACAAdQBzAGUAIABhAG4AeQAgAHQAaABpAHIAZAAgAHAAYQByAHQAeQAgAHMAbwBmAHQAdwBhAHIAZQAgAG8AcgAgAGEAbgB0AGkAdgBpAHIAdQBzACAAcwBvAGwAdQB0AGkAbwBuAHMAIAB0AG8AIAAgAHIAZQBzAHQAbwByAGUAIAB5AG8AdQByACAAZABhAHQAYQAgAC0AIABpAHQAIABtAGEAeQAgAGUAbgB0AGEAaQBsACAAdABoAGUAIABwAHIAaQB2AGEAdABlACAAawBlAHkAIABkAGEAbQBhAGcAZQAgAGEAbgBkACAAYQBzACAAYQAgAHIAZQBzAHUAbAB0ACAAYQBsAGwAIAB5AG8AdQByACAAZABhAHQAYQAgAGwAbwBzAHMAIQANAAoAIQAhACEAIAAhACEAIQAgACEAIQAhAA0ACgBPAE4ARQAgAE0ATwBSAEUAIABUAEkATQBFADoAIABJAHQAJwBzACAAaQBuACAAeQBvAHUAcgAgAGIAZQBzAHQAIABpAG4AdABlAHIAZQBzAHQAcwAgAHQAbwAgAGcAZQB0ACAAeQBvAHUAcgAgAGYAaQBsAGUAcwAgAGIAYQBjAGsALgAgAEYAcgBvAG0AIABvAHUAcgAgAHMAaQBkAGUAIAB3AGUAIAAoAHQAaABlACAAYgBlAHMAdAAgAHMAcABlAGMAaQBhAGwAaQBzAHQAcwAgAGkAbgAgAHQAaABpAHMAIABzAHAAaABlAHIAZQApACAAcgBlAGEAZAB5ACAAdABvACAAbQBhAGsAZQAgAGUAdgBlAHIAeQB0AGgAaQBuAGcAIABmAG8AcgAgAHIAZQBzAHQAbwByAGkAbgBnACAAYgB1AHQAIABwAGwAZQBhAHMAZQAgAGQAbwAgAG4AbwB0ACAAaQBuAHQAZQByAGYAZQByAGUALgANAAoAIQAhACEAIAAhACEAIQAgACEAIQAAAA==',
 'net': False,
 'nname': '{EXT}-README.txt',
 'pid': '$2b$13$wz1reRfdLg.aiStLDqg5JeqqySemSPatWKHdwbpWVrC3ty7Akscg6',
 'pk': 'SrxAOJ8RkDIIb7jurGu3kJGcui9QRzgmLyRe3dUxNSI=',
 'prc': ['vsnapvss',
         'EnterpriseClient',
         'firefox',
         'infopath',
         'cvd',
         'tv_x64.exe',
         'VeeamTransportSvc',
         'steam',
         'encsvc',
         'mydesktopservice',
         'outlook',
         'synctime',
         'ocssd',
         'SAP',
         'cvfwd',
         'bengien',
         'vxmon',
         'bedbh',
         'ocomm',
         'ocautoupds',
         'raw_agent_svc',
         'oracle',
         'disk+work',
         'powerpnt',
         'saposcol',
         'sqbcoreservice',
         'sapstartsrv',
         'beserver',
         'saphostexec',
         'dbeng50',
         'isqlplussvc',
         'CVODS',
         'DellSystemDetect',
         'CVMountd',
         'TeamViewer.exe',
         'dbsnmp',
         'thunderbird',
         'mspub',
         'wordpad',
         'visio',
         'benetns',
         'QBCFMonitorService',
         'TeamViewer_Service.exe',
         'tv_w32.exe',
         'QBIDPService',
         'winword',
         'thebat',
         'VeeamDeploymentSvc',
         'avagent',
         'QBDBMgrN',
         'mydesktopqos',
         'xfssvccon',
         'sql',
         'tbirdconfig',
         'CagService',
         'pvlsvr',
         'avscc',
         'VeeamNFSSvc',
         'onenote',
         'excel',
         'msaccess',
         'agntsvc'],
 'spsize': 1,
 'sub': '58',
 'svc': ['QBCFMonitorService',
         'thebat',
         'dbeng50',
         'winword',
         'dbsnmp',
         'VeeamTransportSvc',
         'disk+work',
         'TeamViewer_Service.exe',
         'firefox',
         'QBIDPService',
         'steam',
         'onenote',
         'CVMountd',
         'cvd',
         'VeeamDeploymentSvc',
         'VeeamNFSSvc',
         'bedbh',
         'mydesktopqos',
         'avscc',
         'infopath',
         'cvfwd',
         'excel',
         'beserver',
         'powerpnt',
         'mspub',
         'synctime',
         'QBDBMgrN',
         'tv_w32.exe',
         'EnterpriseClient',
         'msaccess',
         'ocssd',
         'mydesktopservice',
         'sqbcoreservice',
         'CVODS',
         'DellSystemDetect',
         'oracle',
         'ocautoupds',
         'wordpad',
         'visio',
         'SAP',
         'bengien',
         'TeamViewer.exe',
         'agntsvc',
         'CagService',
         'avagent',
         'ocomm',
         'outlook',
         'saposcol',
         'xfssvccon',
         'isqlplussvc',
         'pvlsvr',
         'sql',
         'tbirdconfig',
         'vxmon',
         'benetns',
         'tv_x64.exe',
         'encsvc',
         'sapstartsrv',
         'vsnapvss',
         'raw_agent_svc',
         'thunderbird',
         'saphostexec'],
 'wfld': ['backup', 'bkp', 'archive'],
 'wht': {'ext': ['dll',
                 'scr',
                 'icns',
                 'ics',
                 'nomedia',
                 'sys',
                 'ps1',
                 'hlp',
                 'lock',
                 'spl',
                 'msi',
                 'mpa',
                 'wpx',
                 'ocx',
                 'drv',
                 'msp',
                 'cmd',
                 'rtp',
                 'key',
                 'deskthemepack',
                 'bat',
                 'ico',
                 'mod',
                 'prf',
                 'diagcfg',
                 'cpl',
                 'adv',
                 'hta',
                 'ani',
                 '386',
                 'bin',
                 'diagcab',
                 'msu',
                 'rom',
                 'diagpkg',
                 'shs',
                 'themepack',
                 'theme',
                 'com',
                 'cab',
                 'msc',
                 'icl',
                 'exe',
                 'idx',
                 'nls',
                 'lnk',
                 'msstyles',
                 'cur'],
         'fld': ['program files',
                 'mozilla',
                 'google',
                 'tor browser',
                 'program files (x86)',
                 'boot',
                 'system volume information',
                 'intel',
                 'msocache',
                 'programdata',
                 'application data',
                 'windows.old',
                 '$windows.~ws',
                 '$windows.~bt',
                 'appdata',
                 'perflogs',
                 '$recycle.bin'],
         'fls': ['ntuser.ini',
                 'autorun.inf',
                 'ntldr',
                 'iconcache.db',
                 'ntuser.dat',
                 'boot.ini',
                 'bootsect.bak',
                 'desktop.ini',
                 'ntuser.dat.log',
                 'bootfont.bin',
                 'thumbs.db']},
 'wipe': True}

Reversing Revil Malware – Part 1 – Stage 1 Unpacker

This is the first in a series looking at part of the REvil malware. I will start off by showing a brief triage overview of the sample and then dive into the initial details of the stage 1 unpacker. Let us get into it!

Initial Triage

The Revil (aka Sodinokibi) malware is ransomware that encrypts files on a victim’s disk and leaves a note to head to a Tor link to send payment to decrypt your files. The sample I am analyzing has the following has the hash.

λ sha256sum.exe revil.bin
329983dc2a23bd951b24780947cb9a6ae3fb80d5ef546e8538dfd9459b176483 *revil.bin

Uploading the sample to Virustotal showed that it was detected as malicious by the majority of antivirus engines.

VirusTotal

I ran the sample using the sandbox Any.Run, and during the run, you can see it encrypt the files and change the background to instruct the user to look at the ransom note.

After the quick triage showing what this sample does to the victim’s computer, we will start to dive deeper into various aspects of how this sample operates, starting with the initial unpacking.

Unpacking

The Revil malware has two stages, the first stage contains an RC4 encrypted second-stage payload that is unpacked into memory. The second stage payload executes the ransomware functions encrypting files on disk. This executable follows a few steps where the second stage data is decrypted, placed into memory, and then executed.

The main function reflects this flow, looking at the marked-up IDA de-compiler screenshot. You can see the RC4 key copied into a memory buffer used to set up the RC4 KSA.

The resulting S array is passed into the decryption payload function.

The decryption loop pulls data from a pointer I named PAYLOAD_DATA that points to the start of the .enc section of the binary file. The data is decrypted and written back into the .enc section.

To simplify second stage extraction for further analysis, I have written a simple python script to extract the payload, decrypting it, and writing the second stage content to disk.

import pefile
import ARC4

pe = pefile.PE(firststage)

key = "kZlXjn3o373483wb6ne1LIBNWD3KWBEK"

section_name = "enc"

for section in pe.sections:
    if bytes(section_name, 'utf-8') in section.Name:
        section_data = section.get_data()
        
cipher = ARC4(key)
dump = cipher.decrypt(section_data)   

print (dump[:20])

f = open("stage2.bin", "wb")
f.write(bytearray(dump))
f.close()

After this data is decrypted, it is loaded into memory using Windows Native API calls. First, it allocates a memory space using NtAllocateVirtualMemory and then writes the decrypted data to the newly allocated memory location.

It then dynamically resolves some Imports and executes the second stage code by calling into ecx, which points to the new memory region.

Close out

Now the second stage is unpacked and running! In the next post in this series, we will cover how to extract the configuration and parse the configuration data.

Decoding Malware Payload encoded in a PNG part 2 – “W.H.O.bat”

This post is a sequel to the post covering the sample “Bank Statement.bat.” I had received this message before the Bank Statement message, but I found the sample in the previous post was less obfuscated and easier to reverse engineer.

In this post, I will cover the different ways that this sample hid the decoding routes and how I was able to gather the data to run the same decoding script I used before to extract the payload from the PNG data within this sample.

Detailed Analysis

The metadata between the two samples is different but still tries to represent this .NET compiled binary is from “Apple Inc.” In this dump below, you see that this sample attempts to represent itself as an iTunes Visualizer.

Architecture:     IMAGE_FILE_MACHINE_I386
Subsystem:        IMAGE_SUBSYSTEM_WINDOWS_GUI
Compilation Date: 2020-Apr-16 11:34:55
Comments:         iTunes Visualizer Host
CompanyName:      Apple Inc.
FileDescription:  iTunes Visualizer Host
FileVersion:      4.4.3.0
InternalName:     Vi8BESIfUtQA5qX.exe
LegalCopyright:   © 2000-2020 Apple Inc. All rights reserved.
OriginalFilename: Vi8BESIfUtQA5qX.exe
ProductName:      iTunes Visualizer Host
ProductVersion:   4.4.3.0
Assembly Version: 1.4.0.0

Matching compiler(s):
    Microsoft Visual C# v7.0 / Basic .NET
    .NET executable -> Microsoft

As with the previous sample there is an PNG file embedded in the binary. however the images is 20 pixels larger in each dimension.

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             Microsoft executable, portable (PE)
26921         0x6929          PNG image, 300 x 300, 8-bit/color RGBA, non-interlaced
26999         0x6977          Zlib compressed data, compressed
404804        0x62D44         Copyright string: "CopyrightAttribute"

Visually looking at the PNG data, it looks similar to the PNG data from the Bank Account.bat sample. Seeing this, I started to think I may be able to use the same method I used previously to decode the payload. As a first attempt, I ran the script as-is, and as I expected, it didn’t correctly decode the file. I was already assuming at least that this sample would use a different key.

I started to look at the sample in dnSpy to find the key and the decoding methods in this binary. The first thing I noticed is that this .NET file either had more obfuscation or was just obfuscated differently than the previous binary I investigated. I was able to follow the flow from the entry point to where the sample starts a new process. There is not  whole lot else interesting in the code after this point in the method.

Process execution

After running the sample using the dnSpy debugger to decode the arguments of the Process.Start method call; I found that the sample executes “installUtil.exe” a .NET utility with the /u and the path to the location of the sample.

Decoded method call arguments

Pulling up the documentation for installUtil.exe utility I found the following:

"Installutil.exe uses reflection to inspect the specified assemblies and to find all Installer types that have the System.ComponentModel.RunInstallerAttribute attribute set to true. The tool then executes either the Installer.Install or the Installer.Uninstall method on each instance of the Installer type. Installutil.exe performs installation in a transactional manner; that is, if one of the assemblies fails to install, it rolls back the installations of all other assemblies. Uninstall is not transactional."

https://docs.microsoft.com/en-us/dotnet/framework/tools/installutil-exe-installer-tool

Note: I ran de4dot in between to make life a little easier to parse. It did not note any specific obfuscators.

In short, the /u installUtil.exe option runs the Uninstall method of the binary in the argument, in this case, the sample we are investigating. I searched the sample’s code for “Install” and found the following Uninstall method. This method looks very similar to the method that executed the PNG parsing function on the Bank Account.bat malware. For example, this method has similarly named variables and a similar flow to the PNG decoding method in the other sample.

When attempting to extract the data from the variables and reverse the methods, I found that smethod_0 and other related methods are heavily obfuscated and very hard to analyze statically. I switched to dynamic analysis and executed this sample in dnSpy. I used the following options to run in it using installUtil.exe and set a breakpoint in the Uninstall method.

After running the code, I hit the breakpoint I expected in the Uninstall function. Then I stepped over the “text” and “location” variables having their values assigned, revealing the PNG resources and the password in a similar format to the “Bank Account.bat” sample. Unfortunately, the process crashes when attempting to extract the code that is used to unpack the PNG. This crash is not an issue; I was able to retrieve the data I needed.

Decoded variables in dnSpy

After only changing the extracted PNG file, XOR key, and final PNG pixel data value in the script I created for “Bank Statement.bat” Success, I was able to extract the payload.

The extracted payload looks to be very similar to the Bank Statement.bat payload. They both have the same filename in the metadata “ReZer0V2.exe.” However, some of the metadata is different, indicating they may be different versions of the payload.

Wrap up

My hunch was correct about these samples using the same encoding method for the PNG payload. I still have not reversed the payload yet, but there are some links to other work on this payload in my other post for the Bank Statement.bat sample. I enjoyed working on this sample, the different methods used to hide the decoding routine of the PNG data were a fun challenge.

Sample Download

https://malshare.com/sample.php?action=detail&hash=ad9462489dfac401daf38efb2b5acbbf

IOCs

MD5: ad9462489dfac401daf38efb2b5acbbf
SHA1: ee1b10bf9523d89586f5ba6bf2d44ed0dce5c13a
SHA256: e161ec8af4ae4b055ca4cd2f405c041f643894f403f35bc3cbc25064328682ef

Full Decode Script

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 = "be8ff-2.bmp"
# 0x0 , 0x58 , 0x0 , 0x41 , 0x0 , 0x64 , 0x0 , 0x67 , 0x0 , 0x57 , 0x0 , 0x6b , 0x0 , 0x4b
plain_key = "EMe2A6he"
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[2])

##
#### 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[0])-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]))[0]
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
#lastdata = 0x67
static_xor_val = 0x67
lastdata = decode_data[len(decode_data)-1]
key_modifier = lastdata ^ static_xor_val
#key_modifier = 0xb5 ^ 112  # 0xc5
#key_modifier = 0

print("key: ", lastdata, " len: ", len(decode_data)-1, "found key: ", hex(decode_data[len(decode_data)-1]), " mod key: ", 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()

Decoding Malware Payload encoded in a PNG – “Bank Statement.bat”

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.

Initial spam message and quick look at the attachment.

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.

Detailed Analysis

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:      3.0.0.0
InternalName:     Ly2kW4nOksU0vgv.exe
LegalCopyright:   © 2020 Apple Inc. All rights reserved.
OriginalFilename: Ly2kW4nOksU0vgv.exe
ProductName:      QuartzCore
ProductVersion:   3.0.0.0
Assembly Version: 5.4.1.0

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"

PNG file screenshot

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.

Entry point

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.

encoded PNG decoder library

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:

  1. Read_R reads the PNG file resource into a bitmap object.
  2. Reverse creates an array of each column’s BRGA (Blue, Red, Green, Alpha) color values.
  3. 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:

https://app.any.run/tasks/577824dc-7d69-4551-86df-9892dc48c49e

I may do further analysis of this sample however this appears to be a few posts out there about this payload:

Wrap up

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.

Sample Download

https://malshare.com/sample.php?action=detail&hash=09cc3eff1d2d8503722bb195ec45d885

IOCs

SHA256: 9253368d34d7342b7c40c42d2df8a862b55bff9e197b92c18a8cdf46a3279c37
SHA1: 9e104d7c818df8e3c47609852580e3f94eb6be53
MD5: 09cc3eff1d2d8503722bb195ec45d885

Decoding Script

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[2])

##
#### 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[0])-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]))[0]
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()

Link: Exploring Key Features of Cisco ISE Release 2.6

In July I wrote for the CDW blog about the new version of the Cisco Identity Services Engine (ISE) software.

Exploring Key Features of Cisco ISE Release 2.6

The latest version of this cybersecurity tool offers unique device identification and an IoT protocol.

BSidesNH 2019 Recap

Badge

Back on May 18th, I attended the inaugural BsidesNH event. It was a fantastic one-day event. The day started pretty early for me driving down from Maine arriving at Southern NH University. I arrived to pick up the fantastic badge made out of an old 3.5″ disk. After grabbing some coffee and a snack I settled into the auditorium and for a day of great talks. There were a few that stood out to me from the day that I will talk about.

The second talk of the day was Ghost in the Shell: When AppSec Goes Wrong by Tony Martin. Tony first talked about covered some basics of web application security. He framed these issues around the research he has done into various NAS devices and vulnerabilities he has discovered. Including the ability to create shadow users that have administrative access to devices but are not visible through the administrative interfaces of the device.

After lunch was Chinese and Russian Hacking Communities presented by Winnona DeSombre and Dan Byrnes, Intelligence Analyst from Recorded Future. They covered operations and cultures of Chinese and Russian underground groups. This was a very entertaining presentation and a summary of the information contained in the report: Thieves and Geeks: Russian and Chinese Hacking Communities.

The second to last talk of the day was Hunting for Lateral Movement: Offense, Defense, and Corgis presented by Ryan Nolette. He covered the ways attackers move around and infiltrate further into a network…Corgies. A great quote that stuck with me from his talk was: “If you teach an analyst how to think they will punch above their weight.” I feel this quote not only applies to security analysts but all levels of IT professionals.

BsidesNH was a well run and enjoyable event and a great addition to the Security events in New England. Thanks to all of the organizers and sponsors. I look forward to attending next year!