Публичное раскрытие уязвимости в сторонней проверке подписи кода Apple
В отличие от некоторых предыдущих работ, данная уязвимость не требует прав администратора, не требует JIT-кода или повреждения памяти для обхода проверки подписи кода. Всё что нужно — правильно отформатированный файл Fat/Universal, и проверка подписи кода покажет валидный результат.
Резюме
- Найденный обход применяемого сторонними разработчиками API для подписи кода позволяет представить любой код как подписанный Apple.
- Все известные вендоры и проекты с открытым исходным кодом уведомлены (см. список ниже). Для них доступны патчи.
- Есть вероятность, что проблема затрагивает другие сторонние программы, где используются официальные API подписи кода от Apple.
- Разработчики несут ответственность за правильное использование API подписи кода. Есть инструменты демо-взлома (PoC) для тестов.
- Относится только к macOS и более старым версиям OSX.
Затронутые вендоры
- VirusTotal – CVE-2018-10408
- Google – Santa, molcodesignchecker – CVE-2018-10405
- Facebook – OSQuery – CVE-2018-6336
- Objective Development – LittleSnitch – CVE-2018-10470
- F-Secure – xFence (также LittleFlocker) CVE-2018-10403
- Objective-See – WhatsYourSign, ProcInfo, KnockKnock, LuLu, TaskExplorer (и другие) – CVE-2018-10404
- Yelp – OSXCollector – CVE-2018-10406
- Carbon Black – Cb Response – CVE-2018-10407
Важность подписи кода и как это работает на macOS/iOS
Подпись кода — это конструкция безопасности, которая использует инфраструктуру открытых ключей (PKI) для цифровой подписи скомпилированного кода или даже скриптов, чтобы удостоверить надёжное происхождение и гарантировать аутентичность кода. На Windows вы можете криптографически подписать почти что угодно: от бинарников .NET до скриптов PowerShell. На macOS/iOS подпись кода относится в основном к двоичным файлам Mach-O и пакетам приложений, чтобы допускать к выполнению в памяти только доверенного кода.
Антивирусы, системы безопасности и реагирования на инциденты, а также инструменты криминалистической экспертизы анализируют подписи, чтобы выявить доверенный код среди ненадёжного. Проверка подписи ускоряет анализ. Различные инструменты используют информацию о подписи кода для реализации мер безопасности: это белые списки, антивирусы, системы реагирования на инциденты и поиска угроз. Скомпрометировать подпись кода в одной из популярных ОС — значит подорвать основную конструкцию безопасности, от которой зависят многие рутинные операции в сфере ИБ.
В подписи кода уже находили проблемы (1, 2, 3, 4, 5). В отличие от некоторых предыдущих работ, данная уязвимость не требует прав администратора, не требует JIT-кода или повреждения памяти для обхода проверки подписи кода. Всё что нужно — правильно отформатированный файл Fat/Universal, и проверка подписи кода покажет валидный результат.
Подробности об уязвимости
Суть уязвимости в неодинаковой проверке подписи кода загрузчиком Mach-O и Code Signing API, которые используются неправильно. Эту разницу можно эксплуатировать с помощью специально сформированного двоичного файла Universal/Fat.
Что такое файл Fat/Universal?
Fat/Universal — это двоичный формат, который содержит несколько файлов Mach-O (исполняемый файл, dyld или пакет), каждый из которых ориентирован на определенную архитектуру CPU (например, i386, x86_64 или PPC).
Необходимые условия
- Первый Mach-O в файле Fat/Universal должен быть подписан Apple, это может быть файл i386, x86_64 или даже PPC.
- Самоподписанный вредоносный бинарник или посторонний код должен быть скомпилирован под i386 для macOS x86_64.
- CPU_TYPE в заголовке Fat бинарника Apple должен быть установлен на недопустимый тип или тип процессора, не родной для чипсета хоста.
Без прохождения соответствующих SecRequirementRef и SecCSFlags программный интерфейс Code Signing API (SecCodeCheckValidity) проверит первый бинарник в файле Fat/Universal на предмет происхождения подписи (например, Apple) и убедится в аутентичности подписи. Затем API проверит все остальные бинарники в файле Fat/Universal на соответствие Team Identifiers и аутентичность подписи, но без проверки корня доверия центра сертификации. Причина, почему вредоносный код или «неподписанный» код, должен быть i386, заключается в том, что Code Signing API по умолчанию настроен на проверку в первую очередь подписи кода для нативной архитектуры CPU (x86_64).
Одна из причин, почему самоподписанный код успешно проходит проверку — потому что даже в основных бинарниках Apple поле TeamIdentifier установлено как not set
. На иллюстрации ниже показаны валидный бинарник Mach-O, подписанный Apple (python.х64), рядом с самоподписанным Mach-O (ncat.i386). У обоих указано `TeamIdentifier=not set`.
Например, я подписал бинарник с помощью ID разработчика и попытался в lipo объединить его с бинарником от Apple в один файл Fat/Universal. Такой вариант подписи кода не проходит.
Мой первоначальный PoC — это ncat (от nmap), который я назвал ncat.frankenstein. Здесь итоговый файл Fat содержит подписанный Apple двоичный файл python x86_64 и самоподписанный (adhoc) двоичный файл ncat i386. Самоподписанный бинарник легко создаётся командой codesign -s - target_mach-o_or_fat_binary
. Вот как он выглядит в MachOView:
Если запустить этот файл, то запустится именно python x86_64:
А подпись кода проходит проверку:
Как я запустил самоподписанный двоичный файл ncat?
Это делается путём установки недопустимого типа CPU_Type или ненативного CPU (например, PPC). Тогда загрузчик Mach-O пропускает двоичный файл Mach-O с действительной подписью и выполнит вредоносный (не подписанный Apple) код:
Потом выполняется ncat.frankenstein, и результатом проверки будет valid
:
Мы опубликовали ncat.frankenstein и четыре других примера, чтобы разработчики могли проверить наличие уязвимости в своих продуктах.
Рекомендации
В командной строке
Зависит от того, как вы проверяете подписанный код. Если используете codesign, то наверное знакомы со следующими командами:
codesign –dvvvv
— дамп центра сертификации и TeamIdentifier (ID разработчика)codesign –vv
— строгая проверка всех архитектур
Но для правильной проверки этого типа злоупотреблений нужно добавить требование anchor-сертификата следующими командами:
codesign -vv -R='anchor apple' ./some_application_or_mach-o
# для кода, подписанного Applecodesign -vv -R='anchor apple generic' ./some_application_or_mach-o
# для кода, подписанного Apple и разработчиком Apple
Такая команда покажет ошибку при проверке кода с чужой подписью:
Можно использовать команду spctl
, но она требует тщательного анализа вывода команды. Например, бинарник Mach-O с подписью Apple (/bin/ls) и Safari возвращает следующее:
А вот результат приложения, подписанного Apple:
Обратите внимание на строку “(the code is valid but does not seem to be an app)” для /bin/ls, который не проходит проверку. Для сравнения, вот результат файла Fat/Universal ncat.frankenstein:
Для файла ncat.frankenstein Fat/Universal не отображается, что код действителен. Таким образом, я не могу рекомендовать spctl
для ручной проверки автономных двоичных файлов Mach-O. Просто используйте codesign с соответствующими флагами.
Для разработчиков
Как правило, разработчики проверяют бинарники Mach-O или Fat/Universal с помощью API SecStaticCodeCheckValidityWithErrors() или SecStaticCodeCheckValidity() со следующими флагами:
Эти флаги должны гарантировать, что весь загруженный в память код в файле Mach-O или Fat/Universal криптографически подписан. Тем не менее, эти API по умолчанию не обеспечивают должную проверку, так что сторонним разработчикам требуется вычленять каждую архитектуру в файле Fat/Universal и проверять, что identities совпадают и криптографически надёжны.
Лучший способ проверить каждую вложенную архитектуру в файле Fat/Universal — это сначала вызвать SecRequirementCreateWithString с требованием “anchor apple”, затем SecStaticCodeCheckValidity с флагами kSecCSDefaultFlags | kSecCSCheckNestedCode | kSecCSCheckAllArchitectures | kSecCSEnforceRevocationChecks со ссылкой на требование; как показано в пропатченном исходном кодеWhatsYourSign.
Передавая “anchor apple”, в функцию SecRequirmentCreateWithString, такой вызов действует аналогично команде codesign -vv -R=’anchor apple’
, требуя наличия цепочки доверия Apple Software Signing для всех вложенных бинарников в файле Fat/Universal. Кроме того, передавая флаги и требование к SecStaticCodeCheckValidity, все архитектуры проверяются на это требование, и применяются проверки отзыва.
Демонстрации
Инструмент codesign
от Apple и необходимость использовать флаг -R
.
LittleSnitch — проверка файла Fat/Universal на диске не проходит, но LittleSnitch корректно проверяет процесс в памяти.
LittleFlocker — F-Secure купила LittleFlocker, и теперь он называется xFence.
F-Secure xFence (бывший LittleFlocker)
Инструменты Objective-See
TaskExplorer
WhatsYourSign
Facebook OSquery — результат проверки вредоносных образцов и /bin/ls в качестве валидного примера.
Выдача Google Santa – Fileinfo показывает, что ncat.frankenstein в белом списке:
Запрет выполнения ncat (неподписанного) и разрешение выполнения ncat.frankenstein:
Журнал santa.log с демонстрацией событий из предыдущего примера:
Carbon Black Response
VirusTotal — пример bash_ppc_adhoc до установки патча в VirusTotal:
Сроки раскрытия
22.02.2018: в Apple отправлен отчёт и PoC для обхода сторонних систем безопасности.
01.03.2018: Apple ответила, что сторонние разработчики должны использовать kSecCSCheckAllArchitectures и kSecCSStrictValidate с SecStaticCodeCheckValidity API, и документация для разработчиков будет соответствующим образом обновлена.
06.03.2018: в Apple отправлен отчёт и PoC для обхода флагов и строгой проверки codesign
.
16.03.2018: отправлена дополнительная информация в Apple.
20.03.2018: Apple заявила, что не рассматривает это как проблему безопасности, которую следует решать напрямую.
29.03.2018: Apple заявила, что документация может быть обновлена, но «[...] сторонним разработчикам следует выполнить дополнительную работу для проверки, что все identities в универсальном двоичном файле одинаковы, если они хотят представить осмысленный результат».
02.04.2018: первый контакт с CERT/CC и последующее сотрудничество для уточнения масштабов и влияния уязвимости.
09.04.2018: все известные сторонние разработчики, которых затронула уязвимость, уведомлены в координации с CERT/CC.
18.04.2018: последний контакт с CERT/CC с рекомендацией, что публичное раскрытие информации в блоге лучше всего подходит для уведомления остальных сторонних разработчиков, которые используют API подписи кода от Apple в частном порядке.
05.06.2018: окончательный контакт с разработчиками перед публикацией.
12.06.2018: раскрытие информации.
В заключение
Спасибо всем сторонним разработчикам за их кропотливую работу и профессионализм в решении этого вопроса. Уязвимости подписи кода — это особенно деморализующие баги, тем более для компаний, которые пытаются обеспечить безопасность лучше, чем по умолчанию в операционной системе.
Автор: GlobalSign_admin