-
Notifications
You must be signed in to change notification settings - Fork 4
Нефть и монстры
- Категория: Pwn
- Стоимость: 450
- Автор: Владимир Черепанов
- Репозиторий
А раньше всё было хорошо. Не было этих безумных монстров, от которых с каждым днём всё труднее скрыться. В воздухе не витал незаметный запах радиации, а на небе, да-да, на этом самом небе, всегда светило яркое солнце.
Ещё до катастрофы один мой знакомый владел парочкой нефтяных скважин, которые он случайно обнаружил, когда строил свою ферму. Он жутко обрадовался, конечно, и я бы обрадовался при таком раскладе-то. Ну так вот, он был уверен, что найдёт ещё скважины, поэтому написал программу, чтобы всегда знать, сколько нефти на какой скважине у него имеется.
С нефтью ещё ладно, куда ни шло, а вот программистом он был, мягко сказать, нехорошим. Рассказывали, что через эту самую его программу можно как-то управлять скважинами, и даже получалось у кого-то. Сейчас эти скважины даром никому не сдались, зато на его сервере можно найти кое-что полезное...
Чтобы получить флаг, нужно запустить getflag и передать ему токен первым аргументом.
Ваш токен: 31b65c888ea77fbbd200c518041150ab
Пример: ./getflag 31b65c888ea77fbbd200c518041150ab
nc oil.contest.qctf.ru 20001
oil
oil.c
- бинарник и его исходный код на языке C
- адрес сервера с портом, который слушает бинарник
!!! Рекомендуется сначала почитать этот разбор, так будет проще понять ход решения.
Welcome to Oil Platform Manager!
Select your action:
1. Show current oil reserve on the platform.
2. Update information about platform.
3. Export all information.
4. Exit.
Your choice:
Вот так приветствует нас программа, которую нам предстоит эксплуатировать для получения флага.
Авторы дали нам исходник, давайте же почитаем его! В начале зачем-то написана команда для компиляции:
=== HOW 2 COMPILE ===
gcc -fno-stack-protector -mpreferred-stack-boundary=2 -z execstack -m32 oil.c -o oil
=== OK NOW USE IT ===
Как-то многовато флагов... Должно быть, это очень уязвимый бинарник.
- -fno-stack-protector отключает так называемые canaries - защиту стека от перезаписи (при виде такого сразу вспоминаем о переполнении буфера)
- -z execstack помечает стек как исполняемый - прямая дорога к шеллкоду
- -m32 компилирует бинарник под 32-битные системы (значит, внутри него 4-байтные указатели)
- -mpreferred-stack-boundary=2 выравнивает стек по 4-м байтам (по умолчанию стек выравнивается по 16 байтам)
Бинарник позволяет нам хранить информацию о нефтяных вышках, а именно: сколько нефти есть на какой-либо вышке. Диапазон хранимого значения - один байт (беззнаковое число от 0 до 255). Программа может вывести всю имеющуюся информацию до первого нулевого символа, то есть строку, которая начинается в начале массива.
Первая странность: при записи в массив производится неправильная проверка. То есть можно записать что-то в следующий за последним (1025-ый) элемент массива (его индекс как раз и равен 1024).
scanf("%d", &number);
if (number < 0 || number > 1024) {
printf("Incorrect number of the platform!\n");
continue;
}
Вторая странность: посмотрим в коде как вводится число:
scanf("%lld", &platforms[number]);
scanf считывает 64-битное число и кладёт его на место одного байта. Так как языку C без разницы, какой тип у элементов массива, к которому ведёт указатель, то мы получаем перезапись целых 8 байтов в массиве.
А что будет, если записать что-то в последнюю ячейку массива? Верно, мы перезапишем то, что лежит за ним, а именно: BP и RET (своим 8-байтовым числом мы перезаписываем два 4-байтовых указателя). Но мы не достаём до конца RET из последней ячейки массива, тут нам и помогает сломанный индекс: при записи в 1025-ю ячейку массива мы можем перезаписать RET полностью... Или нет?
Возникает проблема, при которой число, большее, чем 0x7FFFFFFF не записывается. Стоит вспомнить про знаковые числа и понять, что если записать отрицательное число, то в шестнадцатеричном виде мы получим число, начинающееся с 0xFF..., что нам и нужно.
Уже ясен алгоритм: записываем байты шеллкода в массив, перезаписываем RET на начало... Стоп. А как узнать адрес массива?
Тут нам поможет функция, печатающая массив в виде строки. Нам достаточно просто заполнить его чем-то (ненулевыми байтами), тогда мы сможем слить BP (1025-1028 байты строки). А от него уже легко посчитать смещение до нашего шеллкода.
Итоговый эксплоит можно найти в репозитории, вот краткий алгоритм его работы:
- заполняем первые байты массива шеллкодом
- оставшиеся байты массива заполняем ненулевым мусором (чтобы дотянуться до BP)
- для сокращения числа запросов можно отправлять не по одному байту, а сразу по 8
- просим программу напечатать строку, достаём оттуда BP, находим адрес начала массива
- создаём число, которым будем перезаписывать (сдвигаем на 32 бита адрес массива, вычитаем получившееся число из
2^64-1
) - отправляем
4
и выходим из функции manage, передавая управление на шеллкод