navigation system (pwn_1 writeup) [AeroCTF]
m4dratОписание: "Наши разработчики создают навигационную панель, которая должна помочь работникам навигационного центра хранит навигационные отчёты для каждой станции и добавлять их."
После первого же запуска делаем вывод, что решение этого таска начнется с IDA. Открываем, и видим нечто похожее:

Поняв с чем сравнивается 'login' и 'password' (просто посмотрев в .data сегмент) успешно завершаем первую часть процесса авторизации. Далее нас ждет более интересная момент, а именно генерация и проверка 'OTPcode':

На самом деле этот процесс тоже довольно тривиальный, но давайте разберем его подробнее. В строке 7 производится вызов функции srand с параметрами в виде текущего времени в секундах (time_), а также двух разыменнованных указателей на login и password. Так как переменные log и pass представляют из себя указатель на массив типа char, то их разыменовывание приведет к тому, что мы получим по первому символу от логина и пароля. Далее следует вызов rand(), которая вернет число основываясь на srand. Так как мы полностью знаем алгоритм, есть возможность его довольно легко реализовать в питоне с помощью модуля ctypes:


Теперь имея возможность без проблем генерировать данные для входы приступим к самому пывну. Основным функционалом программы является возможность вызова readLastReport() и setStation(). Функция readLastReport() позволяет прочитать файл report.txt, в случае, если глобальная (.bss сегмент) переменная flag отличается от '0':

Оставшаяся функция setStation():

Она является более интересной как раз из-за вызова 'printf(buf)'. Если вы знакомы с уязвимостью форматной строки, то сразу поймете что здесь происходит. Ежели нет, то вот неплохая статья format_string_exploits. Вкратце уязвимость позволяет получить два примитива read и write посредством манипулирования как раз форматной строкой ('%x', '%s', ...), в данном случае мы будем использовать ее как write примитив. Функция printf обладает многими спецификаторами. Вот некоторые из них:

Обратите внимание на последний спецификатор '%n' он позволяет записать какое-либо значение по произвольному адресу. Теперь идея понятна записать в переменную flag (0x0804C058) что-либо. Как этого добиться? Довольно просто. Необходимо передать в функцию printf адрес flag позже на нее сослаться и записать туда любое число. Воспользуемся gdb для поиска места на стеке, куда кладется параметр для уязвимого вызова функции printf (0x0804953E)

Он оказывается на стеке 7 по счету, т.е. является как-бы 7 параметром для printf() -> т.к. спецификатор '%n' пишет по адресу, который мы укажем необходимо сослаться на наш буфер, в котором будет лежать адрес переменной flag, но каким образом можно выбрать конкретный аргумент? Для этого существует спецификатор '$', именно он и позволяет выбрать конкретный аргумент:


Получаем план действий:
- Записать в буфер адрес переменной flag
- выбрать его со стека с помощью '$'
- Использовать '%n', чтобы записать по этому адресу число выведенных символов
После пары минут получаем форматную строку: "\x58\xc0\x04\x08%7$n", где первые четыре байта - адрес переменной flag, %7$ - указатель на адрес, а 'n' - format specifier. В итоге имеем полностью готовый эксплоит:

Полный код экплоита: exp
EN: TL;DR
- find vulnerable call to printf (in setStation())
- craft format string to write something to address of flag variable "\x58\xc0\x04\x08%7$n"
- read flag