[Перевод] Я могу стать Apple, и ты тоже

Публичное раскрытие уязвимости в сторонней проверке подписи кода 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`.

gg9akttfbuqgywcesuv5d2dkh0s.jpeg

Например, я подписал бинарник с помощью ID разработчика и попытался в lipo объединить его с бинарником от Apple в один файл Fat/Universal. Такой вариант подписи кода не проходит.

i1cj9dejukiozjgjjgrsz2gdqx0.jpeg

Мой первоначальный PoC — это ncat (от nmap), который я назвал ncat.frankenstein. Здесь итоговый файл Fat содержит подписанный Apple двоичный файл python x86_64 и самоподписанный (adhoc) двоичный файл ncat i386. Самоподписанный бинарник легко создаётся командой codesign -s - target_mach-o_or_fat_binary. Вот как он выглядит в MachOView:

-carh7cnujihiyywsnbzfavb7hw.jpeg

Если запустить этот файл, то запустится именно python x86_64:

fwctcbarfuchn_zuaqo5nrlrlkw.jpeg

А подпись кода проходит проверку:

cnrgijkhtupfpf75drzdtbeo1wy.jpeg

Как я запустил самоподписанный двоичный файл ncat?

Это делается путём установки недопустимого типа CPU_Type или ненативного CPU (например, PPC). Тогда загрузчик Mach-O пропускает двоичный файл Mach-O с действительной подписью и выполнит вредоносный (не подписанный Apple) код:

kja5dif0ounjx61pwa_5kbktp-c.jpeg

Потом выполняется ncat.frankenstein, и результатом проверки будет valid:

rhoehulraulhxqazycbhkvkavko.jpeg

Мы опубликовали 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

Такая команда покажет ошибку при проверке кода с чужой подписью:

qslg0k82jyorajl2gdax2ghepua.jpeg

Можно использовать команду spctl, но она требует тщательного анализа вывода команды. Например, бинарник Mach-O с подписью Apple (/bin/ls) и Safari возвращает следующее:

q6t0m6xoiy4trbbpimvdr0ndiu0.jpeg

А вот результат приложения, подписанного Apple:

vixi8vqdbpxy3ea0gq_urvumtaq.jpeg

Обратите внимание на строку »(the code is valid but does not seem to be an app)» для /bin/ls, который не проходит проверку. Для сравнения, вот результат файла Fat/Universal ncat.frankenstein:

yyvp0ulpgffoo_4wliofmeop2eq.jpeg

Для файла 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.

n7lfh3nrjpeylassdofv9w0iqec.jpeg

LittleSnitch — проверка файла Fat/Universal на диске не проходит, но LittleSnitch корректно проверяет процесс в памяти.

pxmr2oaqo3rotqzptv7vm73hale.jpeg

rrnqcm80lu_9smft0aoyixtiv-u.jpeg

LittleFlocker — F-Secure купила LittleFlocker, и теперь он называется xFence.

m-t3c-bekefcza87gvgmnacblra.jpeg

F-Secure xFence (бывший LittleFlocker)

jdcst0vmipx5vorefqsczajcnkm.jpeg

Инструменты Objective-See

TaskExplorer

dybcquoyqgfxzz2g29b4-g7v8nw.jpeg

qoeonha_rqgnwfp1ksjoifotw5e.jpeg

WhatsYourSign

pkbcd4tosyoby3ngalitzxde1v8.jpeg

Facebook OSquery — результат проверки вредоносных образцов и /bin/ls в качестве валидного примера.

ixwamwpmyxwi9lgfb1urbbo886w.jpeg

Выдача Google Santa — Fileinfo показывает, что ncat.frankenstein в белом списке:

azmlq5ejhlwoebbwkoklxybnbyq.jpeg

Запрет выполнения ncat (неподписанного) и разрешение выполнения ncat.frankenstein:

ec46t2ehunppwswa18s73v9ilrq.jpeg

Журнал santa.log с демонстрацией событий из предыдущего примера:

m3s_7gyftvwojvotz2thbvs7bks.jpeg

Carbon Black Response

w7l5kmkd-bz8s113nbozrpgr2vs.jpeg

VirusTotal — пример bash_ppc_adhoc до установки патча в VirusTotal:

jwbyduuae8n78jh3yk527ffjbsc.jpeg

Сроки раскрытия

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: раскрытие информации.

В заключение

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

© Habrahabr.ru