Last updated on September 18, 2020
I also want to warn that this is an advanced technique and you need to have basic knowledge of assembler and reversing. If you don’t have them, I recommend the Pentester Academy courses “x86 Assembly Language and Shellcoding on Linux” and “Reverse Engineering Win32 Applications“.
In this post I’m going to show you how to turn an exe into a Trojan, modifying its code to run a remote terminal. To do this we will start with the simplest way to do it, but also the most detectable by antivirus: the “Code Cave” method.
The process I’m going to follow is as follows: first, I’ll take a benign executable program. Then I will increase the space occupied by the program to create an empty “code cave”. Then, I will insert code in assembler that executes a reverse shell in this new section. Finally, I will make the program code go through this new section when it runs, and then continue its normal flow. That way I get a reverse shell without the user noticing anything weird.
Creatinh the code cave
Before you start, first disable your antivirus. I recommend that you create a virtual machine as a working environment with a windows 7, for example, or with some version of windows 10 something old, since from the update 1809 windows gives problems when creating the file.
The first thing is to choose a file on which we will make the modification. This method only works in 32 bits files. You also have to make sure that the files do not have DEP (Data Execution Prevention) or ASLR (Address Space Layout Randomisation) protection. This is because they are security measures that prevent the application from executing instructions in areas of the memory that are not allowed and that introduce randomness in the memory addresses. We can check if a program has these security measures with the PowerShell PESecurity tool.
For this example I am going to use the executable of the tool 7zip, 7zfm.exe, downloaded from the official website.
Open the exe with an executable editor. In my case I have used LordPe. Click on PE Editor and select the executable.
Click on Sections. Right click, “Add Section Header” Give it a name, for example .lethani (remember what name you give it, you’ll need to know later) and add a cave of code enough to fit the shell. With 1000 bytes it will be worth it, we indicate it in “Virtual Size” and in “Raw Size”.
If we try it now, the program does not open, since we are telling it that it has 1000 bytes more than it actually has. We have increased its size by indicating it in the header, but now we have to increase its size really. For this I have used the HxD. Open the file, do Ctrl + End to go to the end of it, and there click on Edit -> insert bytes. We have to insert 1000 bytes, we insert them with value 00.
Inserting the Reverse Shell
If you try to open the executable, it will open smoothly and follow the normal flow of the program. Now let’s use this new space we created to insert the payload. For this you can use the Immunity Debugger tool. From this program, open the executable 7zip.exe. You have to look for the cave of code that you have created. Press m to open the memory map, and look at the memory position of the code cave you have created, in this case is called .lethani.
Write the memory address of this section in a txt. In my case it is 0047E000. Now press the Run program button (►), and it will take you to the first line of code of the program. Copy in a txt the first instructions of the program, because we are going to modify them and later you will need to know what they were.
004538D8 >/$ 55 PUSH EBP
004538D9 |. 8BEC MOV EBP,ESP
004538DB |. 6A FF PUSH -1
004538DD |. 68 280D4600 PUSH 7zFM.00460D28
004538E2 |. 68 D2384500 PUSH <JMP.&MSVCRT._except_handler3> ; SE handler installation
004538E7 |. 64:A1 00000000 MOV EAX,DWORD PTR FS:
The first instruction is the first one that will execute the program, that’s why we’re going to modify it. Instead of doing an push ebp, I’m going to do a jump (jmp) to the code address of our code cave. To do this, place yourself on the instruction and press space. Write JMP 0047E000, and the first thing the program will do is jump to the code cave. However, by inserting this instruction, it is possible that the instructions below have also been modified:
If we look closely, we see that the JMP instruction has not only modified the PUSH EBP instruction, but has also removed the MOV EBP, ESP and PUSH -1 instructions. This is because the jump instruction takes up more space and overwrites them. We will fix this later. Now let’s go to our code cave. But first, save what you have done by selecting the JMP instruction and right-clicking, copy to executable, and a window will open where you will have to select the executable.
To jump to the code cave, press CTRL + G and go to the memory address 0047E000. The first thing to do in the code cave is to save the value of the records. This is achieved by adding the PUSHAD and PUSHFD instructions. To write an instruction you go to the desired memory address and press space.
It’s time to generate the shell. For this I will use msfvenom:
This is a payload for windows encoded in hexadecimal that when executed will launch a reverse shell to the attacker ip (in this case 192.168.1.156) to port 1234. Copy it to the clipboard. To paste it in Immunity Debugger, we have to select a good set of instructions from our code cave, under the two pushes we have done, right click and press binary -> binary paste.
In the end we will have something like this:
However, this payload is not valid as is. There are two problems we need to solve. First, the last CALL EBP causes the program to end after executing the reverse shell. We want the program to continue, so that the user doesn’t notice. Therefore, we remove CALL EBP and put in its place a NOP, the assembly instruction that does nothing.
Secondly, the payload of the shell at a given time takes a parameter, and waits that time in milliseconds before starting another thread in the process. But the parameter it is receiving is -1, which is equivalent to waiting infinite seconds. To fix this, we put two NOPs in the DEC ESI and PUSH ESI instructions:
Now we have to align the stack, that is, see what values it has before it executes the malicious code and put them afterwards, so that everything remains intact. For it we have to debug the program putting breakpoints to see that value has the ESP before and after the shell, and to do the subtraction to know how much has varied the stack and to return to leave it as it was. First, put your attacking machine to listen with the command nc -lvnp 1234, so that the shell can connect and the program does not break. Then, go to the first and last shell instruction (“CLD” and the “NOP” we put, respectively) and put breakpoints with F2. Ignore the warning that appears. Click on play and take the ESP value at that moment (you can find it on the right).
Then we hit play again and, if we’ve created everything right, we’ll have created a shell. Make an exit in the shell, and the program will stop at the next breakpoint. We take the value of the ESP at the end.
In my case the initial value is 0019FF60 and the final value 0019FD60. The difference of the subtraction is 200 (we can calculate it with any hexadecimal calculator) Therefore, we add just below the NOP the instruction “ADD ESP, 200“.
The next thing is to leave the values of the register as they were, with “POPFD” and “POPAD”.
Then we add the instructions that were broken when pasting the JMP, which in my case are “PUSH EBP”, “MOV EBP, ESP” and “PUSH -1”. We write them down in a txt in the first steps.
And finally, we have to make a jump to the main code of the program, to the following address after the JMP, which in my case is the address 004538DD.
This is how the final part of the code cave would look like:
There you go. We select again all the code to save the changes, right click, copy to executable, right click again, save file.
If we run the exe, it will open a reverse shell on the attacking machine.
However, this method is very provocative, and is detected by many antivirus programs. It is rare that there is a jump to another part of the code at the beginning of the program (if in a real program you need to jump from the beginning to another part of the code, why not start directly on that other side?), and having added a new section to the program is also very appealing for antivirus.
These problems will be solved in the next post, that you can see here.