Re: vulndev1.c solution (warning SPOILER)

From: Marco Ivaldi (raptorat_private)
Date: Thu May 15 2003 - 04:10:08 PDT

  • Next message: Cameron Brown: "RE: vulndev1.c solution (warning SPOILER)"

    > for the byte of \x0b.. this could have really been anything, as long as
    > the least sig bit was 0x1..  This is how the allocation/deallocation
    > functions mark the previous chunk as in use..  This tricks the first
    > free() call into overwriting the GOT entry, using data from buf2 (which
    > it thinks it part of a chunk header)..  I hope this helped clarify.. if
    > anyone else can add more to this please do..
    
    This is not entirely correct. This is the chunk structure for allocated
    chunks (ripped from the excellent http://www.phrack.org/phrack/57/p57-0x08
    by Michel "MaXX" Kaempf):
    
        chunk -> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                 | prev_size: size of the previous chunk, in bytes (used   |
                 | by dlmalloc only if this previous chunk is free)        |
                 +---------------------------------------------------------+
                 | size: size of the chunk (the number of bytes between    |
                 | "chunk" and "nextchunk") and 2 bits status information  |
          mem -> +---------------------------------------------------------+
                 | fd: not used by dlmalloc because "chunk" is allocated   |
                 | (user data therefore starts here)                       |
                 + - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
                 | bk: not used by dlmalloc because "chunk" is allocated   |
                 | (there may be user data here)                           |
                 + - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
                 |                                                         .
                 .                                                         .
                 . user data (may be 0 bytes long)                         .
                 .                                                         .
                 .                                                         |
    nextchunk -> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
                 | prev_size: not used by dlmalloc because "chunk" is      |
                 | allocated (may hold user data, to decrease wastage)     |
                 +---------------------------------------------------------+
    
    And this is the same structure for free chunks:
    
        chunk -> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                 | prev_size: may hold user data (indeed, since "chunk" is |
                 | free, the previous chunk is necessarily allocated)      |
                 +---------------------------------------------------------+
                 | size: size of the chunk (the number of bytes between    |
                 | "chunk" and "nextchunk") and 2 bits status information  |
                 +---------------------------------------------------------+
                 | fd: forward pointer to the next chunk in the circular   |
                 | doubly-linked list (not to the next _physical_ chunk)   |
                 +---------------------------------------------------------+
                 | bk: back pointer to the previous chunk in the circular  |
                 | doubly-linked list (not the previous _physical_ chunk)  |
                 +---------------------------------------------------------+
                 |                                                         .
                 .                                                         .
                 . unused space (may be 0 bytes long)                      .
                 .                                                         .
                 .                                                         |
    nextchunk -> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                 | prev_size: size of "chunk", in bytes (used by dlmalloc  |
                 | because this previous chunk is free)                    |
                 +---------------------------------------------------------+
    
    Altough there may are multiple ways to exploit this off-by-one bug, my
    exploit overflows the least significant byte of the size control field of
    second chunk with an 'A' (0x41) char. When executing the first free(),
    dlmalloc will consequently think the beginning of the next contiguous
    chunk is in fact after the real size (0x100), plus the corrupted byte (in
    this case 0x41), and will therefore read the memory 0x41 bytes after the
    correct place (size field of the next contiguous chunk). Since this memory
    area is (hopefully) zeroed, it finally reads an even integer (0x0), whose
    PREV_INUSE bit is clear (#define PREV_INUSE 0x1), thus processing the
    corrupted second chunk with the unlink() macro and altering the program
    behaviour as explained by Jon Erickson.
    
    In my current setup, any value of char => 0x4 (no matter if even or odd)
    is good to trigger the unlink() to process the corrupted second chunk.
    
    I highly suggest the reading of the following papers:
    
    "Once upon a free()", by anonymous
    http://www.phrack.org/phrack/57/p57-0x09
    
    The aforementioned "Vudo malloc tricks", by MaXX
    http://www.phrack.org/phrack/57/p57-0x08
    
    Happy hacking :)
    
    :raptor
    --
    Marco Ivaldi
    Antifork Research, Inc.   http://0xdeadbeef.info/
    3B05 C9C5 A2DE C3D7 4233  0394 EF85 2008 DBFD B707
    



    This archive was generated by hypermail 2b30 : Thu May 15 2003 - 22:38:45 PDT