DLL proxying & sideloading
While studying DLL injection techniques, specifically DLL sideloading, I found this article by Palo Alto Network’s Unit 42 When Pentest Tools Go Brutal: Red-Teaming Tool Being Abused by Malicious Actors. The article describes how the dropper was using using a technique known as DLL search order hijacking to sideload a malicious crafted DLL that will inject a Brute Ratel agent (badger) into a remote process’s memory space, in this case RuntimeBroker.exe. So I decided to replicate such behaviour. I don’t have access to Brute Ratel so I’ll use Bishop Fox’s Sliver for this PoC. The full code can be found in my github repo.
Execution Flow
Since all the details about the dropper are discribed in detail in the mentioned article I won’t go into the details of the dropper, just give a brief explanation of the execution flow.
This dropper was delivered as an ISO image that contained the next files:
- A Windows shortcut (LNK) file
- An original and legitimate copy of OneDriveUpdate.exe (hidden)
- An original copy of version.dll (C:\Windows\System32\version.dll) renamed to vresion.dll (hidden)
- A file named OneDrive.Update which contains encrypted shellcode (hidden)
- A malicious crafted DLL named version.dll, it will proxy all the calls to the original DLL and inject the shellcode into RuntimeBroker.exe’s memory (hidden)
The execution flow looks like:
Image taken from Unit42’s article |
To replicate this dropper I already have 2 of the needed files, the original copy of OneDriveUpdate.exe and version.dll which are included in windows 10, the rest of the files need to be created. So let’s begin with the coding fun!
OneDrive.Update
This file is the easiest one to create. I’ll first generate a beacon in shellcode format:
I’m using mTLS protocol (mutual TLS) because it’ll encrypt all communications. The shellcode is saved to SATISFIED_FISHING.bin (nice name choosen by Sliver itself). Sliver is written in golang and it will generate large size PE files, this shellcode has a size of almost 13 MB, so if you have space limit issues you can use a stager payload instead.
Then I’ll create a python script that will XOR the content of the .bin file with a key, in this case I’ll use the same key mentioned in the article (“jikoewarfkmzsdlhfnuiwaejrpaw”), and save the output to a file called OneDrive.Update:
From here and on I’ll be working in a windows 10 machine to create the rest of the files
Version.dll
This file will require more work but is a straight forward procedure. So we know we have to proxy all the calls to the original version.dll from our malicious DLL. First we need to know which functions OneDriveUpdate.exe imports from the DLL. We can use MSVC’s dumpbin.exe for this purpose:
As you can see only 3 functions are imported from version.dll, so it will be pretty easy to proxy those calls. Take in mind that during runtime more functions might be imported but we can start from here, if the update application crashes then we’ll need to find out if any import is missing, you can also proxy all the functions to avoid any errors but in this case I’ll proxy these 3 functions only. If you want to check the imports table in a linux machine you can choose among different tools to do the job, for example you can use radare2’s rabin2:
With this information I can add some comments to the malicious DLL source code, this comments will instruct the linker to forward any call to these functions to vresion.dll, which is the original version.dll:
In red we can see the re-named version.dll and in green the ordinal number of the function represented as decimal value. Take a look at the result of dumpbin and you can see that the ordinal number for VerQueryValueW is 10, in hexadecimal format, in decimal it is equal to 16. For this to work vresion.dll will be loaded by the malicious version.dll as a dependency.
Now I’ll follow the steps mentioned in the article to achieve remote injection:
We can see they are using some native API calls, these are the kind of functions that will pass execution to kernel so they are not intended to be used directly by users but from their top level API call equivalent. For example if you call Sleep() in your code there will be a sequence of calls to pass execution to kernel and just before passing it NtDelayExecution() will be called.
To clarify this I’ll make a brief parenthesis here. I’ll create a simple code that will sleep for n milliseconds:
As you can see I’m calling the Sleep function and passing a desired interval of milliseconds as an argument. After compiling and running the code I attach a debugger and set a breakpoint in the Sleep() function which can be found in kernel32.dll:
Resuming execution, the breakpoint is hit and as you can see there is only a jump instruction:
Following the jump it lands in kernelbase.dll:
Folllowing the code we can see that NtDelayExecution will be called:
Stepping into the call we can see the syscall stub in ntdll.dll, this is the very last step before passig execution from user land to kernel:
This is an example of how calling WIN32 API functions ended up in a syscall stub to pass execution to kernel. Take in mind that not all the WIN32 API calls will end up in a syscall.
So going back to the code in the malicious DLL I’ll start by creating a function named Load() that will do all the job, this function will be called once the DLL is attached to a process:
The first thing to do as mentioned in the article is to find the PID of RuntimeBorker.exe, this is a service that will run by default in windows. To find the PID I’ll use a technique learned in Sektor 7’s course. First a snapshot that includes all the running processes in the system will be taken, then iterate over all the processes until RuntimeBroker.exe is found and finally return it’s PID:
Next, the content of OneDrive.Update will be read and stored in a buffer:
Now I also want to resolve all the native API calls dynamically, this can help to prevent these calls from being part of the imports table of the malicious DLL. So I’ll need to manually define all the functions and use GetProcAddress to get the addresses of all of them. You can also use direct syscalls to create a more stealthy DLL with techniques such as SysWhispers or Hell’s Gate to mention some of them.
The next step is to use NtOpenProcess to get a handle of the remote process. I’m using this native API call as an example to illustrate the above mentioned dynamic resolve. Here you can see that the function is defined, get it’s address using GetProcAddres and then call it the same way as the original function:
Now I’ll create a section that will be used by the local and remote processes. This technique doesn’t write the payload in the remote process memory address but gives access to it. The payload is only stored in the local process memory. To be able to write the payload to the recently created section I’ll need to map a view of the section in the local process:
Then I’ll derypt and write the payload into the view, this for loop will do both:
In order for the remote process to have access to the payload I’ll need to create a map of view of the section in such process:
In the article it is mentioned that NtDelayExecution is called to sleep for 4.27 seconds, I don’t really know why that exact amount of time but I’ll use 3 seconds only. Then create a thread in the remote process with a start address pointing to the remote view, then sleep again:
The next part is not mentioned in the article but as malware developers we know we have to do some cleaning, right? So I decided to do it. I’ll close all the opened handles and unmap the local and remote views. This will also help to avoid leaving suspicious memory sections:
LNK file
I know this file can be created easily but as I’m practicing coding I decided to write an executable that will create the LNK file. I did a research on internet to get some code examples and found out this really cool example by x86Matthew where he embeded an executable inside a LNK file, you can check his project here.
So I grabbed part of his code, did some modifications and made a program that creates the LNK file. This has some advantages as you can add a custom description which will be showed once you hover over the file and choose a custom icon. The description is hardcoded in the source code to properly add null bytes so it looks more real while hovering over the LNK file.
ISO
To continue coding for fun I also created an application that will pack the content of a folder into an ISO file. You can set the hidden attribute to each file and it will be kept in the ISO:
According to the article the name of the ISO file is Rosha-Bandra_CV but to keep coherence with OneDrive update I renamed the ISO to OneDrive.
Let’s test it out
To make a kind of realistic test I’ll move the ISO file to kali and serve it via apache:
Once the ISO has been downloaded I can mount it and double clicking in the LNK file will launch OneDriveUpdate.exe and sideload my malicious DLL (keep in mind that all the files have the hidden attribute set, except for the LNK file):
Going back to Sliver we can see that a connection is received:
As you can see everything worked as expected and the beacon was loaded in memory without Defender getting in the way.