Публичное раскрытие уязвимости в сторонней проверке подписи кода 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 # для кода, подписанного Apple
  • codesign -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: раскрытие информации.

В заключение


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



АКЦИЯ GMO GlobalSign Russia для подписчиков Habr


Дополнительную информацию вы можете получить, связавшись с менеджером GlobalSign по телефону: +7 (499) 678 2210, или заполните форму на сайте, указав промо-код CS002HBFR.

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


  1. aamonster
    14.06.2018 01:18

    Я правильно понимаю, что если проверяешь бандл на соответствие своему TeamId — тебя эта уязвимость не затрагивает?