Friday, September 5, 2014

Microcorruption - Hanoi

These next few levels, if you aren't familiar with delving into assembly code, are a great place to start stepping through the code to get an understanding of program flow.

From looking at the code we can see that there are some basic structures that we will see over and over again in the coming levels.

trap


The trap interrupt is a software call to a hardware interrupt that results in some action being taken based on the status of certain registers - in this case the contents of the status register are used. The action taken is either to unlock the lock or set some value in another register (like r15) indicating that the supplied password was correct.

We will use this trap interrupt whenever we can. Page 8 of the manual starts the listing of valid status register contents. While some of the beginning entries seem unusable, the last entry is clearly gold: "interface with deadbolt trigger to unlock door..."

If you can set sr to 0x7F and call the trap, you win.

low level init, do copy data, do clear bss

These are some housekeeping routines called at the start of the program that aren't useful for our purposes.

main


Mostly we can expect that this is where the action would begin. Throughout the various levels we will see a variety of program styles. Some will have main filled and very few function calls, whereas this particular level the only purpose of main is to call login and then drop through to:

stop_prog_exec


Terminal point for program execution. If you get here, you failed.

unlock_door


As you can see from the code the action of unlocking the door is exactly as described above: Get 0x7F into the sr and (eventually via INT) call the trap. This doesn't always occur - hardware implementations generally don't have this function in any meaningful fashion.  But if it exists and you can get the code to call it, you win.

INT


Putting this out of order because it goes with unlock door nicely, this performs some housekeeping of preserving register state and calling the trap.

**note** As I mentioned in another posting the game changes over time to prevent people from cutting and pasting entries. In the version I played, 0x7F isn't actually the status that needs to be in the sr. I needed 0xFF to be in the sr - even though the manual said 0x7F. Generally I will refer to 0x7F as being required, but my code will always change to 0xFF.

putchar, getchar, getsn, puts


assembly implementation of C standard io lib functions. Used for user i/o. We will take advantage of their weaknesses and the code that uses them unwisely. There will come a time when the program asks for a 16 character limit password, but will ultimately accept any length which allows us to overflow the buffer. The combination of that limited expectation without following through on i/o limitations allows this kind of buffer overflow exploit. This is how we will be getting our desired value into the sr.


test_password_valid


we can expect to have to endure this function every time, but nothing of any real note happens in here.

login


The start and end of the action generally speaking. There is some point usually in this function where a branch is taken or not the ultimately allows the weakness. It varies by level, but ultimately occurs here - not in test_password as one might expect.

Having an understanding of program flow and the parts each of these functions is important. Knowing that login generally contains the meat allows us to focus on that for our weakness. Let's look at the code:We see that the true comparison for whether or not the door will be unlocked happens at line 455a and has nothing to do with test_password_valid function. The true comparison checks for a hardcoded value (0x9B) at a fixed address (0x2410). If that value is at that address code execution falls through the jne branch to "access granted" followed by a call to the unlock_door function.

4544:  b012 5444      call #0x4454 <test_password_valid>
4548:  0f93           tst r15
454a:  0324           jz $+0x8
454c:  f240 6100 1024 mov.b #0x61, &0x2410
4552:  3f40 d344      mov #0x44d3 "Testing if password is valid.", r15
4556:  b012 de45      call #0x45de <puts>
455a:  f290 9b00 1024 cmp.b #0x9b, &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>


We also know that the starting address of where our input string occurs is 2400. Because the code doesn't actually prevent us from entering more than 16 characters (despite using getsn which can take a limiting size) we can fill addresses 2400-240F with garbage and put 9b at 2410, which will result in unlock door being called.

Here is a sample winning string (don't forget to check the box 'hex encoded') which does exactly that.


414243444546474849505152535455569b

No comments:

Post a Comment