Изучение PWN #8

Изучение PWN #8

Михаил

Полезная информация по ИБ и разборы задачек CTF.

Предыдущая задача - Изучение PWN #7.

Мы подошли к изучению темы уязвимости форматных строк. Первое вводное задание - Format Zero.

Исходники:

int main(int argc, char **argv) {
 struct {
  char dest[32];
  volatile int changeme;
 } locals;
 char buffer[16];

 printf("%s\n", BANNER);

 if (fgets(buffer, sizeof(buffer) - 1, stdin) == NULL) {
  errx(1, "Unable to get buffer");
 }
 buffer[15] = 0;

 locals.changeme = 0;

 sprintf(locals.dest, buffer);

 if (locals.changeme != 0) {
  puts("Well done, the 'changeme' variable has been changed!");
 } else {
  puts(
    "Uh oh, 'changeme' has not yet been changed. Would you like to try "
    "again?");
 }
 exit(0);
}

После беглого изучения исходного кода понимаем, что нам нужно изменить значение переменной changeme, которая определяется в структуре locals после массива dest. Посмотрим, на что мы можем повлиять своим вводом. Видим, что пользовательский ввод записывается в массив buffer типа char размером 16, при этом последний символ заменяется на 0. После чего используется функция sprintf.

Попробуем для начала проверить, что произойдет в памяти программы, если мы попробуем переполнить буфер, как делали в заданиях прошлого раздела. 

Для наглядности, я буду использовать IDA на Windows и сервер IDA linux_server64 на Kali.

Открываем файл format-zero (предварительно скопировав его с виртуалки qemu, чтобы у нас был скомпилированный с нужными флагами, отключающими защиту, файл) в IDA, переходим к декомпилированному коду (Tab) и ставим точку останова на функции sprintf:

Выбираем Remote Linux Debugger :

На Kali запускаем ./linux_server64 (файл находится в dbgsrv в папке Иды).

Указываем IP-адрес Kali:

Запускаем, после чего нужно в Kali выполнить ввод, попробуем ввести 32*"A":

В IDA наводим мышкой на s и видим, что в ней хранится только 14 букв "A". Сделаем шаг на одну строку в декомпилированном коде и увидим, что содержимое s скопировалось в v4. Переменная changeme, которая в декомпилированном коде называется v5 не перезаписалась.

Посмотрим адреса переменной v4 и v5 (двойной клик ЛКМ по переменной в декомпилированном коде):

v4 - 0x00007FFFFFFFDED0
v5 - 0x00007FFFFFFFDEF0

Посчитаем смещение:

0x00007FFFFFFFDEF0 - 0x00007FFFFFFFDED0 = 0x20 = 32

Таким образом, простое переполнение буфера вводом большого количества мусорных символом в данной задаче не сработает, так как наш ввод ограничен 14-ю символами. Посмотрим, что мы сможем сделать с функцией sprintf.

Функция sprintf в языке программирования C является одной из функций семейства стандартной библиотеки для форматированного вывода. Она используется для формирования строк с использованием форматных спецификаторов. Одним из основных рисков использования sprintf является потенциал переполнения буфера, так как функция не выполняет проверку размера выходного буфера. Это может привести к перезаписи соседних данных в памяти, что угрожает безопасности приложения.

Основные спецификаторы формата:

Если мы введем вместо обычной строки спецификатор формата, функция sprintf при копировании нашего ввода из buffer запишет в locals.dest данные из стека, из которого она была вызвана, отформатировав их согласно спецификатору. Таким образом, при вводе спецификатора %x мы передаем только два символа, но функция sprintf воспримет это как формат и выведет данные из стека в hex-формате, то есть запишет 8 символов в locals.dest. Давайте проверим это, запускаем процесс отладки, в Kali вводим %x.

В IDA можем посмотреть, что хранится в buffer:

Сделаем шаг, чтобы выполнилась функция sprintf и посмотрим, что записалось в locals.dest ( v4 в декомпилированном коде):

Видим, что записалось 8 байт, которые представляют собой адрес начала buffer, а также 9-ым байтом дописался байт 0x0A, обозначающий символ возврата каретки. Получается, что для того, чтобы перезаписать переменную changeme нам необходимо ввести 4 раза %x - %x%x%x%x.

Запускаем отладку заново, вводим в Kali - %x%x%x%x, делаем шаг в отладчике и смотрим память:

Видим, что все произошло так, как мы и предполагали и по адресу 0x00007FFFFFFFDEF0, по которому расположено значение переменной changeme записался символ возврата каретки 0x0A.

P.s. это не единственный способ решения, можно также передать, например, `%32d`, что запишет в `locals.dest` 32 десятичные цифры, и т.д.


Report Page