This weekend I have been in Albacete (Spain) in the 'Navaja Negra Conference'. It was an amazing event! I had a great time there watching the conferences and playing the CTF. I recommend you go to the conference next year, really, it is amazing!! Congratulations to the organization.
In this post, I will tell you how I solved the 'Pokedex' challenge (category: exploiting).
As you can see, the provided binary is a Linux binary that allows us to register Pokemons (it is a Pokedex :P). It allows us to add, edit, delete and view Pokemons. With this description, we can start thinking it is a classic Use-after-free challenge, and we are right. If we open the binary in a dissembler we can see how it works, and if we just try to delete a Pokemon and view/edit it after that, we will finally confirm that it is a classic use-after-free. I will exploit the use-after-free, but there is a different way to solve it by exploiting the double free vulnerability (you can delete a Pokemon many times..).
A Pokemon is composed by 5 fields: ID, Name, Height, Weight and Power. The most important field is the name, which is a C string (char*). It is important because we need to use this string to modify the struct of a previously deleted (freed) Pokemon. The size of the Pokemon structure is 24 bytes. Checking how the memory is reserved, we can see that every time we create a Pokemon, it reservers 24 bytes for the struct and then X more bytes for the string.
Pokemon's structure in green, pointer to the name in red, name immediately after the struct |
In order to allocate a string in the space of a freed Pokemon's structure, we have to generate a hole in the following way:
1. Create 4 Pokemons with a 24 bytes long name (with IDs: 0,1,2,3).
2. Delete/Free the 2 Pokemons in the middle (in order to create a hole) (IDs: 1,2)
3. Create a new Pokemon (ID: 4) with a >72 bytes long name (in my case I used a 100 bytes long name)
4. Create a new Pokemon (ID: 5) with a 24 bytes long name (it will be allocated in one of the previous freed Pokemon's structures).
With this, we can edit the name of the last created Pokemon to modify the structure of the last allocated and freed Pokemon (ID: 2). We need to create a Pokemon (ID: 4) with a name longer than 48 bytes because we want to keep the hole for the new structure (ID: 5) and the name.
The memory will change as follows:
After creating the 4 initial Pokemons:
[--- 24 bytes ---] (struct Pokemon 0)
[--- 24 bytes ---] (name Pokemon 0)
[--- 24 bytes ---] (struct Pokemon 1)
[--- 24 bytes ---] (name Pokemon 1)
[--- 24 bytes ---] (struct Pokemon 2)
[--- 24 bytes ---] (name Pokemon 2)
[--- 24 bytes ---] (struct Pokemon 3)
[--- 24 bytes ---] (name Pokemon 3)
After deleting the 2 Pokemons in the middle:
[--- 24 bytes ---] (struct Pokemon 0)
[--- 24 bytes ---] (name Pokemon 0)
[--- 24 bytes ---] (struct Pokemon 1) FREE
[--- 24 bytes ---] (name Pokemon 1) FREE
[--- 24 bytes ---] (struct Pokemon 2) FREE
[--- 24 bytes ---] (name Pokemon 2) FREE
[--- 24 bytes ---] (struct Pokemon 3)
[--- 24 bytes ---] (name Pokemon 3)
After creating the new Pokemon:
[--- 24 bytes ---] (struct Pokemon 0)
[--- 24 bytes ---] (name Pokemon 0)
[--- 24 bytes ---] (struct Pokemon 4)
[--- 24 bytes ---] (name Pokemon 1) FREE
[--- 24 bytes ---] (struct Pokemon 2) FREE
[--- 24 bytes ---] (name Pokemon 2) FREE
[--- 24 bytes ---] (struct Pokemon 3)
[--- 24 bytes ---] (name Pokemon 3)
[--- 100 bytes ---] (name Pokemon 4)
After creating the new Pokemon:
[--- 24 bytes ---] (struct Pokemon 0)
[--- 24 bytes ---] (name Pokemon 0)
[--- 24 bytes ---] (struct Pokemon 4)
[--- 24 bytes ---] (struct Pokemon 5)
[--- 24 bytes ---] (name Pokemon 5) (and struct Pokemon 2 still points to here and can be used)
[--- 24 bytes ---] (name Pokemon 2) FREE
[--- 24 bytes ---] (struct Pokemon 3)
[--- 24 bytes ---] (name Pokemon 3)
[--- 100 bytes ---] (name Pokemon 4)
Once we are here, we just have to edit the Pokemon 5 to modify the name, overwritting the struct of the Pokemon 2 and replacing the pointer to the Pokemon's name with a pointer we want. In my case, I replaced the pointer with the pointer of atoi@got (which is, basically, a table to know where the imported funcions are). At this point, we can use the "view" function to view the Pokemon 2 data, leaking the address of atoi. Once leaked, we can use this address to calculate the system address (the provided the libc.so binary, so we can calculate the offset and use it to calculate the address of system using the leaked address of atoi). Once we calculate the address of system, we can use the 'modify' function for the Pokemon 2 to modify the address of atoi with the address of system. The next time it uses the atoi function to process the selected option in the menu, it will call system. So, finally, we can write "sh" or "bash" to get a nicer shell and read the flag from the file "flag.txt". :D
nn8ed{Tc4che_c0rrupt10n_FTW!}
You can see the full exploit here: https://gist.github.com/segura2010/0101bb7d53460001dac02018b41bff1c
I hope you enjoyed this post!
See you in the next post and, hopefully in the next edition of Navaja Negra Conference! :D