9447 CTF 2015 Write-Up: Cards
We have released a new card game! If you win, you get a flag. Check it out at cards-6xvx9tsi.9447.plumbing port 9447
Challenge Overview
This is a simple game of cards which we cannot win. At The start of the game we can enter up to 52 numbers, which will be the values of our cards. Our opponent uses our same deck.
Then we play as many round as there are cards in the deck. In a round we choose a card from our deck, that card is flagged as “used” and will be disabled in the next rounds. Our opponent will choose his card, and who has the highest card value wins the round. At the end of the game if we won more rounds than our opponent, we get the flag.
Unfortunately our opponent is smart and always choose the next higher card in the sorted deck. So we can win only when we choose our highest card, all the other rounds are ties or losses.
We must find another way to win :D
Vulnerability
In the shuffle function, for every card, the value is used to determine one of the two indices of the cards to swap.
In particular the sign of val
is flipped if negative, and then normalized to mod size
before being used as an index.
Too bad in two’s complement the least negative number cannot be made positive and remain unchanged if negated. In C the modulus of a negative number is still negative, that means the calculated index is negative and enable us to swap a value from outside the deck array.
To control the index to swap we must change the size of the deck.
The least negative number for a long long int
is -9223372036854775808, and the deck can range from 1 to 52 cards.
Possible (deck_size, index) pairs are:
So exploiting the shuffle function, we can swap some areas outside the deck array, with values we control. Since we can play multiple round and the shuffle function gets called once per round, we can swap multiple qwords from the stack.
The plan is to swap the ret address of shuffle with the address of printFlag, problem is, ASRL is active and the binary is PIE, so first we must leak an address on the text section, and compute the printFlag address from that.
Memory near RSP
in shuffle function:
Row 0056
is the start of the deck array, 0048
is the return address of shuffle, 0032
pointer to _start function.
Luckily for us, the last pointer can be consistently found in that position and can be accessed by deck[-3]
(-3 index is obtained by a deck size of 5).
After the shuffle, the actual game begin, our deck gets printed and we can retrieve the pointer.
On the next game we choose a deck with seven elements (so we can swap the address in deck[-1] which is shuffle’s ret address), and we put the address of printFlag as an element.
Script
Launching the attack…
\o/