OUR BLOG

Blog

What Year Is It? VB6 Payload Crypter

Last year, researchers identified new crimeware, Loki-Bot, which steals data and login credentials. Loki-Bot is generally distributed through malicious spam, and is difficult to identify without getting into the malware. Loki-Bot crimeware targets Windows, in contrast to the recent Android banking ransomware, Loki Bot. While the crimeware is not as prevalent in the wild, it has some unique and differentiating characteristics

Loki-Bot’s crypter is especially interesting and unique because it utilizes Visual Basic 6.0 to load multiple stages of shellcode to deliver the Loki-Bot payload. We saw an interesting sample highlighted by @DissectMalware on Twitter and decided to take a closer look for ourselves. We’ll walk through Loki-Bot’s crypter functionality, the first and second stage shellcodes, the payload, and then provide some thoughts on stopping these kinds of attacks and what we can expect to see next.

Loki-Bot Crypter Functionality

The main Visual Basic compiled binary uses a raw pointer access technique to jump into the first stage shellcode which then calls the second-stage shellcode that is executed off of the stack. The first stage shellcode disables data execution prevention (DEP) to make the stack executable, and uses jmp esp to jump into the stage 2 shellcode to allow it to begin execution. Stage 2 then sets up persistence, decrypts the payload, and executes the payload contents by using process hollowing. In this case the payload is Loki-Bot, crimeware designed to steal private information from a system. Loki-Bot’s functionality has already been covered in detail elsewhere, so we will instead focus on the mode of compromise and its anti-reversing engineering tactics.

Delivery Context

This particular malware sample began its life as an executable with an RTF exploit. Generally an RTF exploit is a specially crafted file that exploits vulnerabilities in the Rich Text Format parser of an application like Microsoft Word or Adobe Acrobat in order to gain code execution on the victim. The malware can then use this initial code execution to begin its exploit chain. Usually, crafted files are spread as attachments in phishing emails, which was the case in this sample. Once the malware gets code execution on the host it then downloads jazz.exe from the link below.

PE Description

The binary was compiled with VB6.0 professional/enterprise and so contains normal x86asm with a dependency on msvbvm60.dll. Stage 1 makes specific use of the visual basic runtime DLL to make dll calls to other libraries.

First Stage Shellcode

The first stage shellcode exists within the VB6 portion of the malware, which we’ll refer to as the crypter. The first stage shellcode exists in the “rentegninger” sub module. The original sub module is then partially overwritten with obfuscated shellcode. The “Remanipulation8” public function is called from Load_Form(). This function manipulates the list of values of the Virtual Function Table returned by the “Me” reference to the form.

Set var_2 = Me

How to Get the Shellcode Entrypoint

Stage 1 overwrites one of the variables in the compiled Visual Basic 6 code to point to an offset in the middle of the “rentegninger” submodule. The pointer is a hard coded integer that is calculated with division and a square root as shown below.

Utilizing StrPtr to Access Addresses

In the code example below the pointer to the Me value points to the beginning of the Virtual Function Table of the class that Me points to. In this case the Class is a Form. The offset 0x2B0 is actually the function “Show”. The pointer to the show function is overwritten by the entry point of the shellcode which is 0x43AF4B. Then you can easily call “Form.Show” and call into your shellcode. An example of this is located in the Appendix.

var_num1 = StrPtr(var_2) + 2B0h
ReplacePtr(var_num1, new_value)

This value is later called in the “Remanipulation8” function as call dword ptr [eax+2B0h]. It treats this call as a method of the Me object. DispCallFunc .

String-to-Stack Method

The crypter uses a common trick to get the strings that are inline with the assembly onto the stack. When a call is made, the next address gets pushed onto the stack as the return destination address. A disassembler will try to disassemble the string even though it never gets executed.

Using Shell_NotifyIcon

The first stage shellcode uses the Shell_NotifyIcon in a non-standard way. It passes an address off the stack that does not resemble a proper PNOTIFYICONDATA struct. The Windows API still processes the events as if they were normal. As you can see below, the Shell_TrayWnd icon is junk data for this process. It is called twice where NIM_ADD and NIM_DELETE are used.

Utilizing the PEB Loader and DllFunctionCall

The first stage shellcode uses a common technique among other Windows shellcode to get a reference to DllFunctionCall by utilizing the Process Environment Block (PEB). The PEB is a data structure provided to every running process, and can be used to gain information about that process such as environment variables, image base addresses and DLL imports. This shellcode contains a PEB loader routine that gets a reference to msvbvm60.dll and then finds the offset of DllFunctionCall at 0x8D560CEC. Once it has the correct offset to DllFunctionCall, it can then use it to load Windows APIs so that it can make calls to them. More information on the PEB can be found here.

In a nutshell, the PEB is a linked list of offset values and the string names of the desired functions. You can linearly traverse the linked list, check for the desired function and save its offset if found.

Disabling Data Execution Prevention (DEP)

The function ZwSetInformationProcess can be called with parameters -1 and 0x22 to turn off DEP for the process. DEP was originally intended to prevent programs from executing code on the stack, however DEP can also cause normal programs to crash without any notifications. Giving the program the ability to turn off DEP for itself allows the malware to avoid unknown crashes while also allowing malware authors to execute shellcode explicitly on the Stack. Microsoft Support provides additional details about DEP.

Decoding the Shellcode

The stage 1 shellcode then utilizes JMP ESP to jump into that stack at the offset where the buffer for stage 2 shellcode was allocated. The stage 2 shellcode that was initially loaded onto the stack undergoes an initial pass of XOR decoding with an immediate value of 0x510473D1.

Second Stage Shellcode

Sandbox Evasion

The stage 1 shellcode executes CPUID to detect if it’s being run in a virtual environment. If it is running in a virtual environment, it exits. If the stage 1 shellcode is not running in a virtual environment, then it continues execution normally. As detailed elsewhere, the malware sets the EAX register to 1, calls CPUID and then checks the 31st bit of the ECX register by applying a bitmask. If the 31st bit is 0 it knows it’s being run in a virtual environment.

Subsequently, the stage 1 shellcode calls the sleep function. Sleeping for prolonged durations of time is one evasion technique used by malware to subvert detection in sandboxed environments which are usually constrained by resource allocation to not run any given sample for longer than some established period of time, such as 30 seconds. Thus for the first 30 seconds of the program’s lifetime, it is benign and might fool some sandboxing environments.

Anti-Debugging using NtYieldExecution

The ntdll function NtYieldExecution or its kernel32 equivalent SwitchToThread function allows the current thread to allocate the rest of the execution time, and allows the next scheduled thread to execute. If no threads are scheduled to execute or when the system is busy (and will not allow such a switch to occur), the ntdll NtYieldExecution() function returns a STATUS_NO_YIELD_PERFORMED (0x40000024) status code, which causes the kernel32 SwitchToThread function to return zero. When an application is being debugged, the act of single-stepping through the code causes debug events to occur and often results in no yield being allowed to occur. However, this is a hopelessly unreliable method of detecting the presence of a debugger because this method will also detect the presence of a thread that is running with high priority. An example of this code can be found here.

Check for Adapters and Windows

The stage 2 shellcode calls VirtualAllocEX to populate a new memory region with GetAdaptersInfo. It checks the offset +10Ch of the struct for the Description. It then calls EnumWindows to check if the window has an empty string, most likely an attempt to detect execution within some sandbox.

Persistence and Process Hollowing

The stage 2 shellcode takes two routes. During the first route, the shellcode sets up persistent mechanisms with schtasks.exe. It then decrypts the payload with Xor and RC4 during the second route, creating a suspended process of itself and then hollows it out with the payload’s contents. Each route is explained below.

Route 1 (Persistence)

The stage 2 shellcode’s first route will acquire the hardcoded strings APPDATA=, TEMP=, and copied.exe in order to place a copy of itself in the %APPDATA% and %TEMP% locations as %AppData%\\Roaming\\copied.exe. Once the path is acquired, it will create a scheduled task to copy and run copied.exe using ShellExecuteA.

Route 2 (Process Hollowing)

The stage 2 shellcode’s second route focuses on decrypting the executable payload into memory and then hollowing out a child process to execute the payload. “Wee2" is the marker on the stack in the shellcode and in the file that denotes the beginning of the copy operation. The format is Wee2<length of payload><key>.

Decrypting the Payload with Key

The key is stored in the shellcode after the marker and size in the shellcode. Its length is 0x100h, and is shown below.

The stage 2 shellcode uses the 0x100 sized byte key to xor the Loki-Bot payload of 0x34801 size then uses the same key to RC4 decrypt the product of the xor operation.

The shellcode then calls CreateProcessW to create a process of itself in suspended mode. It uses NTCreateFile, NTWriteVirtualMemory, NTUnMapViewofSection for process hollowing on the newly created process which contains the Loki-Bot payload. NtGetContextThread and NtSetContextThread and NtSetContextThread resume the process. The parent process terminates allowing the child to run as an orphan process. Because the Loki-Bot payload has already been reviewed by many researchers, we did not post the details about this malware.

Conclusion

There are a few key aspects to this crypter and its behaviour that make it fishy, including its crafty implementation of the VB6 runtime in shellcode, and use of anti-reverse engineering techniques and process hollowing. First, VB6 and the VB6 run time are rather old. While there are numerous binary distributions of software in the wild that were built with VB6 enterprise, it is still suspicious. Other suspicious activities include disabling its own DEP and checking if it’s being virtualized. Lastly, the crypter makes calls to the Windows API with malformed structs (i.e. the lack of an image for Shell_NotifyIcon). The combination of all of these suspicious activities could signal to a sensor like Endgame MalwareScoreTM that the program is up to no good, allowing us to stop it before the final execution occurs.

As for the future, we are likely to see more samples using legacy run times and features. Judging from this sample, a performant Visual Basic 6 crypter has recently been distributed in the wild. It seems natural that in the future its capabilities will improve and the volume of distribution will increase with continued black market adoption.

Appendix: Stage 1 Code Replication

After careful analysis, we were able to reproduce the method of Stage 1 shellcode execution in VB6. The code below will only display an empty message box.