judgement writeup
Сейчас попробуем с вами решить pwn таск под названием judgement c прошедших MMA CTF 2016!
Что же нам дано:
Нам дан файл
и ссылка на сервис:
Host : pwn1.chal.ctf.westerns.tokyo
Port : 31729
Начнем решать!
Тип файла сразу не определи. Посмотрим, что выдаст нам команда file на данный бинарный файл:
Значит перед нами 32-битный бинарный исполняемый ELF файл для Linux'a. Значит пришло время включать виртуалку и IDAPro!
Первое, что требуется для запуска данной нам программы - дать нужные привелегии командой:
$chmod +x judgement
Далее запустим наш бинарник:
Видим, что без файла flag.txt он не запустится. Создаем в той же директории файл flag.txt с содержимым FLAGFLAGFLAG.
Заного запускаем программу:
Программа требует от нас какого то ввода. Введем строку abcdef:
Вот мы уже догадываемся, что программа требует на ввод флаг, который вписан в файл flag.txt. Проверим нашу догадку:
Мы оказались правы!
Обобщим то, что мы пока узнали о программе:
1. Программа берет флаг из файла flag.txt
2. Принимает с нашего ввода строку.
3. Выводит эту строку.
4. Сравнивает флаг из файла и нашу строку.
5. Выводит результат сравнения.
Посмотрим внимательнее на второй и третий пункт. Ничего не напоминает?
Одной из наиболее распространенной уязвимостью является "фича" форматирования строки в С/С++, позволяющая получать значения из стека.
Подробнее об уязвимости
Форматирование строки обычно используют в функции printf и др. Наглядный пример:
#include <stdio.h>
int main(void)
{
printf("%d", 1234);
return 0;
}
Данный код выведет 1234 тк %d означает замену на десятичное число, которое было передано как второй аргумент.
Но если мы уберем второй аргумент из функции printf и заменим %d на %08x, то мы получим верхнее значение стека (08 означает преобразование значения в 4 байта тк в hex при переводе строки в стандартную каждый байт=символ занимает две char ячейки , например символ 'U' в hex -> str будет занимать две ячейки '55'.)
#include <stdio.h>
int main(void)
{
printf("%x %x %x");
return 0;
}
выведет нам первые три значения стека. Пример:
Но это только ссылки на ячейки памяти, а нам нужны их значения. Для этого есть конструкция %N$s, которая выводит строковое значение. Но об этом чуть позже.
Вернемся к задаче.
Протестируем уязвимость форматирования строки, введя несколько %x:
Замечания по решению:
• Узнать мы могли про эту уязвимость не только фаззингом, но и проанализируя программу в дизассемблере
• Последующее решение таска может быть заменено на обыкновенный перебор значений стека путем написания %1$s %2$s %3$s и тд. Но мы с вами проанализируем программу и по-умному найдем, какой номер в стеке нам нужен.
Меньше слов, больше дела!
Открываем наш исполняемый файл в IDAPro, переходим в главную (main) функцию и видим следующую картину:
Выделенная красная строка - брейкпоинт(пауза) на выводе сообщения об ожидании ввода флага.
Первое условие проверяет наш ввод на читабельные символы, и в обратном случае выводит следующее сообщение и завершается:
Нас это не сильно касается, так что переходим ко второму условию (в случае если наша строка имеет только читабельные символы):
В начале первого окна замечаем вывод строки, используя уже знакомую нам функцию printf().
call _printf
Далее у нас идет сравнение строки ввода и флага:
call _strcmp
Ну и соответственно в зависимости от этого два перехода на "Wrong flag" и "Correct flag".
Как мы ранее убедились нам интересен момент вывода нашей форматированной строки. Ставим breakpoint на _printf, перезапускаем программу но уже в отладчике IDAPro и вводим случайные значения.
!!!Не забываем что после первого брейкпоинта, выводящего приветствие, нам требуется что то вписать!!!
Вот мы достигли брейкпоинта _printf и видим следующее (некоторые окна свернуты для удобства):
В правом окне Stack view мы можем наблюдать нашу текущую позицию в стеке и последующие (которые ниже). Полистаем вниз и поищем "подозрительные" записи, содержащие в себе флаг. Вот нашли!
Проверим, два раза тыкнув на адрес ячейки (зеленый цвет - в данном случае 0804A0A0) и переведя взгляд на окно Hex-View-1 заметим ЭТО:
Значит мы опять все верно сделали и эта ячейка хранит в себе флаг!
Теперь вернемся к форматированию строки. Помните я сказал, что %N$s выведет строковую запись под номером N из стека? Так вот чтобы вывести флаг, нам потребуется посчитать, какой по счету в стеке является эта запись (отсчет идет от нашей текущей позиции). Эта позиция будет под номером 28. И чтобы программа вывела содержимое флага нужно ввести %28$s.
Cказано - сделано:) Тестируем:
Ура работает! Подключаемся к сервису, который был дан задании (для тех, кто не в курсе: на нем крутится такой же бинарник, но с флагом) и пробуем проэксплуатировать:
Успех, флаг получен:)
TWCTF{R3:l1f3_1n_4_pwn_w0rld_fr0m_z3r0}
P.S. Райтап в данной категории пишу впервые, так что рад услышать ваши замечания по нему в комментариях.
- Автор: drakylar
- Комментарии: 2
- Просмотры: 2829