The goals for assignment #5 were
- Take up at least 3 shellcode samples created using Msfpayload for linux/x86
- Use GDB/Ndisasm/Libemu to dissect the functionality of the shellcode
- Present your analysis
For this assignment, I decided to look at the shellcodes for the following metasploit payloads:
- linux/x86/adduser – adds a user to the system
- linux/x86/exec – use this to launch an nmap scan with a lot of command line arguments
- linux/x86/shell_find_port – launches a shell that finds a port to connect/bind to
To begin my analysis, I looked at what the default configuration was for the payload.
Based on the options, it looked like the shellcode would add the user “metasploit” with the password “metasploit”, and set the user’s shell to be “/bin/sh.” For this analysis, I used the default options to generate the shellcode with the following command:
msfvenom –payload linux/x86/adduser -f raw
Analysis in GDB
The shellcode was analyzed using GDB. A breakpoint was set at the first “int 0x80” instruction, which is the interrupt to make a syscall, and the registers were analyzed.
The assembly code leading up to that syscall is as follows:
mov ebx, ecx
The value in the EAX register determines what syscall is being made. At the time of the “int 0x80” instruction, EAX is set to 0x46. This will make the syscall “setuid”, which sets the user identity that will be used for the new user.
setuid accepts one argument, which is the value for the user identity. When the syscall is made, this value will be stored in the EBX register. In the above shellcode, the ECX register is XOR’d with itself, resulting in the ECX register containing the value 0. The value of ECX is then moved into the EBX register, making EBX 0. So, when the syscall is made, the value of the user identity will be “0”, or the user identity of root.
The next “int 0x80” instruction is when EAX is set to 0x5, or for the syscall “sys_open”, which opens a file. The assembly is as follows:
mov ebx, esp
mov ch, 0x4
The shellcode begins by pushing the value “0x5” onto the stack, and popping that value into the register EAX. This sets up the syscall for “sys_open.”
The next set of instructions will set up the arguments to be passed to the sys_open syscall in the EBX and ECX registers. The arguments for sys_open can be found using “man 2 open”
The first argument, which will be in the EBX register, is a pointer to a memory address containing the pathname of the file to be opened. In the assembly code, first ECX is XOR’d with itself and pushed to the stack. This is to set a null terminator for the end of a string value. Next, three values are pushed onto the stack. These values are hex encoded to “/etc//passwd”, meaning the file that will be opened is /etc/passwd. The stack pointer’s memory address is moved into EBX, so that the EBX register has the address that points to the beginning of the “/etc//passwd” string.
Next, the assembly code sets an integer value for the “flags” argument, which determines the “access mode” for the file that is opened, or whether the file is opened as read-only, will be overwritten, be appended to, etc.
The first instruction is “inc ECX”, which increases ECX from it’s value of 0 to 1. Next, the value 0x4 is moved in CH. When ECX has a value of 1, the register looks like 0x00000001. The last two numbers are the CL register, and the next two numbers immediately to the left of CL, or 0x00000001, are the CH register. By moving the value 0x4 into CH, the value of ECX becomes 0x00000401. This can be seen in GDB as you step through the assembly code.
The value of 401 in ECX means that the opened file will be appended to.
The sys_open syscall is then made with the registers set to open “/etc//passwd” and append to it. After the syscall is made, if it is successful, the file descriptor of the opened file will be returned in the EAX register. During analysis, the file descriptor for the opened “/etc//passwd” file was 3.
The next instruction is “xchg ebx, eax”. This takes the values in the EBX and EAX registers and swaps them. After the swap, EBX contains the value 3, and EAX contains the memory address of the “/etc//passwd” string.
The next instruction is “call 0x804a093”. This means that the assembly code will now move to the memory location 0x804a093 to perform it’s next instruction. The memory location 0x804a093 was inspected in GDB, and the instruction at 0x804a093 was for “pop ecx”
When the “call 0x804a093” instruction is performed, it first puts the memory address immediately following the call onto the stack before jumping to the memory address in the call. In this shellcode the memory address immediately following “call 0x804a093” is 0x804a06b.
After the call instruction is made, the next instruction performed is the “pop ecx” at 0x804a093. This will take the value at the top of the stack, and pop it into ECX. Because of the previous call instruction, this means that the memory addres 0x804a06b is popped into ECX.
The instructions following the “pop ECX” are as follows:
0x804a093 : pop ecx
0x804a094 : mov edx,DWORD PTR [ecx-0x4]
0x804a097 : push0x4
0x804a099 : pop eax
0x804a09a : int 0x80
The value of EAX before the interrupt for the syscall is 0x4, which corresponds to sys_write, meaning something will be written to a file. The arguments for the sys_write call are shown from “man 2 write”
The first argument is for the file descriptor to be written to. This is currently in the EBX register from the “xchg ebx, eax” instruction.
The next argument is a pointer to a a buffer of what will be written to the file. Currently, the ECX register contains the memory address 0x804a06b. When this address is inspected, it contains a string that looks like an entry in an /etc/passwd file.
The final argument is the length of the buffer that will be written to the file. The instruction “edx,DWORD PTR [ecx-0x4]” sets EDX to the value that is stored at the address that is in ECX, minus 4. 0x804a06b minus 4 is 0x804a067. When this memory address is inspected, it has the value “0x28”, which means 40 bytes from the buffer will be written to the file.
When this was all completed, the shellcode wrote to the “/etc//passwd” file to add the metasploit user. This was confirmed by looking at the last line of the “/etc//passwd” file. This line was run through the password cracking tool John the Ripper to confirm that the password for the user was “metasploit”, as stated in the default configuration.
linux/x86/exec to execute Nmap
The next meatsploit payload to analyze was the /linux/x86/exec payload. By default this shellcode will launch a /bin/sh shell using execve, which is something I was already familiar with. To make this more interesting for analysis, I configured the payload to launch Nmap with several command line arguments, as I was not familiar with how the command line arguments would be handled through execve. The command to generate the payload was:
msfvenom -p linux/x86/exec CMD=”/usr/bin/nmap -p 21,22,23 -sV –stats-every 60s -T4 scanme.nmap.org” -f c
Analysis in GDB
The first two instructions of the assembly code are to put the value “0xb” into EAX, which will be used to make the execve syscall.
The next instruction is “cdq.” I wasn’t sure what this would do when I first saw it. To test it out, I stepped through the instruction in GDB, listing the contents of the registers before and after the “cdq” instruction.
The result of the instruction was that EDX went from 0xb7fbc870 to 0x0. To research what the “cdq” instruction does, I used the following resource: http://qcd.phys.cmu.edu/QCDcluster/intel/vtune/reference/vc69.htm
“CDQ” stands for “Convert Doubleword to Quadword.” “CDQ” will sign extend the EAX register into the EDX register. Basically, if the value of EAX is positive, then EDX will be set to “0x0000000.” If EAX is negative, the value of EDX will be set to “0xFFFFFFFF.” A quick test program was written that ran CDQ after EAX was a positive number, and then a negative number, and it should how this changed the value of EDX.
So, the “cdq” instruction was to get the value of EDX to be 0x0.
The next set of instructions are:
This pushes 0x0 to the stack as a string terminator, and then 0x632d, which is the string “-c.” This will be used to specify when /bin/sh executes, it will execute a command following the “-c” flag. The stack pointer is then moved into the EDI register.
The next set of instructions push the string “/bin/sh” onto the stack, and then move the stack pointer into the EBX register. This is to tell execve to launch /bin/sh.
The next instruction is “call 0x804a0a1.” This will jump execution to the 0x804a0a1 address, and will place the next memory location after the “call 0x804a0a1” instruction onto the stack, which is 0x0804a05d.
The assembly code at location 0x804a0a1, which the shellcode just jumped to, is shown below.
This is setting up the stack for the execve syscall. “push edi” pushes the memory location of the “-c” string onto the stack. “Push ebx” pushes the memory location of the “/bin/sh” string onto the stack. Then, the stack pointer is moved into the ECX register.
The stack now has the following in it:
- the memory address 0x0804a05d from the “call” instruction
When the memory address 0x0804a05d is inspected, it shows that it contains the command to launch an Nmap scan.
This can also be seen when using libemu to see the “*arg” values used for the execve call.
The final requirement for the execve syscall is the “envp” argument, which is stored in EDX. The value of EDX is still 0x0.
When the shellcode is executed, an Nmap scan successfully launches.
The linux/x86/shell_find_port payload works by looking fro an established connection on a system and then spawning a shell on that connection. The options for the payload are shown below.
The only option for the payload is “CPORT”, which is the port to look for the established connection on. If CPORT is not provided at the time of the payload creation, a random number will be chosen. The CPORT value of “50509” shown above changes if you list the options again, shown below.
Libemu was used to generate a graphical representation of the shellcode using the command: “msfvenom -p linux/x86/shell_find_port -f raw | sctest -vvv -Ss 10000 -G shell-find-port.dot”
The libemu output shows that the shellcode’s first syscall is for 0x66, or sys_socketcall. Sys_socketcall provides a number of functions or “calls” that it can execute. The function it will execute is stored in the EBX register, which for this shellcode is 0x7. The sys_socketcall calls and their corresponding numbers is found in /usr/include/linux/net.h.
As shown in the libemu output, the 0x7 sys_socketcall call is for “getpeername.”
getpeername “returns the address of the peer connected to the socket sockfd. The arguments for getpeername are shown below.
getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
After getpeername was called, a comparison between the value stored at memory address in the EDI register plus 2 and the value “0xde1f”. If these two values are not equal, the shellcode jumps back to the “inc [ecx]” instruction, and performs the geetpeername call again.
To better understand how the the call for getpeername was being set up by the shellcode, GDB was used to step through the assembly code and shellcode execution.
At the time of the getpeername call execution, the ECX register will store the values used by getpeer name. The values in ECX are shown below.
The values for ECX correspond to the following arguments
- int sockfd – 0x00000001
- struct sockaddr *addr – 0xbfffef38
- socklen_t *addrlen – 0x00000010
The first getpeername call will look at the socket file descriptor “1.” The values stored at the memory addresses in ECX are shown below.
The *addr argument for getpeername is 0x00000000 meaning that the address being used is 0.0.0.0. The *addrlen argument for getpeername is 0x10, or 16 hexidecimal, which is the length of the address. This all means that getpeername will look at socket file descriptor 1 and see if it has an established connections on any interface on the system.
After getpeername is executed, a comparison is made by the shellcode with the following instruction.
cmp WORD PTR [edi+0x2],0x4621
The value stored at the memory address stored in the EDI register, plus two, is compared to the value 0x4621. 0x4621 corresponds to the hex value, in reverse order, of the port number 8,518. The value stored at EDI+0x2 is 0x0000, shown below.
Because these values are not equal, the comparison will fail. The next instruction is “jne 0x804a04e”, which will cause the shellcode to jump back to the “inc [ecx]” instruction. This will increase the value of the socket file descriptor, and retry the getpeername call again.
When this ran on my system, the shellcode never found an established connection to launch the shell on. To get past this and analyze the remaining shellcode, the instruction pointer (EIP) was set to the address of the instruction after the port comparison and the conditional jump.
The remaining shellcode is similar to how a shell is set up for a bind-shell or reverse-shell payload. The dup2 syscall (0x3f) is used to duplicate the file descriptor of the socket discovered in getpeername into the file descriptors 2, 1, and 0 (stderr, stdout, stdin).
The next syscall is for execve (0xb), which executes a /bin/sh shell for the connection
All shellcode, scripts, and notes used for this analysis can be found at: https://github.com/FatRodzianko/slae-exam/tree/master/5-msf_payload_analysis
SLAE Student Disclaimer
This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification: http://securitytube-training.com/onlinecourses/securitytube-linux-assembly-expert/
Student ID: SLAE- 1373