This Write-Up is about de first pwn challenge of unionctf: babyrarf. It was a really easy challenge with a stack based buffer overflow. The source code was provided so, no need to reverse the binary :).
puts("Congratulations! You may now declare yourself the winner:\n"); fgets(player.name, 48, stdin); return0; }
It’s basically some kind of game, we have to win a lot of times to display Congratulations! You may now declare yourself the winner. And when we reach this part we can trigger a buffer overflow with a call to fgets (fgets(player.name, 48, stdin);). We notice too the get_shell function (maybe we will have to jump on ?).
So main_ret_addr minus player.name is equal to: 0x00007fffffffdf48 - 0x00007fffffffdf20 = 40 . So we have basically a padding of 40 bytes before the return address, and according to the last fgets, we can only enter 48 bytes. We can so overwrite only the return address.
We can see, the binary is PIE based, so in order to jump on get_shell we need to leak some binary’s functions. To do so we can mind the code of choose_attack function:
attack choose_attack(){ attack a; int id; /* Some print stuff */ id = read_int(); // It is readinf the type of weapons we want /* Here it is handling properly dammage and weapon type */
elseif (id == 4){ if (score == 0){ puts("l0zers don't get cr0wns\n"); } else{ a.id = 4; a.dmg = 40; } } else{ puts("Please select a valid attack next time\n"); a.id = 0; a.dmg = 0; } return a; }
The interesting part is that when our score is zero and that we choose the fourth weapon, the id et dmg fields are not initialized. And so it’s returning a non initialized struct that it will print just next in the main function:
Uninitialized structures are very useful to obtain leaks because their content is depending of the ancient stackframes which have stored local variables and especially useful pointers. And when we try to leak these datas, we can see that a.id displays the address of __lib_csu_init. So we just need to leak the address of __lib_csu_init to compute the base address of the binary and so the address of get_shell.
We can compute compute the value of rand to avoid bruteforce, but I’ve choosen to do not. So while it does not print l0zers don't get cr0wns, I’m sending 4 for cr0wn and when it is teh case I get my leak of the csu and I compute the base address. When It’s done I’m sending 1 because it sounds more speed and I wait to win. And when I won I can trigger the buffer overflow and jmp on get_shell.