Жизнь без SDL. Зима 2017

c28d9196d0ff4e379385b38892c7a443.jpg

Введение, дисклеймер или зачем эта статья


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

Первая попытка формирования проблемы была представлена на финальном этапе M*CTF в Конгресс-центре МТУСИ. В день лекций был представлен к вниманию доклад «Жизнь без SDL. Осень-2016», затрагивающий подробный разбор DOM XSS на WIX.COM и Cryptsetup Initrd root Shell в современных дистрибутивах. Последний случай мы рассмотрели на недавнем вебинаре.

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

Краткий обзор


В статье рассказывается о том, как проходит жизнь без практик безопасной разработки программного обеспечения (SDL).

В качестве материала для анализа были рассмотрены следующие уязвимости:

  1. Уязвимость CVE-2017–6074: DCCP в ядре Linux
  2. Уязвимость fsquirt в ОС Windows 7/8/8.1

Рассмотрим каждую из них.

Уязвимость CVE-2017–6074: DCCP в ядре Linux


Описание и разбор


Недавно Андрей Коновалов раскрыл уязвимость CVE-2017–6074, которую он нашел в коде ядра Linux, затрагивающем малоизвестный протокол транспортного уровня DCCP. Эта уязвимость связана с классом ошибок «двойное освобождение памяти». Исследователь показал, что при определенных условиях некоторый указатель на область памяти освобождается два раза.

Для проведения атаки ядро системы должно быть собрано с условием «CONFIG_IP_DCCP». Мнение людей, обсуждающих проблему, разделилось: одни утверждают, что во многих современных дистрибутивах данная опция включена по умолчанию, другие — что поддержка DCCP в большинстве реализаций Linux отключена (представители Red Hat заявили, что проверка архива службы техподдержки за последний год не выявила ни одного случая активного DCCP у клиентов). Следует отметить, что DCCP не забыт и даже встречался не так давно на EKOPARTY CTF.

В рамках данной статьи нам более интересна причина возникновения и сам факт, что такого рода уязвимость находилась достаточно долго в системе (с версии 2.6.14 — октябрь 2005 г. и вплоть до выпуска 4.9.11.). Давайте разберем причину проблем и подумаем, как сделать так, чтобы они не привели к очень серьезным последствиям.

4a1c250cbde9457ab40a3af3a761ddf5.png

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

Поскольку блок помечен как свободный, то последующая работа ПО может изменить содержимое памяти по этому адресу. Второй вызов освобождения памяти будет пытаться интерпретировать те данные, которые записаны как размер освобождаемой памяти. Именно это действие может привести к потенциально не желаемым последствиям — падению ПО (например, из-за того, что функция освобождения памяти может попытаться пометить «всю» доступную ей память как освобожденную, поскольку там, где раньше был размер, оказалось 0xffffffff), или даже исполнению стороннего кода.

Именно пример, когда может произойти выполнение стороннего кода демонстрирует Андрей. Схематично он указывает такой порядок вызовов функций:

kfree(pointer)
. . .
some_object = kmalloc()
. . .
kfree(pointer)

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

Повторный вызов освобождения приведет к тому, что память по адресу pointer будет помечена как свободная, но при этом будет считаться, что some_object указывает на корректный участок памяти, что окажется не так. Так уязвимость двойного освобождения памяти превратилась в «использование памяти после освобождения» — код будет использовать указатель some_object не учитывая, что он был освобожден.

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

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

Оценка последствий


TL DR;
CVSS v2: 7.2 HIGH
Vector: (AV: L/AC: L/Au: N/C: C/I: C/A: C)


Несмотря на то, что данная уязвимость требует локального доступа, она все же является довольно опасной и может привести к:
  • Несанкционированному раскрытию информации:
    • в памяти может оказаться указатель на все что угодно;
  • Несанкционированной модификации информации:
    • использование ранее освобожденной памяти может повредить действительные данные, если область памяти была выделена и использована должным образом в другом месте;
  • Нарушению доступности сервиса:
    • если консолидация чанков (chunk) происходит после использования ранее освобожденных данных, то может произойти сбой, если в качестве информации о фрагменте используется недопустимый чанк;
  • Выполнению неавторизованного кода:
    • двойное освобождение памяти может привести к тому, что примитив «Write-what-where» позволит злоумышленнику выполнить произвольный код.

Используем практики безопасной разработки программного обеспечения (SDL)


Рассмотрим данную ситуацию со стороны, как будто бы при разработке использовался SDL. Для решения данной проблемы на первый взгляд хорошо подходят следующие фазы:
  • Design/Проектирование:
    • в общем случае такого класса ошибок при разработке выберите язык, который обеспечивает автоматическое управление памятью. Если язык уже определен, то заранее обсудите по каким правилам будет осуществляться управление памятью, чтобы исключить это на уровне проектирования;
  • Реализация/Implementation:
    • пользуйтесь заранее установленными правилами по управлению памятью. Контролируйте то, что память освобождается только один раз;
    • после освобождения памяти, установите указатель на эту память в NULL, чтобы указатель не мог быть освобожден снова;
    • если используется объектно-ориентированный язык, то убедитесь, что деструкторы объектов удаляют каждый участок памяти только один раз;
    • используйте инструменты статического анализа, чтобы найти участки кода с двойным освобождением памяти. В рамках данной уязвимости это является достаточно проблематичной задачей, ввиду сложности проекта;
  • Проверка/ Verification:
    • динамический анализ. Использование верификации во время выполнения (Runtime Verification) ПО позволяет проверять его функциональность с помощью инструментов, отслеживающих поведение приложения в случаях повреждения памяти, проблем с привилегиями пользователей и других критических проблем безопасности;
    • фаззинг. Использование преднамеренного ввода заведомо искаженных или случайных данных в приложение помогает выявить потенциальные проблемы, причем требуя при этом относительно не больших затрат. Обнаружить данную уязвимость Андрею удалось с помощью утилиты «syzkaller», которая в свою очередь является фаззером системных вызовов в Linux.

Как все исправить


Одним из вариантов является отключение DCCP:
# echo "install dccp /bin/true" >> /etc/modprobe.d/disable-dccp.conf

Разработчики дистрибутивов Linux уже выпустили обновления и все исправили. Данный патч исправляет ошибки:

769b4a6017fb47649c3eda966ccd1789.png

Уязвимость fsquirt в ОС Windows 7/8/8.1


Описание и разбор


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

Работа исследователя включает определенное множество задач, одной из которых является исследование ПО на предмет оказания воздействия сторонних компонентов. Одним из инструментов исследователя в таких типах работ является утилита «Process Monitor» из состава «SysInternals» Марка Руссиновича. Она позволяет отслеживать работу различных приложений с файловой системой, реестром, сетью и многие другие события. Одна из замечательных функций утилиты — отслеживание активности в момент загрузки системы. Как раз при анализе такого журнала событий была обнаружена странная активность, на которую обратил внимание наш исследователь:

100d53b12141466ab575104c301ad016.png

Explorer.exe пытается обратиться к файлу с нетривиальным именем «fsquirt.exe», причем ищет его в очень необычных местах. Если внимательно посмотреть на пути, по которым происходит попытка доступа к файлу, становится ясно, что это пути из переменного окружения PATH активного пользователя.

Сначала обратились в Google, который услужливо нам подсказал, что файл «fsquirt.exe» чаще всего располагается в »C:\Windows\System32\» и является частью подсистемы работы с Bluetooth. Подробнее о «fsquirt.exe» на сайте Microsoft.

Поскольку поиск файла идет в папках из переменного окружения PATH, это означает, что указанный файл не был найден в своем стандартном расположении, и ОС продолжила поиск по путям из PATH. Мы подумали, что если расположить файл по такому пути, то он будет запущен. Практика показала, что мы не ошиблись.

Поскольку исследование велось не на чистом стенде, то все-таки была некоторая вероятность, что эти действия привнесены сторонним ПО. Поэтому мы проверили гипотезу на свежеустановленных версиях ОС.

Итог: Windows 7, Windows 8 и Windows 8.1 загружают «fsquirt.exe», Windows 10 — нет.

В Windows 7, Windows 8 и Windows 8.1 (но не Windows 10) во время загрузки операционной системы (после входа пользователя) процесс «explorer.exe» ищет и запускает приложение «fsquirt.exe». Запуск производится с правами пользователя, эскалация привилегий отсутствует. Поиск приложения осуществляется в местах, подконтрольных пользователю, так как он может менять свое значение переменной окружения PATH.

Пример эксплуатации:

Потенциально вредоносное приложение может создать папку по пути »C:\SpecialDirFromPath» и изменить переменную окружения PATH для текущего пользователя, добавляя созданную директорию в список. Затем в этой папке создается файл «fsquirt.exe» с «нагрузкой».

В примере это просто консольное приложение с текстом и ожиданием ввода:

450fedc2be26403da0e3b0bec9d21e7c.png

Оценка последствий


Несмотря на то, что данная уязвимость не позволяет повысить права в ОС, она все же является довольно опасной. В первую очередь это возможность пользователю (или потенциально вредоносному приложению с правами пользователя) создать ситуацию, при которой при загрузке ОС будет исполняться произвольный код. Для реализации необходимо добавить путь к приложению в переменную окружения пользователя PATH и назвать приложение «fsquirt.exe».

Обнаружить такой автостарт будет достаточно затруднительно, поскольку он не использует ни один стандартный механизм для этого (реестр, задачи или специальные директории типа «Автозапуск»).

При использовании многоступенчатого запуска (когда «fsquirt.exe» запустит «Приложение А», то запустит «Приложение Б», а то, в свою очередь, запустит «Приложение В», и так далее) даже поведенческий анализ от антивирусных продуктов, возможно, не отследит причину начала активности или, как минимум, возникнут трудности.

Используем практики безопасной разработки программного обеспечения (SDL)


В данной части статьи для решения текущей проблемы хотелось бы затронуть не общие фазы, а уже конкретные практики:
  • Определение требований ИБ;
  • Анализ/сокращение поверхности атаки;
  • Тестирование/проверка безопасности сторонних компонент.

Проблема в большей степени носит организационный характер. Ведь с точки зрения функционирования все работает как положено, но организационно не определено: верно или не верно. Возможно, в данном примере логично объединить «определение требований ИБ» и «Анализ/сокращение поверхности атаки», так как здесь это сильно взаимосвязано.

Например, к драйверам в той же Windows применено огромное количество требований, чтобы они не нарушали общий уровень безопасности ОС. Условно для запуска драйвера нужно большое количество различной информации записать в реестр, положить по нужному пути, подписать файл цифровой подписью. Вот такой «небольшой» перечень действий, а для обычного старта ПО столько требований нет.

Как все исправить


Чтобы обезопасить себя от такой атаки стоит взять пустой исполняемый файл, который ничего не делает, и поместить его по пути »C:\Windows\system32\fsquirt.exe». В таком случае будет запущено данное приложение, и не будет осуществляться поиск иных путей исполнения. В Windows 10 данный файл имеется.

Заключение


Эта статья — рассказ о малой части проблем безопасности, возникающих при разработке ПО. Я хотел на практике показать важность цикла безопасной разработки SDL. Надеюсь, что эта статья в худшем случае окажется для вас немного полезной, а в лучшем — станет побуждением к действию. Уязвимость в ПО — это неприятно, опасно и иногда очень дорого. Главное — вовремя исправлять ошибки, а SDL — это тот инструмент, который поможет сократить число таких экстренных исправлений.

Я также хочу выразить огромную благодарность моему коллеге Василию Кравцу xi-tauw за то, что благодаря его вниманию при исследовании, у меня появился свежий пример для статьи. Также спасибо за ревью, найденные неточности и полезные предложения.

Комментарии (5)

  • 5 апреля 2017 в 17:11

    0

    Никогда не подумал бы что «fsquirt.exe» это часть винды. Кажется, я безнадежно испорчен.
  • 5 апреля 2017 в 17:11

    +2

    История с fsquirt навеяла вот такое замечание: «Если из унитаза потекло, пустой тазик можно поставить…, но может лучше канализацию починить»?
    • 5 апреля 2017 в 17:12

      +2

      Полностью согласен, но в данном контексте починить это может только Microsoft. В терминах вашей аналогии лучше поставить «тазик», пока ждёшь, когда «приедут и починят».
  • 5 апреля 2017 в 18:48

    0

    При внедрении SDL компания, сформирует что-то типа политики написания программного кода / разработки ПО.
    Но для того, чтобы какие-либо требования работали, в том числе данная политика, нужен контроль их выполнения.

    Как вы видите лучшую практику организации подобного контроля для небольших компаний, где есть 1–2 разработчика ПО и например 1 ИБист?

  • 5 апреля 2017 в 22:17

    0

    По заголовку решил, что это статья про game dev,
    т.к. SDL довольно известная в узких кругах библиотека: http://www.libsdl.org/

© Habrahabr.ru