Oh man, time flies fast. Before I even knew it three months passed again. So here I am again writing a little tutorial on how to create a simple bind TCP shellcode with a password.
As I get more involved into exploit development, I constantly keep finding myself creating shellcodes instead of using public ones. I mean it kinda makes sense, I fell in love with creating shellcodes since the first time I wrote “Hello world” back in 2012-2013
Goals
So this is not a post that will explain how each instruction in x86 ASM works.
My goal is to break my shellcode into small chunks and replicate what I would do if the “backdoor” was written in C.
Use socket() to create a socket.
Use bind() to assing our port and interface.
Use listen() to listen for connections.
Use accept() to accept a connection.
Use recv() to get the password.
Use dup2() to duplicate STDIN,STDOUT,STDERR to our client socket.
Use excve() to execute ‘/bin/sh’.
sys_socketcall
As in every shellcode we will use kernel syscalls to achieve our little list.
Running man socketcall will give you any info needed for the socketcall syscall.
You will probably find your syscall table at “/usr/src/linux-headers-$(uname -r)/arch/x86/include/generated/asm/syscalls_32.h” but I would recommend using the Kernelgrok Syscall Table as it provides you with info such as the syscall number and what parameters are used by each call.
You can find more info about the “int call” used by socketcall() as the first parameter at “/usr/include/linux/net.h”.
The source code but every time I type xor it gets faster.
First zero our registers and call socket(AF_INET,SOCK_STREAM,IP_PROTO) to create a new socket.
In order to use the bind() syscall we need to replicate the sockaddr_in struct.
So let’s call bind(sockefd,struct sockaddr_in, sizeof(struct sockaddr_in)).
Now we should call listen(socketfd, 0).
Also do not forget the listen label so we can jump here later.
Here is what our stack will look like before and after the int 0x80 instruction.
Time to compare the received string to our password “w0t1sd1s”.
Of course our registers can only hold four characters, so we will just split it into two parts.
Don’t forget that our string is in little-endian format.
If someone connects to our shell and does not know the password, the shellcode will start re-listening to the same port.
That means the connection will hang until the end of time or until they terminate the connection.
So if you provide the wrong password to actually access the shell you need to re-connect and try again.
Next in our list, use dup2(STDxxx,client_sockfd) and copy STDIN,STDOUT,STDERR to our client socket.
And last but not least calling execve(“/bin/sh”,NULL,NULL).
The string “/bin/sh” is 7-bytes long, we have to push 8-bytes on the stack in order to avoid having a NULL byte in our shellcode.
We can just use ”//bin/sh” or “/bin//sh” since it does not really make a difference.
The actual code
Our shellcode in action
Compile it.
Dump our shellcode.
Our shellcode. After some manual formatting. *cough*
Result
Closing thoughts
There is definitely room for improvement, I assume my shellcode could be 20-40 bytes smaller.
But then again, my goal was just to create this shellcode without caring that much about the size.