Creating Custom TCP Bind Shell | Linux x86


Bind TCP shell require three syscalls, one for setting up socket that includes socket(), bind(), listen(), and accept() functions. The second syscall is dup2() for file descriptors, and the last syscall execve() used to spawn shell upon receiving a successful TCP connection. This post is an in depth analysis of those syscalls and/or functions as well as their corresponding assembly code. The post will then conclude by tying all the pieces together to create working shellcode.


The socket() function is responsible for creating a communication medium using file descriptors and it consist of three arguments domain, type, and protocol as shown below.

Domain argument specify the protocol family which will be used for communication, we will be dealing with IPv4 Internet protocols hence will use “AF_INET”. The second argument that we need to provide is type, type is responsible for selecting socket type “SOCK_STREAM” for TCP connections in our case. Protocol argument is used to specify what protocol can work with the socket, we only have single protocol hence will go with “0”. Now that we know what the function does let’s update it with our desired values

Let’s check socket syscall ID on Linux x86 system “EAX”

Now we need to find ID for “SOCK_STREAM”


Finally, we need to figure out what system socket call function ID is “EBX”

Now that we have all the information we need, let’s start coding!


The bind() function is used to bind an address to a socket, and it consist of three arguments sockfd, addr, and addrlen as shown below

sockfd points to the socket() to bind an address to, hence we need to save the content of “EAX” after socketcall interrupt in socket() to “ESI”. The second argument addr is basically where you assign an IP address to the socket, but wait there is more to it than just assigning an IP address, according to ip(7) manpage under address format section addr consist of three parts sin_family, sin_port, and sin_addr as shown below

Now sin_family is pretty self-explanatory so will go with “AF_INET”, which according to the first code block in socket() translates to “2”. sin_port will be “2018” and needs to be pushed in network byte order “big-endian”, why you ask? Well, here’s quote from RFC1700

sin_addr on the other hand is were we actually put in an IP host address in network byte order, and hence we want to listen on all interfaces will go with “INADDR_ANY” which translates to “0”. The last argument would be addrlen which defines the size of addr in bytes. lets update bind() function

Its time to find ID for bind() function “EBX”

Back to the terminal


listen() function allow for socket referred to by socket file descriptor to listen for incoming connections. The function have two arguments sockfd and backlog as shown below

At this point I think we all know what socketfd does, hence will use “EDX” to point to socket(). The second argument backlog is where you store the maximum length of the queue for pending connections before it stop accepting new ones, in this case will use “1”. Let’s update listen()

Let’s check listen() function ID “EBX”

Now let’s code


accept() function is used to accept incoming connections for socket specified by sockfd. The function have three arguments which have already been covered in previous sections as shown below

Now in accept() case addr and adrrlen is referring to the peer socket which we don’t care about, hence will go with “0”. Let’s update accept()

Its time to check accept() function ID “EBX”

Off to the terminal we go


dup2() syscall is used to duplicate file descriptors and by file descriptors I mean stdin, stout, and stderr, and it consist of two arguments oldfd and newfd as shown below

oldfd is basically peer socket file descriptor, hence we will store “EAX” content in “EBX” from accept(). newfd is where we specify new file descriptors. Let’s update dup2()

Let’s get dup2() syscall ID “EAX”

Coding we shall


execve() syscall basically execute a binary and/or script, and it consist of three arguments as shown below

filename is the pointer to the binary to be executed “/bin//sh” in our case, now the reason we went with “/bin//sh” instead of usual “/bin/sh” is the fact we need to push 8 bytes without effecting the executable, which we did! The second argument argv[] is an array of arguments to be passed on to the binary as strings, the first argument must contain the address of executable in question argv[0]. The last argument envp[] is an array of strings to be passed on to executable environment, we’re not going to use any hence will go with “0”. Let’s update execve()

Let’s check execve() syscall ID

Some more code!

Final Shellcode

In this section we will glue all of previous code blocks together as shown below and then produce our final working shellcode. Feel free to skip the following code and go right to the graphical representation!

Here’s graphical representation of the final code for your convenience


Its Demo Time! Let’s compile and run

Now that we know it works, let’s go ahead and generate shellcode and then create python script that takes port number as an input and add it to our shellcode

Here’s the script

Now running the script with port 2018 will output the exact same shellcode generated earlier!

Closing Thoughts

I most certainly picked up new skills writing this blog post and hope you did too! All of the above code is available on my github as shown in the link below. Feel free to contact me for questions using the comment section below or just tweet me @ihack4falafel . This post is one of many to come so stay tuned!

This blog post has been created for completing the requirements of the SecurityTube Linux
Assembly Expert certification:

Student ID:    SLAE-1115

Github Repo:

Leave a Reply

Your email address will not be published. Required fields are marked *