Эксплоиты. Первая часть
Торчик
Введение
Здравия желаю, товарищи. На этой неделе под нашим прицелом - экcплоиты. Изучим что это за чудо и как с ним работать.
Общие сведения
Вне всякого сомнения, эксплоиты - это в настоящее время самый мощный и распостранённый вид хакерского оружия. Хакеры, которые самостоятельно находят уязвимости и пишут под них эксплойты, относятся к хакерской элите. И это не громние слова, умение программировать эксплойты требует глубогих знаний операционных систем, языков C/C++, Assembler'а и прочих технологий. Данная статья даёт начальные знания, необходимые при программировании эксплойтов.
Основная часть
Термины и определения
Эксплоит (от англ. exploit - эксплуатировать, второе значение этого слова "подвиг") - это программа, которая использует уязвимость в программном обеспечении для выполнения заранее подготовленного кода. Часто в хакерской среде используется искажённое написание слова "exploit", например "sploit" или "xploit".
Все эксплоиты принято разделять на два больших класса: локальные эксплоиты (local exploits) и удалённые эксплоиты (remote exploits). Эти два класса существенно отличаются по внутреннему исполнению. Локальные эксплоиты предназначены для применения при имеющемся непосредственном физическом доступе к атакуемой машине, а удалённые эксплоиты используются для атак по сети.
В этой статье мы будем расматривать самый распостранённый и сложный тип локальных и удалённых эксплоитов - это так называемые экплоиты-шеллкоды (shellcode exploits), т. е. эксплоиты, которые запускают командную оболочку (shell) на взломанной системе (как правило, /bin/sh в Linux). Кроме запуска оболочки такие эксплоиты могут попутно выполнять другие действия, например, изменять правила сетевого экрана. Центральной частью эксплоитов этого типа является шеллкод (shellcode), который иногда называют "полезной нагрузкой" (an exploit payload).
Шеллкод - это заранее подготовленный машинный код, который внедряется в память уязвимой программы и непосредственно запускает оболочку системы. Но не все эксплоиты содержат в себе шеллкод, например, существуют эксплоиты, которые просто подвешивают систему или выполняют другие деструктивные действия через обнаруженную уязвимость - их называют DoS-эксплойты. По сути DoS-эксплойты являются утилитами для реализации DoS атак, которые мы рассматривали ранее.
Возможность написания эксплоитов во многом зависит от того языка программирования, на котором была создана уязвимая программа. Каждый язык программирования имеет свои специфичные ошибки. Например, программы на Perl и PHP могут быть подвержены ошибке с названием "ядовитый null-байт" (posion NULL byte), но такая ошибка не свойственна программам на С и С++. Существуют ошибки, которые свойственны сразу многим языкам программирования, например, ошибка индексации массива.
Так как мы будем в статье создавать експлоиты на языке Си, то эти эксплоиты будут эксплуатировать ошибки, свойственные только этому языку (ошибки переполнения буфера в стеке, ошибки форматной строки, ошибки переполнения буфера в куче и .bss). Существуют эксплоиты на иных языках программирования, таких как PHP, Perl, Python, ASP и пр., которые эксплуатируют ошибки свойственные данным языкам. Но иногда возможно написать эксплоит, например, на языке Perl, который будет эксплуатировать ошибки в программах, написанных на Си.
Часто в хакерских кулуарах встречаются термины как "0-day exploit", "private exploit", "fake exploit", "PoC", "autorooter", "massrooter". Ниже им даны определения.
0-day exploit (от англ. zero-day - "0-дневные эксплоиты") - это свежие эксплоиты под ошибки, для которых ещё не существует патчей или исправленных версий ПО. Обычно, после появления эксплоита, производители ПО, выпускают патч или новую версию ПО, в которой устраняется уязвимость, используемая эксплоитом, после чего он считается устаревшим. Изначально 0-day эксплоиты известны лишь небольшой группе хакеров, но, как правило, со временем информация о них страновится общедоступной.
Private exploits (от англ. private - частный, приватный) - это личные эксплоиты. О таких эксплоитах никто не знает, кроме их создателя. Как правило, со временем создатель делает приватный эксплоит 0-day эксплоитом, либо это происходит в результате случайной утечки. Приватные эксплоиты и 0-day эксплоиты очень ценятся (в том числе и в денежном экививаленте), поэтому за ними охотятся неквалифицированные подростки скрипт-кидди (script-kiddy).
Fake-exploits (от англ. fake - фальшивый, обманный) - это программы имитрующие эксплоиты, но на самом деле ими не являющиеся. Под такими эксплоитами часто маскируются трояны. После запуска такого "эксплоита" на машине, где он был запущен, устанавливает чёрный ход, а на e-mail создателя фейкового эксплоита отправляется письмо, уведомляющее об этом событии. Фейковые эксплоиты направлены против скрипт-кидди, которые бездумно запускают любой код. Существуют целые группы, которые промышляют продажей таких фейковых эксплоитов под видом 0-day эксплоитов. Так как эксплоиты используют администраторы для проверки своих систем, то им не рекомендуется брать эксплоиты из сомнительных источников, либо перед использованием нужно внимательно изучить его исходный код. Например, может понадобится перевести шестнадцатеричные коды шеллкода в их символьные эквиваленты, т. к. под видом шеллкода часто в фейковых эксплоитах зашифрованы деструктивные команды.
Термин PoC (от англ. Proof of Concept - доказательство концепции, решения или демострационный пример) используется чаще всего в среде специалистов по безопасности вместо термина "эксплоит". Сообщения о найденных уязвимостях распостраняются в двух видах: "Proof of Concept Theory" (доказательство уязвимости в теории) и "Proof of Concept Code" (доказательство уязвимости в виде реализующего ее использование программного кода). Под эксполитом, как правило, понимают последнее.
Autorooter (от англ. auto - автоматический и root - права системного администратора в UNIX-подобных системах) - это комплекс из одного или множества эксплоитов и других боевых утилит, таких как порт-сканер или сканер-безопасности. Autorooter или авторутер может быть выполнен в виде одного файла или множества связанных файлов. Он самостоятельно ищет в сети уязвимые машины, осуществляет их взлом и затем оповещает об этом хозяина, т. е. авторутеры осуществляют массовый автоматический взлом в сети, поэтому имеют ещё одно название - massrooter (от англ. mass - массовый) или массрутер.
Авторутеры специально создаюся хакерами для облегчения взлома серверов, они действуют во многом аналогично интеренет-червям, но только их действие находится под полным контролем хакера. Публичных авторутеров на момент написания книги известно не очень много, но очевидно, что число их будет расти. Из тех, что доступны в публичных интернет-архивах, можно назвать: massrooterfinal от Daddy_cad, lpd autorooter от dave, OpenSSL-uzi от Harden и др. Авторутер в руках скрипт-кидди - настоящее бедствие в сети.
Программирование авторутеров мы рассматривать не будем, но в наших статья вы найдёте сведения по всем необходимым "компонентам".
Структура памяти процесса
Для написания эксплоитов необходимо знание особенностей ОС. Так как мы будем разрабатывать эксплоиты только под ОС Linux, то вспомним некоторые особенности этой системы.
Программа на диске отличается от того образа, который она принимает, когда загружается в память. Выполняющаюсь в памяти программу принято называть процессом. Процесс может работать в двух режимах - в режиме ядра (kernel mode) и в пользовательском режиме (user mode). В пользовательском режиме процесс выполняет инструкции, допустимые на непривилегированном уровне защиты процессора. Когда процессу требуется получение каких-либо услуг ядра, он делает системный вызов, который выполняет инструкции ядра на привилегированном уровне процессора. Ядро, таким образом, защищает собственное адресное пространство от доступа прикладного процесса, который может нарушить целостность структур данных ядра и привести к разрушению ОС. Соотвественно образ процесса состоит из двух частей: режима ядра и пользовательского режима.
Образ процесса в пользовательтсвом режиме делится на отдельные сегменты: кода, данных, стека, разделяемых библиотек и других структур, к которым он может получить непосредственный доступ. Образ процесса в режиме ядра состоит из структур данных, недоступных процессу в пользовательском режиме, например, структуры управления процессом, таблицы для отображения памяти и пр.
Виртуальное адресное пространство каждого процесса составляет 4 Гбайт в 32bit системах (16 Эбайт в 64bit системах). В нём старший 1 Гбайт виртуальной памяти принадлежит ядру системы, а 3 Гбайт отводятся процессу в пользовательском режиме. Стандартно в Linux-системах виртуальное адресное пространоство процесса в пользовательском режиме начинается с адреса 0xc0000000.
Размещение сегментов процесса в пользовательском режиме зависит от формата исполняемого файла. Основным форматом исполняемых файлов в системе Linux является формат ELF (Executable and Linkable Format), только этот формат рассматривается в этой статье, хотя существуют и другие форматы файлов, например COFF. На рисунках ниже показано размещение основных сегментов процесса, загруженного из ELF-файла. Сегменты в Linux-системах размещаюстя в виртуальной памяти, начиная с адреса 0xc0000000, в следующем порядке:
- Внешние переменные: переменные окружения (environment strings) (имя, путь программы, домашний каталог, имя почтового ящика, имя терминала и т. д.), аргументы командной строки (argv), указатели на переменные окружения (env pointers), указатели на аргументы командной строки (argv pointers), парметр argc.
- Сегмент стека (stack) используется для временного хранения переменных.
- Сегмент кучи (heap). В этом сегменте приложение имеет возможность выделять нужный объём памяти и управлять его ростом, т. е. распределять динамечески.
- Сегмент (.bss) содержит неициализированные данные.
- Сегомен данных (.data) содержит инициализированные данные.
- Сегмент текста (.text), называемый также сегментом кода, содержит инструкции программы. Этот сегмент предназначен только для чтения (read-only).
- Разделяемые библиотеки (shared libraries). В этом сегменте расположены функции общего назначения.

Для написания эксплоитов нужно чётко знать, в какие сегменты памяти помещаются переменные объявленные и определённые в программе. Это в свою очередь во многом зависит от типа переменной. В Си существуют следующие типы переменных:
- глобальные переменные - имеют область видимости во всей программе;
- локальные переменные - имеют область видимости, ограниченную только функцией, в которой они определены;
- автоматические переменные - это локальные переменные, которые возникают только при входе в функцию и исчезают после выхода из неё;
- статические переменные - переменные этого типа специфицируются словом
static, которое помещается перед обычным объявлением. Статические переменные могут быть локальными и глобальными. Статические локальные переменные, в отличие от автоматических, не возникают только на период работы функции, а существуют постоянно. Статические глобальные переменные ограничивают область видимости переменной концом файла.

- указатели - это особые переменные, храящие адреса памяти, по которым расположена некоторая информация. В архитектуре x86 применяется 32-разрядная система адресации, поэтому указатель это всегда 32-битный целый адрес в памяти.
Все глобальные и статические переменные, если они инициализированы конкретным значением, размещаются в сегменте .data, а если не инициализированы, то в сегменте .bss.
Автоматические переменные размещаются в стеке (stack).
При объявлении указатель размещается в .bss или в стеке, и не указывает ни на какой действительный адрес. Когда процесс выделяет память в куче (например, с помощью функции malloc()), адрес первого байта этой памяти (тоже 32-битное число) помещается в указатель.
Завершение
В этой статье мы программировать ничего не будем. Ваша задача сейчас - разобраться с теорией. Информация одна из самых сложных, если не самая сложная вообще. Поэтому прошу настоятельно - разберитесь с тем, что описано выше и почитайте доп. материалы.