Microcorruption: Algiers

Wed 17 May 2023

The Scenario

My adventures in Algeria begin the same as everywhere else I've gone so far - staring down a Lockitall LockIT Pro, revision d.01 this time with support for the brand-spanking-new LockIT Pro Account Manager. This lock doesn't have an attached HSM so with any luck the exploit will be more straightforward, but it's impossible to say before diving in.

Understanding The Code

The second thing I notice looking at the code is that once again main() is a stub function to call login() - the first thing I notice is the presence of malloc() and free() functions. In a vacuum the presence of these functions doesn't mean much - practically every C executable that interacts with data uses them - but their presence here is a big signal for what I would have to do to make these bonds mine: heap exploitation.

A screenshot of the disassembly of the login function

Like the format string exploits in Novosibirsk, heap corruption and exploitation is something I've heard about for a long time but never implemented myself; after reading a bunch of guides on the structure of the heap and general techniques on exploitation I dove into the lock's implementation.

After laboriously single-stepping through the firmware multiple times, I think I have a good model on how malloc() works in this lock. The most important part in regards to exploiting the implementation is the heap metadata of two linked lists and a header containing size and other associated data: the first word in each metadata section is a pointer to the previous chunk in memory, with the exception of the first chunk pointing to itself; the second word is a pointer to the next chunk in memory, with the last pointing back to the first to make a circular list; the third word is a combination of the size of the current chunk and a combination of allocation metadata and the use status of the previous chunk.

An annotated screenshot of malloc data and metadata

But of course malloc() is only one half of memory management, so once the program is done with its memory it calls free() to return it. free() works on this platform by clearing the in-use status bit of the current chunk, then checking the previous and next chunks to see if the current chunk can be coalesced with either of them, and finally if possible adding the size of the current chunk plus metadata to the next chunk.

Exploiting The Code

Given the existence of the unlock_door() function, I figured I shouldn't try to fix what isn't broken and figure out what pointer I needed to overwrite so login() would return to unlock_door(). I wish I could explain my process, but the best explanation I have for what I did here is "pointer shenanigans": I knew I'd have to overflow the username to overwrite the heap metadata, but I really just tried random things until I got the value I wanted in the location I wanted and the door unlocked.

As I was flying back to Russia, having spent the better part of a day trying to wrap my head around heap exploitation, I realized I was kidding myself when I thought things were a bit difficult before - between the previous format string attacks, the current heap overflows and no idea what the future may hold, Real Hacker Hours start now.