This year, like last LegitBS released updated services part way through the competition with the old vulnerabilities fixed and new ones. The first imap service (implements the IMAP protocol) had a bug in the SELECT command that would overflow the mailbox name, allowing one to change the username to “.” which would then allow listing/reading all users mail (things were stored in a directory per user)

(edit: DEF CON CTF Challenges available @ http://shell-storm.org/repo/CTF/Defcon-22-finals/)

But in v2, this bug was fixed… instead it was MUCH more evil. instead of being stored in plain text, messages were base64 encoded. Not a huge deal, but what was weird was the message was base64 decoded in the STORE handler. This command only updates message flags, there’s no need to decode the contents… Encoded messages were limited to 1000 bytes, which means that a decoded message could be 750 bytes large… turns out the buffer that the message was decoded in was only 740 bytes… this allowed us to overflow whatever came after the buffer. It turns out this was a pointer to the flags, which were written right before the function returned. It’s only a 1 byte write … with only control over bits 1-6 (0-7 numbering). Bit 7 was always 1, and you could either leave bit 0 as is, or clear it. The basic pseudo code was:
    update_flags(aMode, aFlags) {
    int len;
    int new_flags;
    uint8_t flags;
    char decoded_buf[740];
    uint8_t *pFlags;
    char encoded_buf[1000];
    get_message(&buf, &flags, len);
    base64_decode(encoded_buf, len, decoded_buf);
    new_flags = parse_flags(aFlags); // bit or of result of parse with 0x80
    if (mode == SET_ALL) // command option is FLAGS
        *pFlags = new_flags;
    if (mode == SET) // command option is +FLAGS
        *pFlags |= new_flags;
    if (mode == CLEAR) //command option is -FLAGS
        *pFlags ^= new_flags; //xor
        *pFlags |= 0x80
    store_flags(...);
}

Now the question is what to write.. luckily there was also an info leak .. Messages were given a unique id that was randomly generated when the mailbox was created. It so happened that the random numbers were not actually the result of calling rand.. (intentional…) one turned out to be the stack canary, the other was an address on the stack.. So now we know the address of the stack, and the stack canary for our fake stack frame… now how do we get a stack frame where it’ll be used. This was the hardest part.. now luckily the stack was executable.. but we couldn’t just change a return address because we didn’t know the value present and we couldn’t write the low bit and stacks are all in high memory..

It turns out that main uses r7 as a frame pointer, and the update_flags function stores this variable on the stack for us. we could change main’s frame pointer, and when it goes to return it may read from space we have control over.. well first I just tried moving r7 by a small amount, that way the previous command input would be used as the stack.. however this has a problem – if we only change r7 by a small amount (512) we are still close to sp.. and when main would call bzero, the data buffer that was being cleared was part of the stack frame for bzero, which would mean r7 was cleared and main would then crash. Eventually I found the function that handles the FETCH command. it has a very large stack frame and the decoded message is read info a buffer.. I could move r7 to point to an address that when used by main to return would point to the message data.

So that was the “easy” part..the next part was dealing with ASLR.. not only was simple ASLR turned on where the page address changed, but the stack randomization was turned on as well. which meant that the stack was aligned at 256 byte boundaries (meaning 24 bits of random).. and I was trying to reduce the frame by an order of 3000 bytes.. I needed to calculate not only the necessary flags (stored as bitfield) to change r7, but also the new return address (luckily stack was rwx), offset of the stack canary and return address in my message that will be used as the stack frame

So final sequence was:

  1. Create mailbox
  2. Leak stack canary and stack address with EXAMINE command
  3. Calculate address of various things on stack
  4. Determine a valid address i can set r7 to that will put the return address of main into my buffer, not the offset of the frame end
  5. Create messages that will allow me to set low two bytes of r7
  6. Create message – main’s new stack frame, using the offset calculated
  7. Send STORE to update 2nd lowest byte of r7 (jump far away first, as main does bzero and we don’t want to overwrite stored stack frames)
  8. Send STORE to update low byte of r7
  9. Send FETCH to retrieve stack frame
    1. Return from main

Actual implementation is left as an exercise to the reader. Sadly I wasn’t quite able to get this done during the competition.. going on no sleep Saturday night was a little hard.. I was close, I just failed to use my calculated offset in the calculation of the return address.. everything else was in place.. of course then getting shellcode to find the key in the messages was the next step.. and that wasn’t done.