I know, I know. With every new paper/post/challenge around ptmalloc2 if feels like everything has been said. And yes, one rarely needs to understand the Malloc Maleficarum to exploit a heap bug. Neither does one need to get PC control from wizardly metadata overwrites to exploit applications where function pointers, indexed arrays and the like already live on the heap.
Nowadays exploiting heap structures is often nothing more than finding a way to overwrite interesting structures residing on the heap, by having old chunks overwritten or new chunks allocated within areas under attacker control. Pure heap exploits have been discarded as almost impossible for quite a few years now.
I've recently had the pleasure to stay for a couple of weeks in the gorgeous industrial zone of Belmont, WA. Nearest pub being a ~25 minutes walk, I've put my 5 Kbps Internet connection to good use and downloaded a few challenges from recent CTFs. qwn2own from the latest Boston Key Party CTF definitely stood out, as we don't often see browser exploitation challenges in CTFs. I reckon it very successfully touched the browser exploitation world, as it required players to use techniques close to nowadays client-side exploits, but with the ability to easily debug and look at the underlying source. And so I've decided to use this challenge as a material for my yearly article which will try to cover why corrupted vector/array objects can be (and often are) used to write universal, per platform, browser exploits.
PlaidDB is an x64 stripped executable, compiled with full RELRO, PIE and NX support. It's libc was provided, seemingly from Ubuntu LTS 14.04. It manages key-value pairs which one can add, update, retrieve or delete:
The vulnerability lies in the function at 0x1040, used to get the key values from standard input:
"Add" allocates C++ objects, and "perform" calls the first method of each object. Once an object is deleted its slot cannot be refilled, and its actions cannot be performed. As shown above, the pond's action discloses the address of its object, providing us with a free leak.
Comme son nom l'indique, risc_emu est un petit émulateur de CPU RISC, écrit en C++ et compilé pour x64. On doit lui donner en entrée un bytecode en base64 qui est ensuite exécuté. L'exécutable est de taille modeste, je ne vais donc pas rentrer dans les détails.
Le main() commence par initialiser la table des opcodes, main::fun : 10 opcodes de 0 à 9 (add, addi, sub, subi, mmul, mdiv, mxor, mand, mor, term) dans l'ordre. Le bytecode est pris sur l'entrée standard par cin >>, ce qui signifie qu'il est de longueur arbitraire. Il est ensuite décodé, puis recopié dans main::buf, situé 0x90 bytes avant main::fun. Cependant, juste après l'initialisation de main::fun, un cookie (qui correspond simplement au premier rand() de srand(time(0))) est placé juste à la fin de main::buf, et vérifié avant toute exécution de bytecode.