microcorruption

The input

For this particular problem, the user can only input two string in the form of username and password. The maximum accepted length is 0x30 bytes.

The exploit

Looking at the names of the functions, we immediately see malloc and free. Both functions are commonly used to allocate memory on the heap. This gives a vague idea that the exploitation will be heap related. malloc and free are both called twice, once for username and once for password. malloc is called before user input, and free is called just before the program finishes, suggesting free is going to be the target of the exploitation.

In this example, heap is implemented as a linked list, and each element has the following structure. The header is the same for both freed and in use memory. This implementation is similar to that of glibc:

<element>
    <6 byte header>
        <2 byte pointer to the previous block>
        <2 byte pointer to the next block>
        <2 byte size, with the least significant bit being indicating IN_USE>
    <content>

For both password and username, the size of content is only 0x10, which is smaller than the allowed input size of 0x30. As both blocks are allocated next to each other, with password being directly after username, the heap can be overflowed to overwrite the header for password. Furthermore, free is conveniently called on password first (the second input), allowing the overwritten header to be used immediately.

The free function vaguely resembles the following code:

r15 = &freeing chunk
r13 = r15.size
r14 = r15.prev
r12 = r14.size
if (!r14.IN_USE){
    r12 += 6
    r12 += r13 
    r14.size = r12
    r14.next = r15.next
    r13 = r15.next
    r13.prev = r14
    r15 = r15.prev
}
r14 = r15.next
r13 = r14.size 
if (!r14.IN_USE){
    r13 += r15.size
    r13 += 6
    r15.size = r13
    r14.next = r15.next
    r14.prev = r15
}

We aim to overwrite the return pointer (at 0x4394) of free to direct it to our desired location and execute custom code. This can be done by utilising the line r13.prev = r14, where r13 = r15.next and r14 = r15.prev. Thus we can use r15.prev as the pointer to the custom code and r15.next as the position to be overwritten (the return pointer). However, it is important to note that the first 6 bytes after prev will be changed later in the code as it is treated as a header for a chunk, so a 6 byte padding is required before out custom code. Though that header will be treated as code and executed, it is not important as it is likely it will have not severe effect.

The solution

password is left blank and the username has the following structure:

<–8 byte padding–>
<–8 byte payload–>
<–2 byte input address–>
<–2 byte return address–>

and the following solution:

aaaaaaaaaaaaaaaa30127f00b012b6460e249443

8 bytes of padding is required, because when a is used, a 4 byte instruction will appear.