200 points
Category: Binary Exploitation
Tags : #picoCTF 2023
Binary Exploitation
game
Break the game and get the flag. Additional details will be available after launching your challenge instance.
Скачиваем предложенный экзешник игры для анализа. Запускаем Гидру
Декомпилируем. Расставляем удобные нам имена локальных переменных, чтобы код читался проще
Замечаем функцию win
, которая выводит флаг - ищем референс на неё и понимаем, что она нигде не вызывается.
Смотрим функцию move_player
и видим не упомянутую команду l<символ>
которая меняет символ, представляющий игрока на поле с @
на любой введенный. Значит, мы можем поменять один байт внутри массива чаров, представляющего карту.
Замечаем, что на "карту"(массив чаров длинной 2700) игрок выставляется следующей строчкой кода по анализу Гидры(undefined имеет длину 1 байт):
*(undefined *)(*playerCoordY * 0x5a + map + playerCoordY[1]) = player_tile;
Это значит, что мы можем ходить по памяти по одному байту за пределами карты, т.к. выражение в скобках - int
и он приводится потом к указателю на однобайтовый тип данных, разыменовав который мы устанавливаем значок игрока на карту
Замечаем, что адрес возврата из move_player
в main
равен 08049704, а адрес начала функции win
равен 0804975d, которые как раз различаются на один байт. А мы как раз обладаем возможностью изменить один байт в любой точки памяти из-за способа адресации, выбранного создателем игры.
Значение 5d равно символу ']'
.
Запускаем удобный дебагер - я воспользовался gdb+gef, ставим брейкпоинт на move_player
и смотрим, как выглядит стек.
Смещение от начала массива, представляющего карту до первого байта адреса возврата из move_player
в main равно 39. Это значит, что нам надо от левого верхнего угла 39 раз повторить ход влево (от левого - т.к. мы ползем вверх по стеку - в сторону уменьшения адресов)
При попытке просто проехаться на 39 влево мы цепляем важное и все крашит - поэтому мы делаем шаг вверх, чтобы "загрязнять" своими похождениями мусорные байты(ход вверх сместил нас на 90 байт вверх по стеку).
После 39 влево делаем один раз вниз (получаем минус 90 байт, которые мы прибавили ранее ходом вверх) - и попадаем на нужный байт. При установке на него его значение заменяется значком нашего игрока - поэтому предварительно выполняем команду l]
Тестируем локально. Радуемся - все получилось - мы подменили адрес возврата на адрес начала win
и вызвали её
Запускаем дистанционно - ничего не работает.
Почему-то буфер не сбрасывается, если мы приходим в самое начало функции win
0804975d, поэтому пробуем последующие адреса вида 080497xx (т.к. мы можем менять только один байт), пока не получится
Сработало на адресе 08049779, а 0x79
- это символ y
Так мы и получили нашу полезную нагрузку для этого задания и флаг
Последовательность ввода для получения флага:
ly
aaaa
wwwww
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
s