More Advanced Shellcode
6. More Advanced Shellcode
The function we are going to use to spawn the command prompt will be ShellExecute. We could have used a much simpler function such as WinExec, but ShellExecute will allow us to show a few important concept such as dealing with string parameters and parameters order.
The source code we are going to use is the following. This simple code will spawn a new command prompt and will maximize the window. Please refer to Microsoft library page for ShellExecute to understand the purpose of each parameter.
Once we have the source code ready, we just need to compile it. If we inspect the program with Immunity Debugger we should see something like this:
This code is quite similar to the previous one. Once the main function starts, it sets the stack frame and then it pushes the arguments needed for the ShellExecuteA call. Notice that ShellExecuteA is the ANSI name of the function that will be used.
6.1 Dealing With Parameters
The biggest difference from the previous example is that this time we have more parameters to push to the stack. Moreover, we will also have to deal with strings such as cmd and open. Dealing with strings means that we have to:
Calculate their Hex value
Push the string
Push a pointer to the string into the stack
First, as you can see, the parameters are pushed in the reverse order. In the C++ source code, the first parameter is 0, while in the disassembled code, the instruction that pushes this parameter to the stack is the last one.
6.1.1 Dealing with Strings
The first thing to do is to convert the strings (cmd and open) that we will push into the stack.
In the compiled version of the program, these strings are taken from the .data section. As you can imagine, this is something that we cannot do while sending our shellcode (since the .data section will contain something different).
Therefore, we will have to push the strings to the stack and then pass a pointer to the string to the ShellExecutionA function (we cannot pass the string itself).
Things to remember when pushing the strings into the stack:
They must be exactly 4 byte aligned
They must be pushed in the reverse order
Strings must be terminated with \x00 Otherwise the function parameter will load all the data in the stack. String terminators introduce a problem with the NUll-free shellcode. Therefore if, the shellcode must run against string functions (such as strcpy), we will have to edit the shellcode and make it NULL-free. We will se this later on.
General Steps:
Split the strings into groups of 4 characters Our string will be something like following:
Reverse the order
Convert to ASCII
Add PUSH bytecode
Terminate the String
Save the String Pointer to Registers
Tips:
Opcode reference or Metasm
Pushing byte opcode :
\x6A
Pushing word or dword opcode :
\x68
6.1.2 Example
Example:
We want to run :
ShellExecute(0,"open,"cmd", NULL,0,SW_MAXIMIZE);
Pushing strings into registers
Since the ShellExecuteA function arguments require a pointer to these strings (and not the string itself), we will have tot save a pointer to each string using a register.
Therefore, after pushing the strings to the stack, we will save the current stack position into a register (such as EBX or ECX). Hence, it will point to the string itself
Pushing The Parameters Other than string we still need to pass four other parameters to the function: three of them are 0 while one is 3.
We have to push them in reverse order, in order to make the right stack. We will have to push 3 first, two zeros, our strings (cmd and open), and at the end, another zero.
We have many different ways t o push the integer value 3 to the stack. We can directly execute a PUSH 3 instruction, but we can also move the value into a register and then push the register itself.
We could also zero out a register and then the register 3 times, before pushing it to the stack. In our case, we will simply PUSH it to stack with the following instruction:
Now we have to push two zeros into the stack. To do this, we will zero out the EAX register, and then we will push it two times. The code will be the following:
Now its time to push the strings.
Then we push the first parameter : 0
All the parameters have been pushed in the correct order. We need to find and push the address of the ShellExecuteA function and then call it.
In order to find the address, you can use arwin
We need to move this address to a register and then call it
Putting it all together
ShellExecute(0,"open,"cmd", NULL,0,SW_MAXIMIZE);
Testing We can test our shellcode by using the small C++ code provided before.
Notice that since the compiler does not automatically load the Shell32.dll library in the program, we have to force the program to load it with the instruction LoadLibraryA("Shell32.dll")
6.2 NULL-free shellcode
In the previous chapter, we created a shellcode that spawned a command prompt, but as you already know this isn't a NULL-free shellcode.
Therefore, if we try to use it against BOF vulnerability that uses a string function (such as strcpy), it will fail. This happens because when strcpy encounters the \x00 byte, it stops copying data to the stack.
Therefore, we have to find a way to make our shellcode NULL-free.
There are 2 main techniques that we can use:
We can manually edit the shellcode
We can encode and decode the shellcode
6.2.1 Manual Editing
Let's see how we can edit our shellcode in order to avoid the first string terminator (\x68\x63\x6d\x64**\x00**)
Solution Substract (or add) a specific value in order to remove 00.
Example For example let's say we substract 11111111 from 00646d63. We will obtain EF5335C52, which does not contain the string terminator.
Notice that instead of 11111111 we can use any value that does not contain 00 and that does not give a resulting value containing 00
Steps
Move EF535C52 into a register
Adds back 11111111 to the register (in order to obtain 00646d63)
Push the value of the register on the stack
Result In the previous version of the shellcode, we had the following bytecode:
The new bytecode (NULL-free) will be something like the following:
6.2.2 Encoder Tools
You can use msfvenom as an encoder tools to do this also
Last updated