Fun with Assembly 5: Buffer Overflows to reach the parts other code can't reach

Posted by Hywel Carver on August 5, 2015

This is the fifth in a series. You might want to read the previous post before reading this.

This post is based on the Hanoi level on microcorruption.com. Like last time, we’re trying to find an input to open a lock without knowing the correct password, using knowledge of assembly language.

First glance

As usual, I’m going to start by looking through the login function.

4520:  c243 1024      mov.b #0x0, &0x2410
4524:  3f40 7e44      mov   #0x447e "Enter the password to continue.", r15
4528:  b012 de45      call  #0x45de <puts>
452c:  3f40 9e44      mov   #0x449e "Remember: passwords are between 8 and 16 characters.", r15
4530:  b012 de45      call  #0x45de <puts>

The first line here sets the value at 0x2410 to 0 - but it’s hard to say why without reading more. The others write out some text. Reading on…

4534:  3e40 1c00      mov   #0x1c, r14
4538:  3f40 0024      mov   #0x2400, r15
453c:  b012 ce45      call  #0x45ce <getsn>

This should look familiar too. The parameters for getsn are the max number of characters to get, and the place to store the result. Here, they are 0x1c = 28 characters and the location 0x2400.

This already looks like a good place to start. The message says that passwords can’t be longer than 16 characters but the code actually accepts 28. Where do the extra characters go? They’re going to start at 0x2410, a number which sounds familiar. It was set to 0 at the very start of the function.

Looking further down, that number appears again:

455a:  f290 1a00 1024 cmp.b #0x1a, &0x2410
4560:  0720           jne   #0x4570 <login+0x50>
4562:  3f40 f144      mov   #0x44f1 "Access granted.", r15
4566:  b012 de45      call  #0x45de <puts>
456a:  b012 4844      call  #0x4448 <unlock_door>
456e:  3041           ret

First the code checks 0x2410 has the value 0x1a. If it isn’t equal, we jump to a later point in the function. If it is equal, we carry on: the text “Access granted” is printed, then the function to unlock the door is called.

We can use a buffer overflow here to put the value 0x1a in 0x2410. The code allows for 28 bytes to be input from 0x2400, so 16 bytes of nothing followed by the 1a byte should do the trick.

I used 0102030405060708090a0b0c0d0e0f101a for my final input.

Endnotes

This was a straightforward example: the code very clearly expected 16 characters for input, but allowed room for many more. It’s actually very easy to leave room for buffer overflows in real-world code, but they’re normally harder to spot. The easiest way to find them is often to try ‘fuzzing’ with random long strings of input, to see if they make a program crash. If a program does crash, clearly something was unanticipated about the input you gave it, which could let you find a buffer overflow.