Sunday, June 10, 2012

vortex3 (reloaded)

In the original vortex3 post, I wasn't able to reproduce the exploit since the vortex levels were recompiled with a newer version of gcc. Thanks to some hints from the vortex admins, I managed to solve the level using another approach. Here are my notes from github:

Obviously, the objective of this level is to overflow buf which will allow to overwrite lpp. In turn, buf's address will be written to wherever *lpp points to. By selecting an appropriate memory location for lpp, it will be possible to inject &buf as a function pointer into some data structure that will later execute it. I tried two approaches:
  • Overwriting an entry of the .dtors section, which contains a list of destructors, each called subsequently before program termination.
  • Overwriting an entry of the .plt/.got sections, the dynamic linking structure which resolves the position of shared library functions such as exit().

I guess the original intent to solve this level was to use the first approach, induced by the suggested reading material. In the mean time, the vortex wargames have been recompiled with a newer version of gcc and unfortunetaly, the .ctors/.dtors sections are no longer writable, as mentioned by the vortex admins. In a second notice, they suggest to brute force the 2^16 possible values and draw own conclusions. This resulted in 3 address values which led to a successful exploit: 0x0804928c, 0x080492cc and 0x08049306. Interestingly enough, these memory locations originate from a read/write memory location, where the program text is loaded. But the program text is actually executed from the 4k memory region starting at 0x08048000. Comparing the dumps of both regions 0x08048000-0x08049000 (read/execute) and 0x08049000-0x0804a000 (read/write), I realized that they almost match, the only differences several are unitialized memory addresses in the latter. From there on, I started reading about the loading process and dynamic linking in order to understand the meaning of this memory layout. I concluded that the raw program text is loaded in the higher memory region. During initialization, the loader copies its contents and completes missing references to several dynamic process structures such as the .got and the .plt starting at 0x08048000.

Following the pointers from 0x0804928c, we see that it leads to the .plt at 0x0804962c (exit@got.plt) and eventually to the exit() function from the dynamically linked libc at 0x0804830a (). Following the double indirection (**lpp), the .plt entry for exit() is therefore overwritten and program execution will jump to &buf instead of exit() when called at the end of the main function.