Безопасная разработка и уязвимости кода. Часть1. Уязвимость
Понятие DevSecOps (как, впрочем, и DevOps) до сих пор трактуют весьма широко, кто-то считает, что направление в разработке ПО, кто-то считает, что такой специалист «на все руки». Но, пожалуй, наиболее подходящим в контексте этой статьи будет следующее определение DevSecOps — это практика интеграции тестирования безопасности в каждый этап процесса разработки программного обеспечения. Отсюда можно сделать вывод, что и разработчики и тестировщики должны быть знакомы с базовыми уязвимостями, которые можно встретить в коде.
Однако, для того, чтобы лучше проникнуться тем, к чему могут привести уязвимости в программном обеспечении, я предлагаю не ограничиваться только поверхностным «общим» описанием того, как в принципе работает та или иная уязвимость, а пройти полный путь от выявления уязвимости до ее полноценной эксплуатации – запуска калькулятора в контексте уязвимой программы. В качестве примера такой уязвимости мы будем рассматривать переполнение буфера. В первой статье мы напишем уязвимую программу, правильно ее откомпилируем и поищем в ней уязвимости. Во второй статье мы проэксплуатируем найденную уязвимость, попутно разобрав все нюансы и подводные камни. В третьей статье мы поговорим о том, как можно выявлять уязвимости в исходном коде. При этом, так как та же уязвимость переполнения буфера «интернациональна», то есть и в Linux и в Windows, то первые две статьи мы посвятим разбору уязвимости в приложении под Windows 10, а в третьей статье будем рассматривать санитайзеры, работающие под Linux.
Что нам потребуется
В качестве среды разработки я не буду использовать громоздкие инструменты типа MS Visual Studio. Вместо этого в качестве компилятора кода на С у нас будет выступать крошечный компилятор Tiny C Compiler (https://bellard.org/tcc/). Для правки двоичных файлов нам потребуется редактор, я буду использовать HxD, хотя многие скорее всего предпочтут Hiew.
И наконец, отладчик. Я буду использовать x64dbg (32-битную редакцию), хотя здесь можно было бы воспользоваться и старым добрым OllyDebug.
Все приведенные далее манипуляции я проводил на своей хостовой машине, то есть виртуализация не требуется, ничего вредоносного, равно как и нарушающего статьи УК мы делать не собираемся.
Дырявый код
В качестве уязвимой программы у нас выступит следующий код:
int main(int argc, char* argv[]) < char buffer[600]; //Объявляем 600 байтовый буффер strcpy(buffer, argv[1]); //Копируем в буффер 1 аргумент printf("Input: %s\n", buffer); return 0; >
Как видно во второй строке мы объявили буфер размером в 600 байт, а в следующей строке мы копируем данные из аргумента, переданного в командной строке в этот буфер. В случае, если пользователь передаст более 600 байт, произойдет затирание памяти и программа “упадет”.
Откомпилируем этот код:
Далее мы на время забудем, о том, что мы знаем об использовании небезопасной команды strcpy и будем искать уязвимости в откомпилированном файле методом черного ящика, то есть без доступа к исходному коду.
Немного фаззинга пожалуйста
Для того, чтобы выявить уязвимости в программе мы прибегнем к методам фаззинга. Вообще фаззинг это методика тестирования, при которой на вход программы подаются невалидные, непредусмотренные или случайные данные. В нашем случае мы будем подавать данные большого объема, наблюдая за реакцией программы.
Для начала посмотрим, как программа реагирует на корректный ввод:
Далее можно пойти двумя путями. Можно написать скрипт, который будет побайтово увеличивая массив передаваемых данных запускать программу, наблюдая на каком шаге она упадет. Но это долго. А можно сразу подать на вход большой объем данных и посмотреть, что будет.
Передадим порядка килобайта данных, и программа не выводит Input:, значит она аварийно завершила работу.
Для дальнейших действий нам потребуется отладчик.
Осторожно ассемблер!
Откроем наш выполнимый файл в x64dbg. Увидим много непонятных команд, о которых мы подробно будем говорить в следующей статье. Сейчас нам необходимо подать на вход отладчику наш большой набор байт. Для этого выбираем Файл -> Изменение аргументов командной строки и добавляем наш массив байт.
Далее нажимаем Выполнить, отладчик останавливается в состоянии Первая попытка исключения на 0x41414141. Также 0x41 должен быть в значении регистра EIP.
Если отладчик остановился на других значениях, следует выполнить Отладка -> Расширенный -> Выполнить (проглотить исключение).
Что же мы в итоге получили? Переданный нами массив данных затер значение регистра EIP. Данный регистр (Instruction Pointer) содержит адрес следующей машинной команды. То есть, вместо того, чтобы после копирования нашего буфера перейти к выполнению команды по следующему, корректному адресу, программа перейдет по адресу 0x41414141 (0x41 это код буквы А), который естественно корректным не является.
Теперь нам важно узнать, на каком именно объеме данных программа падает. Это нужно для того, чтобы впоследствии мы могли поместить в этот блок наш эксплоит. Здесь самый простой способ это подготовить специальную сигнатуру, которую скормить программе и посмотреть, какие именно байты затрут значение EIP.
У меня для этих целей сгенерирован следующий паттерн.
В нем 1024 байта и как видно символы представлены блоками по четыре байта, где первая буква всегда заглавная. Передадим этот массив в отладчике нашей уязвимой программе и запустим ее.
Теперь в EIP записалось значение 0x66676141 или fgaA. Здесь самое об обратном порядке записи в памяти и обратить полученный набор байт – Aagf. То есть, для того, чтобы вызвать переполнение буфера нам необходимо передать уязвимой программе более 644 байт. Запомним это число, оно нам еще пригодится в следующей статье, где мы будем эксплуатировать найденную сейчас уязвимость.
Заключение
В этой статье мы подробно рассмотрели алгоритм выявления уязвимостей переполнения буфера. Далее нам будет необходимо написать эксплоит, которые позволит нам проэксплуатировать найденную уязвимость и в следующей статье мы его подготовим.
Уязвимости программ
Уязвимости программ — ошибки, допущенные программистами на этапе разработки программного обеспечения. Они позволяют злоумышленникам получить незаконный доступ к функциям программы или хранящимся в ней данным. Изъяны могут появиться на любом этапе жизненного цикла, от проектирования до выпуска готового продукта. В ряде случаев программисты нарочно оставляют лазейки для проведения отладки и настройки, которые также могут рассматриваться в качестве бекдоров или недекларированных возможностей.
В некоторых случаях возникновение уязвимостей обусловлено применением средств разработки различного происхождения, которые увеличивают риск появления в программном коде дефектов диверсионного типа.
Уязвимости появляются вследствие добавления в состав ПО сторонних компонентов или свободно распространяемого кода (open source). Чужой код часто используется «как есть» без тщательного анализа и тестирования на безопасность.
Не стоит исключать и наличие в команде программистов-инсайдеров, которые преднамеренно вносят в создаваемый продукт дополнительные недокументированные функции или элементы.
Классификация уязвимостей программ
Уязвимости возникают в результате ошибок, возникших на этапе проектирования или написания программного кода.
В зависимости от стадии появления этот вид угроз делится на уязвимости проектирования, реализации и конфигурации.
- Ошибки, допущенные при проектировании, сложнее всего обнаружить и устранить. Это — неточности алгоритмов, закладки, несогласованности в интерфейсе между разными модулями или в протоколах взаимодействия с аппаратной частью, внедрение неоптимальных технологий. Их устранение является весьма трудоемким процессом, в том числе потому, что они могут проявиться в неочевидных случаях — например, при превышении предусмотренного объема трафика или при подключении большого количества дополнительного оборудования, что усложняет обеспечение требуемого уровня безопасности и ведет к возникновению путей обхода межсетевого экрана.
- Уязвимости реализации появляются на этапе написания программы или внедрения в нее алгоритмов безопасности. Это — некорректная организация вычислительного процесса, синтаксические и логические дефекты. При этом имеется риск, что изъян приведет к переполнению буфера или появлению неполадок иного рода. Их обнаружение занимает много времени, а ликвидация подразумевает исправление определенных участков машинного кода.
- Ошибки конфигурации аппаратной части и ПО встречаются весьма часто. Распространенными их причинами являются недостаточно качественная разработка и отсутствие тестов на корректную работу дополнительных функций. К этой категории также можно относить слишком простые пароли и оставленные без изменений учетные записи по умолчанию.
Согласно статистике, особенно часто уязвимости обнаруживают в популярных и распространенных продуктах — настольных и мобильных операционных системах, браузерах.
Риски использования уязвимых программ
Программы, в которых находят наибольшее число уязвимостей, установлены практически на всех компьютерах. Со стороны киберпреступников имеется прямая заинтересованность в поиске подобных изъянов и написании эксплойтов для них.
Поскольку с момента обнаружения уязвимости до публикации исправления (патча) проходит довольно много времени, существует изрядное количество возможностей заразить компьютерные системы через бреши в безопасности программного кода. При этом пользователю достаточно только один раз открыть, например, вредоносный PDF-файл с эксплойтом, после чего злоумышленники получат доступ к данным.
Заражение в последнем случае происходит по следующему алгоритму:
- Пользователь получает по электронной почте фишинговое письмо от внушающего доверие отправителя.
- В письмо вложен файл c эксплойтом.
- Если пользователь предпринимает попытку открытия файла, то происходит заражение компьютера вирусом, трояном (шифровальщиком) или другой вредоносной программой.
- Киберпреступники получают несанкционированный доступ к системе.
- Происходит кража ценных данных.
Исследования, проводимые различными компаниями («Лаборатория Касперского», Positive Technologies), показывают, что уязвимости есть практически в любом приложении, включая антивирусы. Поэтому вероятность установить программный продукт, содержащий изъяны разной степени критичности, весьма высока.
Чтобы минимизировать количество брешей в ПО, необходимо использовать SDL (Security Development Lifecycle, безопасный жизненный цикл разработки). Технология SDL используется для снижения числа багов в приложениях на всех этапах их создания и поддержки. Так, при проектировании программного обеспечения специалисты по ИБ и программисты моделируют киберугрозы с целью поиска уязвимых мест. В ходе программирования в процесс включаются автоматические средства, сразу же сообщающие о потенциальных изъянах. Разработчики стремятся значительно ограничить функции, доступные непроверенным пользователям, что способствует уменьшению поверхности атаки.
Чтобы минимизировать влияние уязвимостей и ущерб от них, необходимо выполнять некоторые правила:
- Оперативно устанавливать выпускаемые разработчиками исправления (патчи) для приложений или (предпочтительно) включить автоматический режим обновления.
- По возможности не устанавливать сомнительные программы, чье качество и техническая поддержка вызывают вопросы.
- Использовать специальные сканеры уязвимостей или специализированные функции антивирусных продуктов, позволяющие выполнять поиск ошибок безопасности и при необходимости обновлять ПО.
Уязвимости программ
Уязвимости программ — ошибки, допущенные программистами на этапе разработки программного обеспечения. Они позволяют злоумышленникам получить незаконный доступ к функциям программы или хранящимся в ней данным. Изъяны могут появиться на любом этапе жизненного цикла, от проектирования до выпуска готового продукта. В ряде случаев программисты нарочно оставляют лазейки для проведения отладки и настройки, которые также могут рассматриваться в качестве бекдоров или недекларированных возможностей.
В некоторых случаях возникновение уязвимостей обусловлено применением средств разработки различного происхождения, которые увеличивают риск появления в программном коде дефектов диверсионного типа.
Уязвимости появляются вследствие добавления в состав ПО сторонних компонентов или свободно распространяемого кода (open source). Чужой код часто используется «как есть» без тщательного анализа и тестирования на безопасность.
Не стоит исключать и наличие в команде программистов-инсайдеров, которые преднамеренно вносят в создаваемый продукт дополнительные недокументированные функции или элементы.
Классификация уязвимостей программ
Уязвимости возникают в результате ошибок, возникших на этапе проектирования или написания программного кода.
В зависимости от стадии появления этот вид угроз делится на уязвимости проектирования, реализации и конфигурации.
- Ошибки, допущенные при проектировании, сложнее всего обнаружить и устранить. Это — неточности алгоритмов, закладки, несогласованности в интерфейсе между разными модулями или в протоколах взаимодействия с аппаратной частью, внедрение неоптимальных технологий. Их устранение является весьма трудоемким процессом, в том числе потому, что они могут проявиться в неочевидных случаях — например, при превышении предусмотренного объема трафика или при подключении большого количества дополнительного оборудования, что усложняет обеспечение требуемого уровня безопасности и ведет к возникновению путей обхода межсетевого экрана.
- Уязвимости реализации появляются на этапе написания программы или внедрения в нее алгоритмов безопасности. Это — некорректная организация вычислительного процесса, синтаксические и логические дефекты. При этом имеется риск, что изъян приведет к переполнению буфера или появлению неполадок иного рода. Их обнаружение занимает много времени, а ликвидация подразумевает исправление определенных участков машинного кода.
- Ошибки конфигурации аппаратной части и ПО встречаются весьма часто. Распространенными их причинами являются недостаточно качественная разработка и отсутствие тестов на корректную работу дополнительных функций. К этой категории также можно относить слишком простые пароли и оставленные без изменений учетные записи по умолчанию.
Согласно статистике, особенно часто уязвимости обнаруживают в популярных и распространенных продуктах — настольных и мобильных операционных системах, браузерах.
Риски использования уязвимых программ
Программы, в которых находят наибольшее число уязвимостей, установлены практически на всех компьютерах. Со стороны киберпреступников имеется прямая заинтересованность в поиске подобных изъянов и написании эксплойтов для них.
Поскольку с момента обнаружения уязвимости до публикации исправления (патча) проходит довольно много времени, существует изрядное количество возможностей заразить компьютерные системы через бреши в безопасности программного кода. При этом пользователю достаточно только один раз открыть, например, вредоносный PDF-файл с эксплойтом, после чего злоумышленники получат доступ к данным.
Заражение в последнем случае происходит по следующему алгоритму:
- Пользователь получает по электронной почте фишинговое письмо от внушающего доверие отправителя.
- В письмо вложен файл c эксплойтом.
- Если пользователь предпринимает попытку открытия файла, то происходит заражение компьютера вирусом, трояном (шифровальщиком) или другой вредоносной программой.
- Киберпреступники получают несанкционированный доступ к системе.
- Происходит кража ценных данных.
Исследования, проводимые различными компаниями («Лаборатория Касперского», Positive Technologies), показывают, что уязвимости есть практически в любом приложении, включая антивирусы. Поэтому вероятность установить программный продукт, содержащий изъяны разной степени критичности, весьма высока.
Чтобы минимизировать количество брешей в ПО, необходимо использовать SDL (Security Development Lifecycle, безопасный жизненный цикл разработки). Технология SDL используется для снижения числа багов в приложениях на всех этапах их создания и поддержки. Так, при проектировании программного обеспечения специалисты по ИБ и программисты моделируют киберугрозы с целью поиска уязвимых мест. В ходе программирования в процесс включаются автоматические средства, сразу же сообщающие о потенциальных изъянах. Разработчики стремятся значительно ограничить функции, доступные непроверенным пользователям, что способствует уменьшению поверхности атаки.
Чтобы минимизировать влияние уязвимостей и ущерб от них, необходимо выполнять некоторые правила:
- Оперативно устанавливать выпускаемые разработчиками исправления (патчи) для приложений или (предпочтительно) включить автоматический режим обновления.
- По возможности не устанавливать сомнительные программы, чье качество и техническая поддержка вызывают вопросы.
- Использовать специальные сканеры уязвимостей или специализированные функции антивирусных продуктов, позволяющие выполнять поиск ошибок безопасности и при необходимости обновлять ПО.
При подготовке материала использовались источники:
https://habr.com/ru/companies/otus/articles/744556/
https://www.anti-malware.ru/threats/programs-vulnerability?page=6
https://www.anti-malware.ru/threats/programs-vulnerability