Dragon is a 75 point pwnable from pwnable.kr. That being said, if you are afraid of spoilers, DON’T continue! Try out the challenge for yourself! The binary is a 32 bit dynamically linked elf that still has most of its symbols left in tack. It takes a bit of reverse engineering to actually figure out how to exploit it though.
You can start the challenge by connecting with netcat to pwnable.kr on port 9004. The premise of the challenge is pretty simple, your older brother has designed an RPG game that’s impossible to win. The objective? Kill the dragon, pwn the box, and get the flag.
With ASLR and DEP enabled it was clear that this challenge was meant to be solved in a very particular way. After playing with the application for a bit, I loaded the binary into IDA.
The actual main function of the program was relatively uninteresting until it makes a call to the PlayGame function seen above. This is the same dialogue that asks you to select 1 if you would like to play as a knight and select 2 if you would like to play as a priest. The disassembly reveals a third option (3) that allows you to access a secret level!
Unfortunately to actually access the secret level, you need a password and that password is not found anywhere in the binary. The only interesting part about the secret level is that it makes a call to system.
Assuming that at some point we find a vulnerability that gives us control of the instructional pointer, we should be able to jump directly to the address 0x08048DBF. This should work since the location of the text segment shouldn’t relocate because the application is compiled without PIE.
So how do we actually kill this dragon? The answer lies in the data structures used to keep track of the dragon’s information. At the beginning of the PlayGame function, the game allocates memory for two structs on the heap.
The first 16 byte struct is used to keep track of the player’s information. The values in this struct are determined by weather the player chose a knight class or a priest class. These two classes have different attacks and different amounts of health.
The priest has a unique attack where he can use a magic shield to avoid taking any damage. This shield does have a certain amount of mana and needs to be recharged after a couple of uses.
The second struct is a bit more interesting, it’s used to hold information about the dragon we are fighting.
1 2 |
|
There are two types of dragons a baby dragon and a mother dragon. The baby dragon has less health but deals more damage and has a greater life regeneration. The mama dragon has more health but a lower damage and lower life regeneration. The kind of dragon you get to fight is determined by a counter that alternates back and forth between baby dragon and mama dragon. This counter does not reset between rounds.
One thing you may have noticed in the picture above is that the dragon’s health is stored in a single byte at eax+8. This means that the dragon’s health can only be within the range of 0 to 127. A dragon is considered defeated when its health has turned 0.
Since it’s mathematically impossible to kill the dragon by actually fighting it, the solution to killing the dragon is to not fight it at all! Specifically, to defeat the dragon we need to wait until the mama dragon regenerates enough health so that the mother dragon’s health overflows from 124 to 0.
Fantastic! So now we know how to defeat the dragon! But how does defeating the dragon help us get a shell? Defeating the dragon causes a use after free condition which can be exploited to gain control of the instructional pointer.
Normally, when the dragon wins, the dragon struct is first freed and the “I defeated a dragon condition” is returned as false, or zero, in the register eax. However if eax returns 1 like in the green box, the dragon struct is used later on in the code even though its already been freed.
In some cases a mistake like this might only cause a crash, in this case though we make a call to malloc again that is the same size as the dragon struct that we just freed. Due to the way binning works on linux, the new malloc should be allocated in the block we just freed. This new malloc is user controlled and is meant to store the player’s name. If we enter the player’s name as an address then the call eax, which used to have a function pointer, will actually point to wherever we want it to!
Let’s use some clever python to point the instructional pointer back inside of the secret function and get a shell on the remote machine.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|
Finally, lets enjoy the spoils of victory by piping the script through ncat! What can I say, it used to be a dragon once but then it took a use after free to the knee.