В прошлой статье мы с тобой начали погружаться в мир шелл-кодинга для 64-битных *nix-систем. Пора развить эту тенденцию, ведь это журнал «Хакер»! Сегодня мы напишем эксплоит для обхода технологии DEP. Для этого рассмотрим две техники: ret2libc и ROP-цепочки.

 

Инструментарий

Сегодня нам понадобятся:

  1. Python Exploit Development Assistence for GDP.
  2. Radare2.
  3. GDB.

Для демонстрации уязвимости напишем простую программу на C:

#include <stdio.h>
int main(int argc, char \*argv[]) {
  char buf[256];
  read(0, buf, 400);
}

Компилируем ее:

gcc -fno-stack-protector rop.c -o rop

Так как обход ASLR — тема отдельной статьи, то временно отключаем его командой

# echo 0 > /proc/sys/kernel/randomize_va_space

Чтобы проверить, действительно ли ASLR отключен, введем команду ldd <путь_к_исполняемому_файлу>. Должны получить что-то вроде

linux-vdso.so.1 (0x00007ffff7ffa000)
libc.so.6 => /usr/lib/libc.so.6 (0x00007ffff7a3c000)
/lib64/ld-linux-x86-64.so.2 (0x00007ffff7dda000)

Если еще раз ввести команду, то адреса останутся такими же (указаны в скобках).

 

Коротко о DEP

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

DEP работает следующим образом: память, которая не должна исполняться (например, стек), помечается специальным битом NX. Если ты попробуешь запустить код из памяти с установленным битом NX, то вызовется исключение. Это не позволяет использовать эксплоиты, которые просто передают управление на шелл-код. Для обхода DEP/NX и существуют крутые техники, такие как return-oriented programming (кстати, почитай на досуге кое-что из нашего старенького: тыц и дыц. — Прим. ред.) и ret2libc. Более подробно о них расскажу чуть ниже.

 

Твой первый ROP или ret2libc

В классическом 32-битном случае ret2libc требует создания фейкового стека со всеми необходимыми параметрами для вызова функции из libc. Например, можно вызвать функцию system() и передать ей строку /bin/sh.

Как ты помнишь из предыдущей статьи, в 64-битной системе первые шесть параметров передаются через регистры rdi, rsi, rdx, rcx, r8 и r9. Все остальные параметры передаются через стек. Таким образом, для того чтобы вызвать функцию из libc, нам сначала необходимо присвоить регистрам нужные значения. Для этого мы и будем использовать ROP.

ROP (return-oriented programming) — это технология, которая позволяет обходить NX-бит. Идея ROP-цепочек довольно проста. Вместо того чтобы записывать и исполнять код на стеке, мы будем использовать так называемые гаджеты.

Гаджет — это короткая последовательность команд, которые заканчиваются инструкцией ret. Комбинируя такие команды, мы можем добиться исполнения кода.

При помощи гаджетов мы можем:

  • записывать константу в регистр, например pop rax; ret;;
  • брать значение из памяти и записывать в регистр, например mov [rax], rcx; ret;;
  • копировать значение в память, например mov rbx, [rcx]; ret;;
  • выполнять различные арифметические операции, например xor rax, rax; ret;;
  • делать syscall.

Наш эксплоит будет сравнительно простым. Он будет вызывать system('/bin/sh'). Для этого нам необходимо узнать:

  • адрес функции system(). Мы отключили ASLR, таким образом, он не будет меняться при перезапуске;
  • адрес строки /bin/sh в памяти (или, другими словами, указатель на строку);
  • адрес ROP-гаджета, который скопирует адрес строки /bin/sh в регистр rdi (через него передается первый параметр функции);
  • номер байта, после записи которого начинает перезаписываться регистр rip.

Для того чтобы найти адрес функции system(), воспользуемся отладчиком GDB — введем gdb rop. Затем запустим нашу программу:

gdb-peda$ start

Получим адрес функции system():

gdb-peda$ p system
$1 = {<text variable, no debug info>} 0x7ffff7a7b4d0 <system>

Получим указатель на строку /bin/sh:

gdb-peda$ find '/bin/sh'
Searching for '/bin/sh' in: None ranges
Found 1 results, display max 1 items:
libc : 0x7ffff7b9d359 --> 0x68732f6e69622f ('/bin/sh')

Записываем полученные адреса на листочек или в блокнот (у тебя они могут отличаться). Теперь нам нужен гаджет, который скопирует значение 0x7ffff7b9d359 в регистр rdi. Для этого воспользуемся radare2. Запускаем r2 rop и затем ищем нужный гаджет:

[0x00400400]> /R pop rdi
    0x004005a3                 5f  pop rdi
    0x004005a4                 c3  ret

Этот гаджет нам подходит. Он возьмет значение из стека и запишет его в регистр rdi. Сохрани его адрес.

Осталось узнать, сколько надо записать «мусора» перед нашим эксплоитом, чтобы управление передалось по правильному адресу.

Продолжение статьи доступно только подписчикам

Вариант 1. Оформи подписку на «Хакер», чтобы читать все статьи на сайте

Подписка позволит тебе в течение указанного срока читать ВСЕ платные материалы сайта, включая эту статью. Мы принимаем оплату банковскими картами, электронными деньгами и переводами со счетов мобильных операторов. Подробнее о подписке

Вариант 2. Купи одну статью

Заинтересовала статья, но нет возможности оплатить подписку? Тогда этот вариант для тебя! Обрати внимание: этот способ покупки доступен только для статей, опубликованных более двух месяцев назад.


Комментарии

Подпишитесь на ][, чтобы участвовать в обсуждении

Обсуждение этой статьи доступно только нашим подписчикам. Вы можете войти в свой аккаунт или зарегистрироваться и оплатить подписку, чтобы свободно участвовать в обсуждении.

Check Also

LUKS container vs Border Patrol Agent. Как уберечь свои данные, пересекая границу

Не секрет, что если ты собрался посетить такие страны как США или Великобританию то, прежд…