Attacks
GTER exploitation reusing socket stack: Every byte counts

VP of Hacking
Updated
Jun 12, 2020
15 min
This is the third article on the series of exploiting Vulnserver
, a VbD
(Vulnerable-by-Design) application in which you can practice Windows exploit development.
In our previous post, we successfully exploited the GTER
command using a technique called Egghunting
or Egghunter
.
Using an egghunter was required because we had a reduced buffer to fit a shellcode, as generated by msfvenom
and other tools.
In this post, we will use a manually carved shellcode that will harness instructions that are already loaded on Vulnserver
, allowing us to reduce the final length of our payload.
Reverse shellcode X-ray
A reverse shellcode is basically a series of Windows API
function calls arranged in a delicate order to make a victim machine connect back to an attacker machine issuing a Windows shell, which is commonly an instance of cmd.exe
.
The order of execution of a fully crafted shellcode is the following:
Call
WSAStartup()
to load the neededWinSock
DLLs. Use a call toLoadLibraryA
underneath.Call
socket()
orWSASocketA()
to bind a new socket handle.Call
connect()
orWSAConnect()
to establish a connection to the attacker machine.Call
CreateProcessA()
, which callscmd.exe
and where theSTDIN
,STDOUT
, andSTDERR
are redirected to the previously generated socket handle.
However, if we are exploiting a TCP/IP
server like Vulnserver
, chances are that the 'WinSock' DLL library is already loaded and initialized. That means that we can spare WinSock
rutines initialization from our shellcode that can save us a great amount of bytes. Every byte counts.
As a reference, let’s create a reverse shellcode using msfvenom
:
msfvenom.
This shellcode is 351 bytes long. And, if you remember on our previous post, we only had around 144 bytes to play.
The whole idea of reusing instructions is to minimize the resultant shellcode.
To do that, we need to know what functions need to be called, with what parameters, and translate that into Assembler language to then convert it into our shellcode, keeping in mind to avoid the null bytes that would lead to making our shellcode unusable. It sounds harder than it really is.
With that in mind, let’s look at the signatures of the needed functions:
WSASocketA()
WSASocketA() signature.
If we are going to write this in Assembler, we must remember that in the x86 architecture the functions are called in a very specific way:
The parameters are pushed to the stack on reverse order.
We call the required function.
That function will store the returned value on
EAX
.
For example, the structure for calling WSASocketA
function is as follows:
Push
dwFlags
parameter to the stack.Push
g
parameter to the stack.Push
lpProtocolInfo
parameter to the stack.Push
protocol
parameter to the stack.Push
type
parameter to the stack.Push
af
parameter to the stack.Call
WSASocketA()
.Retrieve the return value of
WSASocketA()
fromEAX
which is the resulting socket handle.
We also need to know the exact address of the WSASocketA()
on the system. Normally, those function addresses won’t change much on a specific version of Windows but will likely change over different updates, so keep that in mind when creating custom shellcodes.
For retrieving the addresses of functions on the current OS
, you can use the arwin tool:
arwin finding WSASocketA().
Ok, with all the required information, we can proceed to write some Assembler. We need to get a socket handle that can be used by a TCP
connection. With that in mind, we can write the call to WSASocketA()
:
WSASocketA() in ASM.
Nice, we stored the socket handle in the ESI register that we will need in the forthcoming functions.
connect()
The connect()
call will create the connection back to the attacker using the socket handle generated by WSASocketA
that we stored in ESI:
connect() signature.
The sockaddr
parameter is in turn:
Get the address of connect()
:
arwin finding connect().
Now that we know the structure of the connect()
function call and the address of the function, we can write it in Assembler:
connect() in Assembler.
Note that the attacker IP
address parameter contains a null byte, which will stop the injection of the payload. To overcome that, we can add a static value to that address, subtract it again, and push the result. This will be the final connect()
payload:
connect() in Assembler.
CreateProcessA()
Now comes the final function CreateProcessA()
, which is responsible for creating an instance of the cmd.exe
command. We also need to point the STDIN
, STDOUT
and STDERR
descriptors to our socket handle to make the resultant shell interactive for us.
CreateProcessA() signature.
We need to fill the _STARTUPINFOA
structure. Luckily for us, most of the parameters are NULL:
And the _PROCESS_INFORMATION
is even easier as all the fields can be NULL:
Get the address of CreateProcessA()
:
arwin finding CreateProcessA().
In Assembler, the call to CreateProcessA()
will look like this:
CreateProcessA() in Assembler.
Putting it all together
Our final shellcode will be this:
shellcode.asm.
We can compile this using nasm
:
nasm compilation.
And obtain the resulting shellcode with:
As you can see, the resulting shellcode is only 126 bytes long and will nicely fit on our buffer without the need to use egghunters.
Update our exploit
Now that we have our manually created shellcode, we can update our previous exploit.
We will remove the egghunter and the previous shellcode and will include our custom shellcode. Let’s see how it looks now:
exploit-socketreuse.py.
It looks simpler! Now, run it to see what happens:

Uhmmm, we got the reverse connection but no shell!
Let’s see what is going on:

As we can see, several things have happened:
Our buffer was correctly delivered.
The
JMP ESP
instruction was successfully triggered.The jump backward occurred.
And we landed at the start of our custom shellcode.
However, if you look carefully at this image:

We can see that the ESP
register is only 24 bytes below the end of our custom shellcode. That means that with every PUSH
performed on our custom shellcode, that pointer will get closer to it and start overwriting it. That’s not good news.
This graph illustrates the issue:

As the execution flows towards a higher memory address, the stack grows backward and will eventually overwrite our shellcode.
However, if you look at the image again, you can see that the EAX
register points to the GTER /.:/
string, which is above our shellcode.
All that’s left to do is align the stack to point to that location, and it’s done easily with two instructions:
Align stack.
The first instruction will push the current value of EAX
to the stack, and the second will pop back that value to the ESP
register, moving the stack pointer above our shellcode, protecting it from being overwritten.
We can use nasm_shell.rb
from Metasploit to get the opcodes of those instructions:
nasm_shell.
Ok, now we can add those instructions to our exploit and see what happens:
exploit-socketreuse.py.
And execute the exploit again:

Whooo! We got our shell again!
You can download the final exploit here
Conclusion
This time I wanted to show that there are always ways to overcome harsh exploitation environments, just by trying harder.
References
Get started with Fluid Attacks' PTaaS right now
Other posts