Codeby Games Writeup: "Странный PWN?" (codeby.games)

  • Название: Странный PWN?
  • Категория: pwn
  • Платформа: codeby.games
Нас встречает одинокий файлик task.elf, радующий весьма разносторонним функционалом:

1721054239698.png


Конечно, первые 2 пункта выглядят интересно, но что-то подсказывает, что интересней всего будет выбрать пункт 3:

1721054342030.png


У нас появился какой-то mood, явно не соответствующий реальному, и программа зажилила нам флаг. Так что ковырнём её с помощью IDA:

1721054516550.png


А тут всё на ладони, под каждую опцию есть своя функция, вызываемая в зависимости от введённого нами символа. И нас, в первую очередь, интересует функция get_flag():

1721054955662.png


Здесь алгоритм следующий: сначала мы вводим ответ на вопрос "Do you want a flag?", после переменная change_mood сравнивается с единицей, и, если проверка пройдена, то нам дают флаг. Но что интересно: эта переменная нигде не меняется на единицу, а значит - нам самим придётся до неё добраться ручками. Вот её отображение в памяти:

1721059151792.png


А вот переменная, которая соседничает с ней:

1721059182424.png


И эта str - та самая переменная, которая используется для хранения ответа на вопрос "Do you want a flag?", она указана в fgets (13-я строка, 2 картинки назад). Между этими метками в секции .bss никаких больше нет, но обратите на адреса этих меток: 0x4AA0C0 для str и 0x4AB0B0 для change_mood. Посчитайте разность, и вы получите число 4080 - именно столько символов нам нужно занести в качестве ответа, чтобы следующий перезаписывал change_mood. Попробуем пронести строку вида "a" * 4080 + "b", чтобы увидеть, возымеет ли эффект эта попытка. Подобную строку легко написать на питоне:

1721059621641.png


Дебажить это дело я буду из-под WSL, в IDA соответствующий пункт "Remote Linux Debugger" и небольшая настройка с IP и портом. Копируем получившуюся строку и в процессе дебаггинга вставляем это дело на место str:

1721060736211.png


А теперь глянем в память:

1721060824539.png


Сработало! Теперь change_mood хранит в себе символ 'b', или, десятеричным числом, 98. Но нам-то нужно, чтобы там была единица! А если посмотреть в , то chr(1) == CTRL+A (да-да, такие комбинации - отдельные символы). Заметьте, если попытаться ввести питоновский "\x01", то это лишь будут 4 соответствующих символа. Поэтому воспользуемся методом, который описан - вставим 4080 символов а, а затем... Просто нажмём CTRL+A!

1721061242899.png


И снова сработало! Теперь всё, что нам осталось сделать - подключиться через netcat и проделать всё то же самое:

1721061361627.png


Получилось, таск решён! И, казалось бы, всё круто - но я не указал вам один момент. Внимательного читателя этот райтап смутил одним моментом:

1721061479041.png


В fgets стоит ограничение на 4000 символов! Тем не менее, нам удалось пробросить аж 4081 символ... Странный, какой-то PWN, не находите?

Когда я увидел это ограничение, я, конечно, огорчился, поняв, что мне не хватает 81 символа, но всё же попробовал, можно сказать - попытал удачу. И каково же было моё удивление, когда сработало!

А оказалось всё интересно: спасибо @yetiraki - он подсказал, что fgets оказался самописной функцией. И действительно - второй аргумент в её псевдокоде мелькает лишь в проверке на неотрицательность, и больше нигде. Полагаю, как такового ограничения по символам эта функция вообще не имеет...

Кстати, после того, как зарешал, придумал, как можно запускать программу, не копируя пейлоад в 4000 символов:

python3 -c "print('3\n' + 'a'*4080 + '\x01')" | ./task.elf

1721066686012.png


Будет вполне удобно решать у себя на машине!

Штош, надеюсь, этот небольшой райтап помог вам в нашей нелёгкой стезе.

Удачного пывына!

made 4 @rev_with_da_boys
 
Последнее редактирование:
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!