Доброго времени суток посетители Codeby! Продолжаем знакомиться с уязвимостями форматных строк и с эксплойтостроением. В этой статье посмотрим что такое примитив записи и чтения - используя уязвимость форматной строки. В прошлой статье посвященной уязвимостям форматных строк, мы перезаписывали локальную переменную. На этот раз мы перезапишем глобальную переменную, которая находится со всем в другой области памяти. Поехали!!!
Описание ExploitMe
Этот уровень показывает, как строки формата могут использоваться для изменения произвольных областей памяти.
Советы
Исходный код
Решение
Рассмотрим исходный код программы. Есть две функции main() и vuln(). В свою очередь, главная функция main() вызывает функцию vuln(), Функция vuln() принимает строку, через аргумент из главной функции. В теле функции vuln() есть условие if которое проверяем значение переменной target. Если переменная target имеет любое значение кроме нуля, то условие считается истинным. И на экране должна отобразится строчка, которая говорит нам о том. что мы прошли задание. Сама переменная target является глобальной переменной.
В противоположность локальным переменным глобальные переменные видны всей программе и могут использоваться любым участком кода. Они хранят свои значения на протяжении всей работы программы. Глобальные переменные создаются путем объявления вне функции. К ним можно получить доступ в любом выражении, независимо от того, в какой функции находится данное выражение.
По мимо условия, есть функция printf() которая принимает строчку из аргумента.
Функция printf() записывает в stdout аргументы из списка arg-list под управлением строки, на которую указывает аргумент format.
Вот прототип этой функции
Глядя на код программы становится очевидно, что функция printf() - это уязвимая функция.
Более подробнее почитать про эту функцию можно
И так для начала узнаем адрес переменной target.
Запускаем objdump чтобы найти адрес глобальной переменной target.
и видим что наша глобальная переменная лежит по адресу "08049638".
Надо еще сказать, что глобальные и статические переменные хранятся в сегменте .data, а не инициализированные данные находятся в сегменте .bss. Наша переменная не имеет не какого значения, но при этом она является глобальной переменной. Компилятор положил нашу переменную в сегмент .bss для оптимизации.
Так же тут следует сказать, что искать какие то данные в программах которые весят очень много - довольно очень трудоёмкая задача. Поэтому лучшем способом будет это использовать objdump в месте с утилитой grep
На много проще и удобнее чем анализировать всё полотно целиком...
Используя уязвимости форматных строк мы можем напрямую записывать данные в произвольные адреса памяти. В прошлой статье. Я рассказывал об этом и приводил примеры: %х, %s, %n...
Команда форматирования «%n» записывает байты в адрес памяти, на который ссылается указатель.
Указатель, как и все остальные параметры, передается через стек.
Поскольку наш вход в printf() функцию хранится в стеке, так как он передается argv[1], у нас есть все необходимые компоненты для его использования.
Первое препятствие для эксплуатации заключается в том, что printf() не находится в том же фрейме стека, что и входная строка. Обычно это означает, что входная строка находится очень далеко от нашего текущего указателя стека. Сначала нам нужно найти, где находится начало нашей входной строки.
Удобно, что уязвимости форматных строк предоставляют нам очень простой способ чтения стека. Каждая команда форматирования %x выводит следующие 4 байта из стека и перемещает указатель стека вперед на ту же величину.
Так же надо сказать, что уязвимости форматных строк дают примитив (состояние) , как для чтения данных, так и для записи, что очень по себе ценно.
Посмотрим как это действует - приметив чтения
На выходе получили цепочку байт из стека... С помощью %x мы используем 2 байта, чтобы извлечь 4 байта из стека.
Если нужно больше то увеличим число
Всё просто.
Теперь найдем нашу входную строку 0x41414141 в стеке. Будем использовать метод известный как "stack popping".
Тут нету нашей строки. Значит она лежит дальше
Вот наша строка. Так же надо сказать, что наша строка формата также хранится в стеке.
Если запустить питон и проверить, то вот, что мы получим.
Теперь уменьшим значение.
Уменьшим еще на 5
А теперь примитив записи.
Теперь мы можем заменить «AAAAA» адресом target, а последний символ форматирования %x заменить на %n чтобы записать данные на адрес.
Отлично задачку можно считать решенной. На экране отобразилась строчка "you have modified the target " Уровень можно считать пройденным, так же мы познакомились с примитивом (состоянием) чтения (%x) и записи (%n) которые предоставляет уязвимость форматной строки. Так же следует сказать, что есть еще один примитив на выполнение. Тут сразу вспоминается Linux со своим RWX)))))))
На этом всё. Теперь можно переходить к следующему заданию
Описание ExploitMe
Этот уровень показывает, как строки формата могут использоваться для изменения произвольных областей памяти.
Советы
- objdump -t ваш друг, и ваша входная строка лежит далеко в стеке
Исходный код
C:
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int target;
void vuln(char *string)
{
printf(string);
if(target) {
printf("you have modified the target :)\n");
}
}
int main(int argc, char **argv)
{
vuln(argv[1]);
}
Рассмотрим исходный код программы. Есть две функции main() и vuln(). В свою очередь, главная функция main() вызывает функцию vuln(), Функция vuln() принимает строку, через аргумент из главной функции. В теле функции vuln() есть условие if которое проверяем значение переменной target. Если переменная target имеет любое значение кроме нуля, то условие считается истинным. И на экране должна отобразится строчка, которая говорит нам о том. что мы прошли задание. Сама переменная target является глобальной переменной.
В противоположность локальным переменным глобальные переменные видны всей программе и могут использоваться любым участком кода. Они хранят свои значения на протяжении всей работы программы. Глобальные переменные создаются путем объявления вне функции. К ним можно получить доступ в любом выражении, независимо от того, в какой функции находится данное выражение.
По мимо условия, есть функция printf() которая принимает строчку из аргумента.
Функция printf() записывает в stdout аргументы из списка arg-list под управлением строки, на которую указывает аргумент format.
Вот прототип этой функции
C:
int printf(const char *format, arg-list)
Глядя на код программы становится очевидно, что функция printf() - это уязвимая функция.
Более подробнее почитать про эту функцию можно
Ссылка скрыта от гостей
.И так для начала узнаем адрес переменной target.
Запускаем objdump чтобы найти адрес глобальной переменной target.
Код:
objdump -t format1
и видим что наша глобальная переменная лежит по адресу "08049638".
Надо еще сказать, что глобальные и статические переменные хранятся в сегменте .data, а не инициализированные данные находятся в сегменте .bss. Наша переменная не имеет не какого значения, но при этом она является глобальной переменной. Компилятор положил нашу переменную в сегмент .bss для оптимизации.
Так же тут следует сказать, что искать какие то данные в программах которые весят очень много - довольно очень трудоёмкая задача. Поэтому лучшем способом будет это использовать objdump в месте с утилитой grep
Код:
objdump -t format1 | grep "target"
На много проще и удобнее чем анализировать всё полотно целиком...
Используя уязвимости форматных строк мы можем напрямую записывать данные в произвольные адреса памяти. В прошлой статье. Я рассказывал об этом и приводил примеры: %х, %s, %n...
Команда форматирования «%n» записывает байты в адрес памяти, на который ссылается указатель.
Указатель, как и все остальные параметры, передается через стек.
Поскольку наш вход в printf() функцию хранится в стеке, так как он передается argv[1], у нас есть все необходимые компоненты для его использования.
Первое препятствие для эксплуатации заключается в том, что printf() не находится в том же фрейме стека, что и входная строка. Обычно это означает, что входная строка находится очень далеко от нашего текущего указателя стека. Сначала нам нужно найти, где находится начало нашей входной строки.
Удобно, что уязвимости форматных строк предоставляют нам очень простой способ чтения стека. Каждая команда форматирования %x выводит следующие 4 байта из стека и перемещает указатель стека вперед на ту же величину.
Так же надо сказать, что уязвимости форматных строк дают примитив (состояние) , как для чтения данных, так и для записи, что очень по себе ценно.
Посмотрим как это действует - приметив чтения
Код:
./format1 $(python -c "print '%x.'*10")
На выходе получили цепочку байт из стека... С помощью %x мы используем 2 байта, чтобы извлечь 4 байта из стека.
Если нужно больше то увеличим число
Код:
./format1 $(python -c "print '%x.'*100")
Всё просто.
Теперь найдем нашу входную строку 0x41414141 в стеке. Будем использовать метод известный как "stack popping".
Код:
./format1 $(python -c "print 'AAAAA' + '%x.' * 100 + '%x'")
Тут нету нашей строки. Значит она лежит дальше
Код:
./format1 $(python -c "print 'AAAAA' + '%x.' * 150 + '%x'")
Вот наша строка. Так же надо сказать, что наша строка формата также хранится в стеке.
Если запустить питон и проверить, то вот, что мы получим.
Код:
"2e782541".decode('hex')
"252e7825".decode('hex')
"78252e78".decode('hex')
Теперь уменьшим значение.
Код:
./format1 $(python -c "print 'AAAAA' + '%x.' * 130 + '%x'")
Уменьшим еще на 5
Код:
./format1 $(python -c "print 'AAAAA' + '%x.' * 125 + '%x'")
А теперь примитив записи.
Теперь мы можем заменить «AAAAA» адресом target, а последний символ форматирования %x заменить на %n чтобы записать данные на адрес.
Код:
./format1 $(python -c "from struct import pack; target=pack('I',0x08049638);print target + '%x.'*125 +'%n'")
Отлично задачку можно считать решенной. На экране отобразилась строчка "you have modified the target " Уровень можно считать пройденным, так же мы познакомились с примитивом (состоянием) чтения (%x) и записи (%n) которые предоставляет уязвимость форматной строки. Так же следует сказать, что есть еще один примитив на выполнение. Тут сразу вспоминается Linux со своим RWX)))))))
На этом всё. Теперь можно переходить к следующему заданию
Ссылка скрыта от гостей
. До скорых встреч!!
Последнее редактирование: