In the previous post, we backdoored a PE file by adding a new section (code cave) to the executable file. However, we encountered the following problems:
- Putty won’t run until the shell has made a connection to our attacking machine.
- We had a bad detection rate of 31/66.
In this post, we’ll try to fixe those issues. As we saw before, simply adding a section header to an executable causes the detection rate to go up. To avoid this, we’ll use a code cave which already exists in the PE file.
To resolve the issue with Putty’s execution, what we’re going to do is to trigger the shell upon user interaction. Specifically, the shell will be triggered once the victim tries to connect to an SSH server, by providing a valid IP address/host and clicking the Open button.
Again, we’ll use the same version of Putty which is v0.66. This version can be downloaded here. Before we start, let’s see the detection rate of the newly download Putty executable. 1/67 is pretty good.
Finding the User Interaction Program Flow
To determine the function responsible for the Open button, let’s observe what happens when we connect to a valid SSH server. For this post, we’ll be using scanme.nmap.org.
0x0041CA9B is the start address that’s responsible for the Open button, this is where we will hijack the program’s flow. Before making any modifications to this address, let’s first save the first 3 instructions as a reference later for our cleanup code.
0041CA9B |. 68 7C7C4600 PUSH putty.00467C7C ; ASCII "login as: " 0041CAA0 |. 8941 04 MOV DWORD PTR DS:[ECX+4],EAX 0041CAA3 |. E8 3FF6FEFF CALL putty.0040C0E7
Code Cave Mining
To make the code cave mining easier, let’s first generate a shell so we could identify its size, and use it as a reference for the code cave that we’re looking for. As seen below, the generated shellcode has a size of 324 bytes.
Instead of manually searching for code caves, let’s use Cave Miner which is available here. Since we already know the size of the shellcode, let’s search for a code cave that has a minimum size of 324 bytes. As seen, two code caves were found. The first one is only 331 bytes (0x0000014B), so we’ll use the second cave which has a size of 559 bytes (0x000002FF). This code cave starts at
Looking back the image above, we can see that our chosen code cave does not have executable privileges. To fix this, let’s use LordPE and edit the
.data section, since this is where the code cave resides.
Hijacking the Program’s Execution Flow
After some trial and error, I discovered that instructions starting at
0x0047AA10 does not change even when Putty is running. To reflect the new address of our code cave, let’s modify the jump instruction, from the Open button function, and save the new file as putty2.exe.
Just like in the previous post, let’s follow this structure inside our code cave:
PUSHAD # Save the registers PUSHFD # Save the flags ...shellcode... STACK ALIGNMENT # Restore the stack to its previous value POPFD # Restore the flags POPAD # Restore the registers ...restore execution flow...
Now, let’s make the necessary changes inside our code cave by saving the registers and flags using
PUSHAD; PUSHFD instructions. Then binary paste our shellcode and change the instruction
DEC ESI into
NOP just like before. Let’s save the modified file as putty3.exe.
The next instruction that needs to be added in our code cave is our stack alignment code. Let’s first compute the difference between the old and new values of ESP:
0x0012AC94 - 0x0012AA98 = 0x1FC. To align the stack, let’s use the
ADD ESP, 1FC instruction. After this, let’s restore the previous values of our registers and flags using
POPFD; POPAD. Lastly, save the changes as putty4.exe.
Improving the Detection Rate by Using a Custom Encoder
Let’s try to have a better detection rate by encoding our shellcode using a custom encoder. The reason we’re encoding our shellcode is to bypass the static/signature analysis mechanisms of some AV products.
During the encoding/decoding process, we might change some registers and/or flags which might cause some issues with how the program works. To be safe, we’ll move down our shellcode and insert our encoder/decoder after the
PUSHAD; PUSHFD instructions. This is the updated structure of the code cave looks like:
PUSHAD # Save the registers PUSHFD # Save the flags ...encoder/decoder... ...shellcode... STACK ALIGNMENT # Restore the stack to its previous value POPFD # Restore the flags POPAD # Restore the registers ...restore execution flow...
For our encoder/decoder, we’ll use an XOR algorithm due to its simplicity and symmetric property. Once we XOR (encrypt) a certain value with a key, we can simply obtain its original value (decrypt) by XORing the encrypted value with the same key. The following image shows how an XOR encoder/decoder works.
Credit goes to the original creator of this image.
This what our XOR encoder/decoder code looks like.
MOV EBX, 0x0047AA26 ; Place shellcode's start address in EBX XOR BYTE PTR DS:[EBX], 0E ; XOR the contents of EBX with using “0E” as key INC EBX ; Increase EBX CMP EBX, 0x0047AB69 ; Did we reach the shellcode's last address? JLE SHORT 0x0047AA17 ; If not, jump back to our XOR command JMP 0x0047AA26 ; If yes, we’re done and let’s jump to the start of our shellcode
Let’s run our encoder and place a breakpoint before the jump to our shellcode gets executed. When the breakpoint is hit, we see that the instructions starting at
0x0047AA26 (our shellcode’s start address) changed. This means that our shellcode was already encoded. Let’s save the changes made to the shellcode and name it as putty6.exe.
Let’s open the updated PE file again and set a breakpoint right before the jump to our shellcode gets executed. As seen, the instructions starting at
0x0047AA26 changes to its original values. This means our decoder worked. To get the new value of ESP, let’s set a breakpoint at
0x0047AB6A, which is the address right after the last instruction of our shellcode. As seen,
ESP = 0012AA98.