In this post, I’m going to discuss how to inject a shellcode in an executable file. I’m also going to discuss the problem that I encountered along the way, and the solutions that I came up with.
The executable file that I’m going to backdoor is Putty v0.66 which can be downloaded here.
Before anything else, let’s first discuss what a code cave is. According to Wikipedia:
A code cave is a series of null bytes in a process’s memory. The code cave inside a process’s memory is often a reference to a section of the code’s script functions that have capacity for the injection of custom instructions. For example, if a script’s memory allows for 5 bytes and only 3 bytes are used, then the remaining 2 bytes can be used to add external code to the script.
If you want to learn more about code cave, this article provides a good insight about it. I highly recommend that you read it.
Program’s Execution Flow
The following diagram shows the execution flow of the executable file, before and after the modification. As you can see, we will create a code cave by inserting a new section header to the executable file, and redirect the program’s execution flow to it. Once the shellcode in our code cave is executed, we will jump back to the original code that was meant to be executed by the program, and continue the execution as if nothing changes.
Adding a New Section Header (Code Cave)
Before adding a new section header to the exe file, let’s first upload it to Virustotal. The file was detected as clean, with a detection rate of 0/68.
Now, let’s add a new section to the exe file. To do this, we’re going to use LordPE which can be downloaded here. Once downloaded, open LordPE, click the PE Editor button, and then select our exe file (putty.exe). To view the current section headers of the file, click the Sections button. To add a new section header, righ-click anywhere in the available sections then select add section header….
We’re going to rename the new section to .meelo, and set the value of VirtualSize and RawSize to 00001000 (Note: This value is in hex format. So the actual size of this section is 4096 bytes.). Before saving the changes that we’ve made, let’s first check the flags by clicking the … button. Make sure that this section is writeable and executable.
Once done, let’s save the changes that we’ve made to the file. If we open up the modified exe file, an error like this shows up. Why? That’s because we created a section header to the exe file which is empty.
To fix the error, let’s open the modified exe file using a Hex Editor called XVI32, which is available here, and then add 1000h bytes of data to the binary by going to Edit > Insert String….
Hijacking the Program’s Execution Flow
Before modifying the program’s flow, let’s first get the first 3 instructions that the program will execute upon opening. As seen below, the program’s entry point is at
0x00454eB0 with an instruction of
Let’s note the first 3 instructions of the program.
00454EB0 > $ 6A 60 PUSH 60 00454EB2 . 68 707B4700 PUSH putty.00477B70 00454EB7 . E8 08210000 CALL putty.00456FC4
To be able to jump to our code cave once putty is executed, we need to change the instruction at the program’s entry point. This could be done by double-clicking at the current instruction and change it to
If you’ve observed, the
PUSH putty.00477b70 instruction was overwritten with 2 NOPs. This is because of the overflow that occurred when changing the original instruction. Going back to the image above, the opcode of
PUSH 60 is only 2 bytes. While the opcode for
JMP [code_cave_address] is 5 bytes. We’ve overwritten a 2-byte space with 5-bytes instructions, so an overflow occurred. The 2 NOPs acted as a filler because the option Fill with NOP’s is ticked. To save the changes, select the instructions that were modified, right-click, and then select Copy to executable > Selection.
Let’s step into (F7) this instruction to jump to our code cave. In this code cave, we’re going to follow this structure.
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...
Using the structure above, we need to save first the registers and the flags using
PUSHFD. For us to align the stack later, we need first to get the value of ESP right at the beginning of our shellcode. In this case,
ESP = 0x0012FFA0.
In our shellcode, we have to change one of its instructions. First, let’s look for the following sequence of instuctions
DEC ESI; PUSH ESI; INC ESI. Once located, we have to change
DEC ESI into a
NOP instruction. We have to do this so that the value of the
dwMilliseconds parameter, from the
WaitForSingleObject function, is set to zero. If we’re not going to do this, we’ll see that putty won’t run until we exited from our shell. Try it yourself!
Let’s save the changes we’ve made above as putty2.exe. The next thing we need to do is to get the new value of ESP after the shellcode has been executed. To do this, we’ll set a breakpoint at the last instruction of the shellcode. On our attacking machine, let’s set up a listener using
nc -lvp 4444. When we run the program, we observe that the debugger hangs up.
The next step is to continue the execution so that the next instruction to be executed (which will align our stack) would be the instruction right after
CALL EBP. However, doing so causes the debugger to hang up again.
To resolve the issue with ntdll.KiFastSystemCallRet, I came up with two solutions. If you discovered other solutions, please share it with me.
SOLUTION #1: (NOPing the Last Instruction)
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 value of ESP:
0x0012FFA0 - 0x0012FD9C = 0x204. To restore the stack to its previous value, let’s use the
ADD ESP, 204 instruction. After this, let’s restore the previous values of our registers and flags using
Right after these instructions would be our clean up code. Since we hijacked putty’s execution flow to redirect to our code cave, we need to restore its original execution flow. If you remember, we overwrote the instruction at the program’s entry point - the
PUSH 60 instruction. Again, due to the overflow, the
PUSH putty.00477B70 instruction was also changed into 2 NOPs. To restore the original instructions, let’s use
PUSH 60; PUSH 00477B70. After that, let’s redirect the execution back to the
CALL putty.00456FC4 instruction which was located at
0x00454EB7 (Remember, this is the 3rd instruction after the program’s entry point.). This redirection can be done using the
JMP 00454EB7 instruction.
SOLUTION #2: (SEH as ExitFunc)
Let’s make the same changes to our shellcode (change
DEC ESI to
NOP), add the instructions to restore the flags and registers, align the stack, and add our clean up code. Let’s save the modified file as putty-seh.exe.
In this post, we learned how to add a code cave to an executable file and used that code cave to inject a malicious code. We also learned how to redirect a program’s execution flow into our code cave and then back to the original execution flow. Doing so will avoid suspicion that something is wrong with the program.
However, if you keep an detailed observation on our backdoored program, you’ll see that putty won’t open up until it has made a connection to our attacking machine. This is not good especially if we forgot to set up our listener. In the next post, we’ll try to resolve this issue.