Process doppelgnging , a new technique of impersonating a process, was published last year at Black Hat conference . After some time, a ransomware named SynAck was discovered that adopted this process for malicious purposes. However, this technique is still pretty rare in wild. So, it was an interesting surprise to notice it in a dropper of the Osiris banking Trojan (a new version of the infamous Kronos).
The authors of this dropper were skilled, and they added several other tricks to spice the whole thing up. In this post, we will have a closer look at the loader’s implementation.
Analyzed sample 5e6764534b3a1e4d3abacc4810b6985d original sample (stage 1) 8d58c731f61afe74e9f450cc1c7987be stage 2 e8c39091cce419adee23153f30cefa5a Osiris core botOsiris is loaded in three steps:
Overview
The dropper creates a new process and injects the content inside:
Interestingly, when we look into the modules loaded in the process space of the injector, we can see an additional copy of NTDLL:
This is a well-known technique that some malware authors use in order to evade monitoring applications and hide the API calls that they used.
When we examine closely what the functions are called from that additional NTDLL, we find more interesting details. It calls several APIs related to NTFS transactions. This brings to mind the technique of process doppelgnging, which relies on this mechanism.
Loading additional NTDLLNTDLL is a special, low-level DLL. Basically, it is just a wrapper around syscalls. It does not have any dependencies from other DLLs in the system. Thanks to this, it can be loaded conveniently, without the need to fill its import table.
Other system DLLs, such as Kernel32, rely heavily on functions exported from NTDLL. This is why many user-land monitoring tools hook and intercept the functions exported by NTDLL: to watch what functions are being called and check if the process does not display any suspicious activity.
Of course malware authors know about this, so sometimes, in order to fool this mechanism, they load their own, fresh and unhooked copy of NTDLL from the disk. There are several ways to implement this. Let’s have a look how the authors of the Osiris dropper did it.
Looking at the memory mapping, we see that the NTDLL is loaded as an image, just like other DLLs. However, it was not loaded by a typical LoadLibrary function, nor even by its low-level version from NTDLL, LdrLoadDll. Instead, the authors decided to load the file as a section, using following functions:
ntdll.NtCreateFile to open the ntdll.dll file ntdll.NtCreateSection to create a section out of this file ntdll.ZwMapViewOfSection to map this section into the process address spaceThis was smart move because the DLL looks like it was loaded in a typical way, and yet, if we monitor the LdrLoadDll function, we see nothing suspicious.
Implementation of process doppelgngingIn order to make their injection more stealthy, the authors took the original implementation of process doppelgnging a step further and used only low-level APIs. So, instead of calling the convenient wrappers from Kernel32, for most of the functions they called their equivalents from NTDLL. Moreover, they used a custom copy of this DLL.
First, they created a new suspended process. This is the process into which the payload will be injected. However, in this case, authors decided to use a function from kernel32.dll: CreateProcessInternal.
Process doppelgnging then starts from creating a new transaction, within which a new file is created. The original implementation used CreateTransaction and CreateFileTransacted from Kernel32 for this purpose. But this is not the case here.
First, a function ZwCreateTransaction from a NTDLL is called. Then, instead of CreateFileTransacted , the authors open the transacted file by RtlSetCurrentTransaction along with ZwCreateFile (the created file is %TEMP%\Liebert.bmp). Then, the dropper writes the content of the new executable to the file―the second stage of the malware. Analogically, RtlSetCurrentTransaction with ZwWriteFile is used.
We can see that the buffer that is being written contains the new PE file: the second stage payload. Typically, for the process doppelgnging technique, the file is visible only within the transaction and cannot be opened by other processes, such as AV scanners.
After the file inside the transaction is created, it will be used to create a buffer in special format, called a section. The function that can do it is available only via low-level API: ZwCreateSection/NtCreateSection.
After the section is created, the file that was created is no longer needed. The transaction gets rolled back (by ZwRollbackTransaction), and the changes to the file are never saved on the disk.
Further, the created section will be used to load a PE file. After writing the payload into memory and setting the necessary patches, such as EP redirection, the process is resumed:
Second stage loader
The next layer ( 8d58c731f61afe74e9f450cc1c7987be ) is not the core yet, but the next stage of the loader. The way it loads the final payload is much simpler, yet still not trivial. The code of Osiris is unpacked piece by piece and manually loaded along with its dependencies into a newly allocated memory area within the loader process.
After this self-injection, the loader jumps into the payload’s entry point:
The interesting thing is that the entry poi