The babyuse service acted like a small weapon shop. You could buy some guns, use, rename and drop them. Seemed likely that this had to be some use-after-free challenge.
When buying a gun, it allocates memory, initializes the gun and puts it in a gunTable array. It also sets a flag in a gunInUse array, which will probably be used later on, to check, if the corresponding gun in the gunTable array is initialized.
From the initialization method for the gun
we can assume, that the guns are implemented as inherited classes from some “base gun” like:
To choose the correct function to call at runtime, C++ uses vtables. Each inherited class contains a pointer to a vtable, which is basically an array of function pointers, that point to the corresponding functions for the instantiated class type.
When a virtual function gets called, it will get the vtable ptr from the object, adds the offset of the function pointer and calls the method at the address, that’s stored there.
So, if we’d be able to overwrite the vtable pointer for a class, we could craft a function pointer array and let the vtable point to that one. When the application then tries to call one of the virtual functions, it would call our injected function instead.
But how can we accomplish this? Well, the service stores the currently selected gun in a global variable, let’s call it SelectedGun. And in most of the functions, the binary validates, if the guns are allocated correctly by checking the gunInUse array. If a gun is “dropped”, it will get free’d and the corresponding entry in gunInUse will be set to 0.
But this won’t reset the SelectedGun, so we could have a gun selected, which just has been free’d. To make this even better, the UseGun method doesn’t check the gunInUse array, to see if the currently selected gun is still allocated, so we have an use-after-free, just wating to get exploited.
This function gives us a possible leak with the name of the gun (which might be free’d and thus containing a heap pointer), and as soon as we’ll be able to overwrite the vtable pointer, it will also execute our payload.
So let’s first leak a heap address, which we’ll need to calculate the address, where our fake vtable will be stored. For this we’ll create a small gun (fastbin size).
This will buy a gun, initializing the gun to
Renaming the gun will free the memory for the name, putting it in the fastbin list, and allocate another memory area for the new name
Dropping the gun will then free the new name also, putting this one in the fastbin list, and the address of the previous fastbin in it’s FD pointer.
Since this gun is still selected, we can still use it, which will show it’s name, containing the address of the old name on the heap. So we have a heap address to start with. But to calculate the needed libc offsets, we’d need a leak for a libc address also.
Well, we can get it the same way, just allocating a gun with a bigger name this time, so it won’t be put in fastbin list. Thus we’ll get a FD pointer to main_arena when dropping this one.
Now let’s create a fake vtable, which contains something more useful (for us) than BIU~. A function pointer to a magic gadget, that opens a shell might be more helpful:
HEAPDEST will contain the address, where this vtable is stored on the heap (Name + 4 bytes). So, all we’ll have to do, is to create a gun, which uses this vtable. Again the use-after-free will help us with this.
The size of a gun class is 20 bytes. If we allocate 3 guns with a name 32 bytes long, each one will allocate 20 bytes for itself and 32 bytes for its name.
When we now free (drop) those guns, the gun classes will be put in fastbinsY[1], while the freed name chunks will be put into fastbinsY[3].
Now allocating a gun with a name only 16 bytes long, will result in malloc using the chunk Gun3 to allocate the memory for the new gun, and chunk Gun2 for the name of our new gun, since its size also matches that of fastbinsY[1].
Thus creating this new gun will result in overwriting the class information of gun 2 with the name of our new gun and with it, its precious vtable ptr.
With this we can overwrite it with the address of our previously created fake vtable, which contains the address of our magic gadget.
Sine gun 2 is still selected, all that’s left is to shoot with it, which will then use our fake vtable to calculate the function address to call, which happens to be our magic gadget.