During December 2017, a new variant of the GlobeImposter Ransomware was detected for the first time and reported on malware-traffic-analysis. At first sight this ransomware looks very similar to other ransomware samples and uses common techniques such as process hollowing. However, deeper inspection showed that like LockPoS, which was analyzed by CyberBit, GlobeImposter too bypasses user-mode hooks by directly invoking system calls. Given this evasion technique is being leveraged by new malware samples may indicate that this is a beginning of a trend aiming to bypass user-mode security products.
Figure 2 shows this connection attempt to these three domains:
Figure 2: Network Connection
Figure 3: Binary File Download
The domains appear to be offline at the time of writing this post.
The GlobalImposter Ransomware binary file contains the following static characteristics:
SHA256 Hash: 3a9d5976fbf41daf80f0eb9e6b7aadcece52a82fe9609984ef7f8ea166048547
File size: 239,104 bytes
Compile Time: 2016-12-11 21:50:59
After a quick examination of the downloaded executable, it appears that it is a NullSoft Scriptable Install System (NSIS). Although the use of NSIS in malware is not new, the GlobeImposter packer uses it in a pretty unique manner.
Usually, malware use NSIS to decompress their payload and load it into memory. A good example of this is the famous SmokeLoader, which first extracts a Dll file and drops it into C:\Windows\System32, and then calls LoadLibraryA to load it into memory and continue to the next stage of its execution flow.
In our case, GlobeImposter does not create any PE files that may be scanned by AVs, but it creates a binary file called “LGU” which is written to the TEMP folder. The mysterious “LGU” file contains a shellcode which will be executed by the NullSoft script in the following manner:
First, it will open the LGU file:
Secondly, it will create a page-file-backed section using NtCreateSection:
And thirdly, the script will map the section with read-write-execute memory protections to virtual memory using NtMapViewOfSection, and will read the content of the file onto the newly created section view.
As you can see in the figures above, the calls to NtCreateSection and NtMapViewOfSection are weakly obfuscated by first calling wsprintf and generating a string that contains the function name and parameters, and then passing the string to System::Call. Finally, the script will use System::Call one last time to jump to the shellcode (at offset 0xDC89 from its start). The developer chose to use NtCreateSection and NtMapViewOfSection and not VirtualAlloc probably because this way the memory region that the shellcode resides in will be Mapped and not Private, which is a little less suspicious.
The use of the NullSoft Installer for this purpose makes it harder for AVs to statically detect the executable as malicious, because there are many legitimate software that use NSIS. It also makes it harder for researchers to dynamically or statically analyze the binary since the whole script is interpreted, and the calls to the System::Call function in the script, will end up in an exported function in System.dll (a Dll used by the Installer) called “Call”. This export dynamically resolves the WinApi functions at runtime, which is similar to what many obfuscated malware do.
Figure 4: System.dll exports
Figure 5: The actual call performed inside System::Call
The shellcode that can be found in the “LGU” file, is encoded, and will only be decoded by a small code stub located at the entry point of the shellcode. During the execution of this stub, it will try to locate the beginning of the encoded shellcode, which is marked by a DWORD egg with the value 0xDEADBEEF.
Figure 6: Searching for the egg
The encoded part of the shellcode looks like this:
Figure 7: The shellcode’s header
After locating the egg, the decoding stub will brute-force a 32 bit key, that will be used later for XORing the rest of the decoded shellcode. It does this by trying values from 0x00 to 0xFFFFFFFF until it finds a value that when XORed with first decoded instruction (offset 0xC from the egg), gives the value of the first plain instruction (offset 0x8 from the egg).
Figure 8: Brute-forcing the decoding XOR key
After the key is found, 0x24CF bytes (as specified in the header at offset 0x4 from the egg) of encoded data will be XORed with key.
Once this stub finishes decoding the rest of the shellcode and jumps to it, we can examine the new code and see what it does:
Figure 9: The decoded data
This looks quite similar to the code we have seen, and as it turns out, this is exactly the same stub that was used before! The process of decoding and jumping repeats itself over and over a few times, and each time, it decodes a new part with a different key. After these iterations are over, it will start executing the real code that is responsible for unpacking GlobeImposter. The unpacking procedure works as following: It decrypts the GlobeImposter unpacked PE in-memory using AES256, creates a suspended child process, and uses process hollowing (a.k.a RunPE) on the suspended process in order to run the unpacked executable.
Figure 10: Decrypting the payload
The process hollowing is performed in a straight forward manner by the shellcode: it unmaps the image of the suspended process using NtUnmapViewOfSection, creates a new section using NtCreateSection, maps it to the other process using NtMapViewOfSection, writes to the mapped view with NtWriteVirtualMemory, sets the remote threads context and finally resumes the thread using NtResumeThread.
Any researcher will spot right away that the mentioned functions are probably used for process hollowing, but the GlobeImposter packer makes life a little harder. Some malware call undocumented function in Ntdll to bypass any poorly placed user-mode hooks done by a security product, but this packer, makes the transition to kernel by itself, thus rendering ANY user-mode hooks or breakpoints completely useless.
All the suspicious system calls (NtUnmapViewOfSection, NtCreateSection, NtMapViewOfSection, NtWriteVirtualMemory and NtResumeThread) are performed by executing the syscall / sysenter instruction directly.
Let’s take a deeper look on how the packer actually performs those system calls.
The first thing it does is to call IsWow64Process in order to determine if the machine is 32 bit or 64 bit. If the machine is 64 bit, the packer will eventually use the syscall instruction, and if it’s 32 bit, the sysenter instruction will be used. In either case, the packer needs to determine the number of the system call before it can actually call it. One way to do this is to hard-code these numbers in the code, but this approach might be inconsistent between different architectures and OS versions. Another completely different approach, is to map ntdll.dll from C:\system32\ into memory, parse its exports, and then call the desired function. This was done by numerous malware in the past. The approach that GlobeImposter takes, is to search the desired export in the already-loaded Ntdll by a custom checksum of its name, and then to take the system call’s number out of the opcode that puts it in eax. Rootkits have used similar techniques in the past for SSDT hooking.
Figure 11: ZwCreateSection in Ntdll.dll
Figure 12: The shellcode searches for the system call’s number in the opcodes of the function
The whole process of calling system calls in the shellcode looks like the following:
Figure 13: Calling NtCreateSection in the shellcode. 32 bit version on the right and 64 bit version on the left
In 64 bit machines, in order to execute the syscall instruction, the shellcode needs to make the transition between the 32 bit code segment to the 64 bit code segment. It does this by performing a far jump to 64 bit code segment. The following code snippet does just that:
Figure 14: Jumping to the 64 bit code
The retf instruction performs a far return to the address that was previously pushed onto the stack and to the segment that was pushed before it. In figure 14, we can see it pushes 0x33, which is the 64 bit code segment (0x23 is the 32 bit code segment), and then it calls the next instruction, effectively pushing its address to the top of the stack, and by adding 0x5 to that address, it now point to the instruction that is located right after retf (0xFABE + 0x5 + 0x5 = 0xFAC8). The 64 bit code after the retf, will execute the syscall instruction.
Most usermode debuggers (like OllyDbg or x64Dbg), cannot handle this transition between 32 bit code to 64 bit code, so if you single-step though this code, after the retf you will surprisingly find yourself after the syscall has already occurred, like experiencing a short blackout (this is a good anti-debugging trick as well).
However, WinDbg x64, both in usermode and in kernel debugging, can actually handle this far return and the transition between the segments, so we can use it to dynamically see what’s happening:
Figure 15: Syscall in WinDbg
Notice the value in rax, it contains the number of the NtMapViewOfSection system call (on Windows 10x64).
After unpacking GlobeImposter, it appears to be almost identical to an older variant found in early November 2017. Here is a comparison of the encryption routines, one from the old GlobeImposter and one from the new variant:
Figure 16: Comparison between the GlobeImposter variants. The old version on the left, and the new unpacked one on the right.
As show in figure 17, the files that are encrypted are then renamed in the following format: filename.doc
Figure 17: Encrypted Files
When the Ransomware finishes encrypting all the files, a message is presented to the victim within a browser as shown in figure 18:
Figure 18: Ransom Notification Message
The Ransomware also offers to decrypt one file for free to prove the decryption works. The note states the following "Before paying you can send us 1 file for free decryption."