Not Expir-ed yet

2014-03-04

Raul Alvarez

Fortinet, Canada
Editor: Helen Martin

Abstract

Expiro is a file infector that resurfaces from time to time, demonstrating more skills on each new appearance – infecting a service that gives a unique vantage point on traditional malicious activities; running the malware at computer restart without creating a start‑up registry; using different mutexes for different types of infected process; escalating privileges; and executing infected files without calling the CreateProcess or WinExec APIs. Raul Alvarez takes a closer look.


Most advanced file infectors employ a strong encryption algorithm with a mix of anti-debugging and anti-analysis techniques, but once you overcome these challenges, they are straightforward with their file infection routine – searching for files to infect, computing the exact location to put the malware body, configuring the MZ/PE header, and there you have it: a file infector.

That’s what Expiro looks like on the outside: a simple file infector with a straightforward encryption mechanism. But if we delve into its code, we will see something that sets it apart from regular file infectors.

This article will look into Expiro’s simple, but meticulous sequence of steps that leads to the infection of a unique group of files before infecting the rest of the executable files in the system.

Simple polymorphic engine

Expiro starts by saving all register values to the stack and initializing the variable that contains the decryption key for the polymorphic engine.

For the first pass, the malware decrypts (0x32A00) 207,360 bytes using a key starting halfway through the .vmp0 section. It decrypts each byte from the middle of the .vmp0 section working backwards to the beginning of the section, where the least significant byte of the DWORD decryption key is used to XOR every byte in the given block of encrypted code.

After the first pass, the malware checks whether it has reached the required number of decryption passes. If it has not, it will execute the same decryption algorithm again.

On the second decryption pass, after decrypting 207,360 bytes, Expiro decrypts the same group of bytes back to its original form using the same algorithm. It makes the same decryption pass over and over again six times, always producing the same original form.

For every decryption pass, the malware increments a counter. The same counter is used within the decryption algorithm, combining it with the decryption key to produce a different result. But after six passes, the bytes are still in their original encrypted form. The changes can only be seen after the seventh decryption pass.

After performing the seventh decryption pass, the (0x32A00) 207,360 bytes are properly decrypted. A block of (0x24880) 149,632 bytes from the newly decrypted code is copied to the free space of the .vmp0 section. This is followed by copying another (0xA1C8) 41,416 decrypted bytes to the free space of the .vmp0 section, beyond the first block.

The decrypted code distributed in the free space of the .vmp0 section is not yet functional. Some of its binaries need further tweaking. These blocks of code need patching.

Expiro uses a table that contains keys with corresponding sizes. Each key is processed to produce patched code, with its size determining the number of times the patched code should be applied. The algorithm that produces the patched code also determines where in the decrypted code the patched code should be applied. Once a patch has been applied the correct number of times, the next key from the table is processed to produce patched code, and again applied to the decrypted code where the last patch was applied. The malware continues patching the decrypted code until all the keys from the table have been used.

Hashing initial API names

A common method of getting the imagebase address of kernel32.dll is by parsing the PEB (Process Environment Block). For file infectors, another method is to parse the import table of the host file.

Parsing the import table to locate kernel32 can be done in many ways. Expiro gets the DLL names and checks the seventh and third characters to determine if they match ‘3’ and ‘r’ from ‘kernel32.dll’, respectively. If they match, the rest of the characters in ‘kernel32’ are also checked.

Then, Expiro gets the imagebase of kernel32.dll by zeroing out the least significant DWORD of the address of the first API found.

Afterwards, Expiro resolves the addresses of some of its APIs using their hash values.

Initially, a routine looks for the export table of kernel32.dll and locates the list of exported API names. The malware computes the hash value of each API name using its own hashing algorithm. Each hash value is compared against the hash value of ‘GetCurrentThreadId’ until they match. The address of the GetCurrentThreadId API is resolved by using the index pointing to the matched API name.

A similar routine resolves the following APIs: CreateThread, EnterCriticalSection, GetProcAddress, GetTickCount, InitializeCriticalSection, LeaveCriticalSection and VirtualProtect.

Commonly, the resolved API addresses are stored in a table with consecutive memory locations, but not in this case.

Once all the API addresses have been resolved, it is easy to determine the next actions of the malware by looking at the list of addresses. For Expiro, each resolved API address is scattered in a different location in its virtual space, making it a challenge for analysts wanting to look at them all at once. Figure 1 shows the resolved APIs stored in different memory locations.

Once the initial APIs have been resolved, Expiro spawns a new thread.

The resolved APIs which are stored in different memory locations.

Figure 1. The resolved APIs which are stored in different memory locations.

On-demand decryption algorithm

Upon execution of the new thread, Expiro executes another decryption algorithm to generate the API names that the malware needs.

The API names are grouped by libraries with the exception of the LoadLibraryA and GetModuleHandleA APIs. Each group of APIs passes through a routine that contains the decryption algorithm and address resolver.

The on-demand decryption algorithm computes each byte of data with a byte taken from the key string ‘V@sna8TbCzTSrs:s[fR@6’. An incrementing pointer determines which byte will be used to decrypt the byte from the given memory location. When the last character from the key string is reached, the pointer will reset to point back to the first character.

There are separate routines for each group of APIs per library (DLL). After decrypting the API names, each routine checks if the module/library already exists by using the GetModuleHandleA API. If it does not exist, it will load the module by calling the LoadLibraryA API. Then, the routine will use the GetProcAddress API to determine the actual API addresses.

Every string or name used by Expiro passes through the on-demand decryption algorithm. All the API names, library names, mutex names, and all other strings use the same decryption. These names and strings are only decrypted when Expiro needs them (see Figure 2).

Part of the on-demand decryption algorithm with sample encrypted and decrypted strings.

Figure 2. Part of the on-demand decryption algorithm with sample encrypted and decrypted strings.

Under WOW64

After getting the addresses of all the required APIs, Expiro checks if it is running under WOW64 (32-bit Windows on 64-bit Windows).

Initially, it checks if the operating system supports WOW64 by checking the OS version using the GetVersionExA API. This is followed by getting its own PID (process id) using the GetCurrentProcessId API, and getting the handle by opening it using the OpenProcess API. Finally, it uses the IsWow64Process API to determine if it is running under WOW64.

This is also the part where Expiro determines the kind of infection needed, since the malware is capable of infecting both 32- and 64-bit executables.

If the malware is not running under WOW64, it can function as a 32-bit piece of malware on 32-bit operating systems, or as a 64-bit piece of malware on 64-bit operating systems.

Checking security access level

After determining whether it is running under WOW64, Expiro checks its security access level using the following routines:

The malware gets the username of the current thread using the GetUserNameA API, and compares it to the newly decrypted strings ‘SERVICE’ and ‘SYSTEM’. Expiro skips this routine if the username contains the strings ‘SERVICE’ or ‘SYSTEM’ – it assumes that it has a higher security level if the username contains these strings.

If it doesn’t have the above credentials, it gets the computer name using a simple call to the GetComputerNameA API, and compares it against the username. If the username of the current thread is also the computer name, the malware will again try to skip the current routine.

Otherwise, it proceeds to acquire the environment block of the current process using the GetEnvironmentStrings API. Expiro searches for the strings ‘systemprofile’ and ‘ervice’ within the environment block. Since the malware checks byte by byte, ‘ervice’ will yield to true if it finds either ‘service’ or ‘Service’. Normally, services that run in the system will contain these strings.

If the malware has a higher security access level on the system, it will skip the routine that escalates its privilege level.

Handling mutex

After determining the malware’s security access level, Expiro decrypts more strings (‘kkq-vx’ and ‘%s_mtx%u’) and checks if a mutex named ‘kkq-vx-mtx28’ exists, using the OpenMutexA API. If the mutex exists, it will terminate it using a call to the CloseHandle API.

The malware will loop back on the start of this routine to check for the existence of other mutexes by changing the value 28 in ‘kkq-vx-mtx28’. (The value may be different in some infected samples.) The number increments by one each time, until it reaches 99. In simpler terms, Expiro checks for the existence of mutexes ‘kkq-vx-mtx28’ up to ‘kkq-vx-mtx99’. If any of these exist, they will be terminated.

After terminating all of these mutexes, a new routine is started. This time, a mutex named ‘kkq-vx-mtx1’ is created using the CreateMutexA API. This is followed by creating a second mutex named ‘kkq-vx-mtx28’. Finally, the WaitForSingleObject API is called, with parameter WAIT_FOREVER for the mutex ‘kkq-vx-mtx28’ (see Figure 3).

Mutexes for infected service and infected file.

Figure 3. Mutexes for infected service and infected file.

Afterwards, Expiro decrypts another string, ‘gazavat-svc’, and checks if a mutex named ‘gazavat-svc’ exists using another call to the OpenMutexA API. If it exists, it will also be terminated.

The mutex ‘gazavat-svc’ will be used later on, after the infection routine.

Escalating access

After handling the mutexes, and if the security access level of the malware is not high enough, Expiro will try to escalate its privileges using the following routine:

Initially, the malware gets the PID (process ID) using the GetCurrentProcessId API, and gets the handle to the process using the OpenProcess API. Afterwards, it gets the access token of the process, and its data, using the OpenProcessToken and GetTokenInformation APIs, respectively.

Expiro uses the token in conjunction with the security descriptor. After initializing the security descriptor using the InitializeSecurityDescriptor API, the malware sets the discretionary access control list (DACL) using the SetSecurityDescriptorDacl API, followed by setting the owner information of the security descriptor using the SetSecurityDescriptorOwner API. The malware evaluates this information before it performs the escalation.

For the actual escalation, the malware acquires the locally unique identifier (LUID) of the SE_TAKE_OWNERSHIP_NAME privilege using the LookupPrivilegeValueA API, then sets the privilege using the AdjustTokenPrivileges API.

At this point, Expiro should have the necessary security privileges to take ownership of any process or file in the system.

Preparing for infection

After a considerable number of tasks and routines have been performed, Expiro is ready to choose which file to infect. But the preparation is not yet over: instead of just enumerating the executable files by performing simple calls to the FindFirstFileA and FindNextFileA APIs with ‘*.EXE’ or ‘*.SCR’ as parameters, Expiro wants a different set of executable files: services.

Let’s look into the preparation.

First, Expiro gets the handle for the service control manager database using a call to the OpenSCManagerA API. This is followed by enumerating the services with name and status using the EnumServicesStatusA API with parameters:

dwServiceType = (0x30) SERVICE_WIN32_OWN_PROCESS and SERVICE_WIN32_SHARE_PROCESS

dwServiceState = (0x02) SERVICE_INACTIVE

Basically, Expiro is searching for inactive or stopped services in the system.

Expiro opens the service from the enumerated list using the OpenServiceA API, and retrieves the configuration parameters using the QueryServiceConfigA API. The service’s configuration contains properties including service name, start up type, service status, and path to the executable file of a given service.

Expiro is interested in the physical location of the executable file of a given service. It converts the whole path name to lower case, then checks if it contains ‘.exe’ – making sure that it really is an executable file.

If the path name passes a series of checks, the malware converts it to a Unicode version of the string for use in a call to the SfcIsFileProtected API. Expiro wants to determine if the specified file is protected. If it is protected, the malware will remove the protection.

After removing the file’s protection, the malware checks if the path name contains the strings ‘rsvp.exe’ (Microsoft RSVP), or ‘chrome.exe’ (Google Chrome) – if either of these strings is found, the malware will skip the infection routine.

Infection routine

After the necessary checks, Expiro will perform the infection routine for the selected service’s executable file.

First, it opens the executable file using the CreateFileA API with GENERIC_READ and GENERIC_WRITE access. It acquires the file size using the GetFileSize API, then copies the file to the newly allocated memory using the ReadFile API, effectively creating an exact image of the service’s executable file.

Once the image is loaded into memory, the malware parses the MZ/PE header to point to the import table and locate the DLL names. Expiro tries to locate kernel32.dll using the following routine:

After getting the DLL name from the import table list, the names will be converted to all caps. Then, the first filter is to check for the eighth byte (char ‘2’) – most DLL names fail this check. The second filter is to check for the seventh byte (char ‘3’) – ADVAPI32.DLL can still pass this one. The next filters are ‘L’, ‘E’, ‘K’, and ‘R’. Finally, it saves the location of ‘KERNEL32.DLL’ for later use.

Once kernel32.dll is secured, the PE header of the image is expanded to make room for the new section header. After a few computations, Expiro modifies the new section header with the following sequence:

  • The new section’s virtual size gets the value (0x7d000) 512,000, which corresponds to the increase in size of the infected file.

  • The starting relative virtual address (RVA) of the new section is also set, which varies between different infected files. Generally, the RVA of the new section is right after the end of the previous section.

  • The NumberOfRelocations, NumberOfLineNumbers and PointerToLineNumbers fields are zeroed out.

  • The malware uses ‘.vmp0’ as the name of the new section, which we already know.

  • The PointerToRelocations field is zeroed out.

  • The value (0x7d000) 512,000 is placed in the SizeOfRawData field, similar to the VirtualSize field.

  • The PointerToRawData or the file offset of the start of the new section is set with a value that depends on the size of the host file.

  • The section’s characteristics are set to (0xE0000000) Executable | Readable | Writable.

After setting up the new section header, the malware sets the SizeOfImage to 0x81000 (which varies between different infected samples). This is followed by incrementing the number of sections by one, to accommodate the newly added section.

Once the MZ/PE header has been modified and the new section header has been added, Expiro generates random values and patches a block of code containing a copy of the malware body.

This is followed by copying a portion of the image (from the entry point) to the patched block of code in memory.

Then, it copies the (0x7C6CD) 509,645 bytes of code (containing the patched code and the portion of the image) to the start of the new .vmp0 section.

Since the malware code at the new .vmp0 section is not yet encrypted (just recently patched), Expiro runs a simple encryption routine to encrypt the content of the .vmp0 section of the image, byte by byte. The encryption skips the copied portion from the image’s entry point.

Applying the changes

After the modification of the service’s executable image in memory, it will release the handle for the physical file using the CloseHandle API.

Afterwards, Expiro creates a ‘.vir’ file, e.g. ‘{service_filename}.vir’, using the CreateFileA API, which is followed by copying the infected service’s image from memory to the .vir file, using the WriteFile API.

Then, the malware frees up the image using the LocalFree API and closes the handle to the .vir file. Expiro copies the .vir file to its .exe counterpart (e.g. it copies ‘XYXYXservice.vir’ to ‘XYXYXservice.exe’), using the CopyFileA API. After a successful copy, the malware deletes the .vir file using the DeleteFileA API.

Expiro checks if the service name of the infected file is found in the string ‘|wscsvc|WinDefend|MsMpSvc|NisSrv|’, which contains the names of the services for Security Center, Windows Defender, Microsoft Antimalware Service and Microsoft Network Inspection, respectively.

If the service name is found, the malware disables the service permanently by calling the ChangeServiceConfigA API with the (dwStartType) SERVICE_DISABLED parameter. Then it exits the current routine.

If the service name of the newly infected file is not in the list, it will set the service’s configuration to the following parameters: (dwServiceType) SERVICE_INTERACTIVE_PROCESS | SERVICE_WIN32_SHARE_PROCESS, and (dwStartType) SERVICE_AUTO_START, using the ChangeServiceConfigA API. Earlier, in selecting which services to infect, Expiro searched for inactive and stopped services. Once these services are infected, the malware changes their start type to start automatically when the operating system restarts. This is another technique for making sure the malware runs during the boot process.

Expiro then runs the infected service simply by calling the StartServiceA API. One of the markers that indicates that a service is infected is the presence of the mutex named ‘gazavat-svc’ (see Figure 3).

Afterwards, the malware will proceed with the infection of the rest of the inactive services, while the rest of Expiro’s malicious activities will be executed within the service’s process.

Wrapping up

Expiro relies heavily on the use of its on-demand decryption algorithm. Although not too complex, it serves its purpose well – not revealing everything all at once.

Expiro is not a new file infector, but it resurfaces from time to time, demonstrating more skills on each new appearance – infecting a service that gives a unique vantage point on traditional malicious activities; running the malware at computer restart without creating a start up registry; using different mutexes for different types of infected process; escalating privileges; and executing the infected files without calling the CreateProcess or WinExec APIs.

Expiro has other malicious activities which are beyond the scope of this article. Simple or not, this is not the last time that we will hear from this malware.

twitter.png
fb.png
linkedin.png
hackernews.png
reddit.png

 

Latest articles:

Nexus Android banking botnet – compromising C&C panels and dissecting mobile AppInjects

Aditya Sood & Rohit Bansal provide details of a security vulnerability in the Nexus Android botnet C&C panel that was exploited to compromise the C&C panel in order to gather threat intelligence, and present a model of mobile AppInjects.

Cryptojacking on the fly: TeamTNT using NVIDIA drivers to mine cryptocurrency

TeamTNT is known for attacking insecure and vulnerable Kubernetes deployments in order to infiltrate organizations’ dedicated environments and transform them into attack launchpads. In this article Aditya Sood presents a new module introduced by…

Collector-stealer: a Russian origin credential and information extractor

Collector-stealer, a piece of malware of Russian origin, is heavily used on the Internet to exfiltrate sensitive data from end-user systems and store it in its C&C panels. In this article, researchers Aditya K Sood and Rohit Chaturvedi present a 360…

Fighting Fire with Fire

In 1989, Joe Wells encountered his first virus: Jerusalem. He disassembled the virus, and from that moment onward, was intrigued by the properties of these small pieces of self-replicating code. Joe Wells was an expert on computer viruses, was partly…

Run your malicious VBA macros anywhere!

Kurt Natvig wanted to understand whether it’s possible to recompile VBA macros to another language, which could then easily be ‘run’ on any gateway, thus revealing a sample’s true nature in a safe manner. In this article he explains how he recompiled…


Bulletin Archive

We have placed cookies on your device in order to improve the functionality of this site, as outlined in our cookies policy. However, you may delete and block all cookies from this site and your use of the site will be unaffected. By continuing to browse this site, you are agreeing to Virus Bulletin's use of data as outlined in our privacy policy.