One very common tool among penetration testers is Metasploit, which includes a lot of nice exploits and payloads. The 5th assignment of the SecurityTube Linux Assembly Expert certification is about Metasploit shellcode analyses for Linux/x86 target systems. The task is to take 3 shellcode payloads generated by msfpayload (which has been replaced by msfvenom in the meanwhile) and dissect their functionalities using different analysis tools like GDB, Ndisasm or Libemu.

Since there are numerous interesting payloads for Linux/x86 in Metasploit:

msfvenom-linux-x86-payloads

But I have already covered bind and reverse tcp shellcodes in previous articles, I’m going to analyse some local payloads in this article.

I try to stick to only one specific tool for each payload, but in some cases I will also use GDB, which helps explaining things a little bit better. But anyways…the first goes with GDB, the second with Libemu and the third with Ndisasm.

  • linux/x86/chmod => GDB
  • linux/x86/exec => Libemu
  • linux/x86/read_file => Ndisasm

Have fun reading!

linux/x86/chmod

The first payload I’m going to dissect is linux/x86/chmod. The payload changes the file permissions of the supplied FILE argument to 666. The following msfvenom command piped through hexdump:

outputs a ready to use shellcode for a C code snippet:

Compiling and executing results in a change of the permissions of the file /tmp/rcesecurity. It had permissions of 600 before the payload execution, and 666 after payload execution:

msfvenom-linux-x86-chmod-1

Let’s attach GDB to the executable and disassemble the payload. The initial part up to the first call at 0x804a045 looks quite normal. The first cdq (convert double to quad) is simply used to clear out the EDX register. After that 0xf is pushed onto the stack and POPed to EAX again, followed by the EDX 0x0, which is PUSHed onto the stack.

msfvenom-linux-x86-chmod-2

After the first call instruction at 0x804a045, it looks like there is a lot of gibberish, but it isn’t really. Interesting to see is that the CALL instruction is referencing some instructions in the middle of the gibberish. If you have a look at the string-representation of the gibberish using GDB, it reveals that you’re dealing with the supplied FILE argument used during the payload creation. Due to the fact that the FILE string is 16 bytes in length, the call starts at the 17th byte after 0x0804a04a <+10>, which would be 0x804a05b and right inside the gibberish.

msfvenom-linux-x86-chmod-4

Since a CALL instruction puts a pointer to the next instruction onto the stack, you’ve got a valid pointer to the FILE argument now. After stepping over the CALL instruction, the finalizing instructions are understandable again and include two new instructions (pop ebx and push 0x1b6), which were not visible before.  So the recently added pointer to the filename is POPed to EBX and the value 0x1b6 is PUSHed to the stack and afterwards POPed to ECX:

msfvenom-linux-x86-chmod-5

Finally the syscall 0xf (sys_chmod) is executed, which changes the persmissions of the FILE. It’s C-style representation and the associated register layout is as follows:

The register layout at this point:

EAX => 0xf => sys_chmod syscall

EBX => *pathname => /tmp/rcesecurity

ECX => 0x1b6 => octal representation of 666

In the end another sequence to gracefully exit the payload is executed – although the EBX is not filled with a 0x0 here, which would be a really graceful exit 😉 – by PUSHing 0x1 (sys_exit syscall) to the stack, POPing it to EAX and executing the syscall…and this results in a chmod’ded file. Done 🙂 !

linux/x86/exec

The next payload is called linux/x86/exec, which simply executes a binars on the target system. Let’s first create a simple binary snippet using msfvenom and the CMD argument “hostname” and piping it through hexdump:

This creates the following shellcode output, which can be put into a C source code snippet:

Compiling and executing results in:

msfvenom-linux-x86-exec-1

Seems to work smoothly too. Now pipe that string to the libemu tool sctest:

This results in the following output:

This does not only create a very nice source code snippet, but also creates a .dot file visually describing the shellcode. The .dot file can be easily converted to another format using the dot command:

Et voila:

msfvenom-linux-x86-exec

Looks nice and simple. Let’s dissect it.

First the value 0xb is pushed onto the stack and afterwards POPed to EAX. The next instruction “Convert Word to Doubleword” (cwd) is a simple trick to clear out the EDX register. By using this function the sign bit of the AX register is copied into DX. Since the sign bit is 0 here, DX is zeroed out:

Afterwards the zeroed EDX is pushed onto the stack, followed by the word 0x632d, which is a representation of “-c”. The pointer to the actual ESP address of the string is then saved to EDI:

Finally another important part is pushed onto the stack: “/bin/sh” via two push dword instructions, and the ESP pointer is also saved to EBX:

In the next step first EDX is pushed onto the stack. The following CALL is used to get the pointer to the customized command (in this case hostname, which is at 0x0041701d) onto the stack:

Another side-effect is that the call moves the EIP 0xe bytes to the next instruction set, which pushes the different arguments into the stack and finally leads to the execution of execve():

So what’s happening here? Maybe you already know the execve() call structure:

The register layout at this point:

EAX => 0xb => sys_execve syscall

EBX => *filename => pointer to “/bin/sh”

ECX => argv[] => ESP (same like EBX)

EDX => envp[] => 0x0

And all arguments “/bin/sh” + “-c” + “hostname” + terminating 0x0 are placed onto the stack.

msfvenom-linux-x86-exec-2

 

Looks like a smooth execve code execution! Done 🙂

linux/x86/read_file

The last payload to dissect is linux/x86/read_file. First let’s create msfvenom payload, which reads the file specified using the PATH argument (/etc/hostname):

Adding the output to the C snippet file:

Compiling and executing it results in:

msfvenom-linux-x86-read-file-1

It works. So let’s create a binary file to use with ndisasm:

Some ndisasm magic later:

You’ve got the plain assembly of the shellcode:

msfvenom-linux-x86-read-file-2

This is a perfect example of a jmp-call-pop technique. The first instruction jumps 38 bytes to the call function at 0x00000038. Maybe you have already recognized the bytes right after the call instruction:

This is the byte representative of the file to read by the payload: /etc/hostname. By using the call instruction the pointer to the address of the next instruction is saved onto the stack, which means you have a reference to the payload. Afterwards the call is heading back to address 0x2, which is a combination of MOVing 0x5 (this is the sys_open syscall) to EAX, POP the recently saved pointer to EBX, clear out the ECX register and finally issue the syscall:

When you take a look at the used syscall arguments, you meay already recognize what is happening here:

The register layout at this point:

EAX => 0x5 => sys_open syscall

EBX => *pathname => points to /etc/hostname

ECX => flags => 0x0

The syscall opens the file /etc/hostname and returns a file descriptor into EAX. This descriptor is then saved to ebx and syscall 0x3 (sys_read) is MOVed into EAX.

The definition of sys_read refers to 3 arguments:

Beside having EBX to reference the file descriptor, you still need a buffer and a size count. First the address of the ESP is MOVed int ECXto have a valid buffer for the return value on the stack, after this the value 0x1000 is MOVed into EDX representing the size_t count followed by the execution of the syscall:

The register layout at this point:

EAX => 0x3 (sys_read)

EBX => fd => points to the file descriptor received by previous syscall

ECX => *buf => some pointer to a valid stack address

EDX => size_t count => 0x1000 bytes

Up next a sequence to setup syscall 0x4 (sys_write) to output the read contents:

Since the previous syscall returned the number of bytes read to EAX, this value just needs to be put into EDX to represent the size_t count. EAX is filled by the syscall as mentioned above, and EBX is filled by 0x1 serving as a file descriptor. Luckily ECX already points to the contents read from the file during the last syscall, so no need to change anything here before executing the syscall:

The register layout at this point:

EAX => 0x4 => sys_write

EBX => fd => 0x1

ECX => *buf

EDX => size_t count

One part, which is not shown by the sctest graph, is about exiting the sequence gracefully by using the syscall 0x1 (sys_exit) and by filling EBX with 0x0:

Therefore the syscall exits with a status of 0 (successful).

msfvenom-linux-x86-read-file-3

 

Contents of the file were successfully read! You can find all the used C source code snippets in my Git too.

This completes another SLAE mission! Damn – I really love this low-level stuff 🙂

 

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

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

Student ID: SLAE- 497

SLAE: Dissecting Msfvenom Payloads (Linux/x86)
Tagged on:             

Leave a Reply

Your email address will not be published. Required fields are marked *

Time limit is exhausted. Please reload CAPTCHA.