
SnatchBox (CVE-2020-27935) 是一个影响 macOS 至 10.15.x 版本的沙箱逃逸漏洞和漏洞利用
SnatchBox (CVE-2020-27935) — это уязвимость побега из песочницы, затрагивающая macOS до версии 10.15, а также ранние бета-версии macOS 11.0. Наиболее значимое влияние SnatchBox заключается в том, что она позволяет вредоносному издателю обойти обязательную песочницу Mac App Store и получить полный доступ ко всем файлам пользователя, нарушая модель безопасности App Store в macOS.
Тот факт, что в macOS, в отличие, например, от iOS, пользовательская задача добровольно помещает себя в песочницу, уязвим по своей конструкции. Поскольку это задача, потенциально вредоносный автор имеет почти полный контроль над её отображениями памяти и содержимым, а код, выполняющийся до инициализации песочницы (например, сам dyld или среда выполнения Objective-C), анализирует содержимое этого потенциально вредоносного бинарного файла, тщательно подготовленные данные могут быть использованы для получения выполнения кода до инициализации песочницы. Если бы процесс никогда не выполнял никакого кода, включая код dyld, до помещения в песочницу, это не было бы проблемой, поскольку раннее выполнение кода не дало бы атаке никаких преимуществ в таком случае. Концептуально это похоже на обход песочницы Саагара Джхи, за исключением того, что он обходит недавно введённые меры защиты и проверки App Store.
Эксплуатация этой ошибки до macOS 10.15 довольно проста. Нужно создать бинарный файл, содержащий категорию Objective-C для класса, который используется до инициализации песочницы (например, OS_xpc_object), и переопределить метод (желательно унаследованный, чтобы избежать предупреждений среды выполнения), который используется до инициализации песочницы (например, , который неявно вызывается при первом обращении к классу). Поскольку категории загружаются до инициализации песочницы, а первое обращение к (или другим подходящим классам-жертвам) происходит после загрузки категорий и до инициализации песочницы, предоставленный атакующим метод (или другой подходящий метод-жертва) будет вызван до инициализации песочницы, что позволяет, например, получить доступ к данным за пределами контейнера. В качестве альтернативы атакующий может заменить ссылку на (который инициализирует песочницу) функцией, подобной NOP, чтобы (потенциально условно) отключить песочницу даже после возобновления выполнения.
+initializeOS_xpc_object+initialize_libsecinit_initializerСреда выполнения Objective-C, используемая в macOS 10.15, не уязвима для ранее описанной техники эксплуатации, поскольку категории не загружаются до установки didCallDyldNotifyRegister, в результате чего наш метод +initialize вызывается только после инициализации песочницы.
Однако map_images по-прежнему вызывается для нашего бинарного файла, что позволяет изменять данные среды выполнения непредусмотренными способами, что дало бы нам возможность выполнять код до инициализации песочницы. Полная, с комментариями, эксплуатация находится в main.c, но я расскажу здесь об основных деталях. Мы создаём структуру класса Objective-C, указатель data которой указывает на место в libxbc.dylib. Это место должно быть выбрано таким образом, чтобы в flags был установлен бит 31 (RW_REALIZED), чтобы среда выполнения не пыталась реализовать этот недопустимый класс и не упала, а firstSubclass должен разделять свой адрес с isa класса, который мы хотим переопределить. Другой (мета) класс будет наследоваться от этого недопустимого класса и предоставлять свой собственный метод +initialize. Мы добавляем подкласс в __objc_nlclslist, чтобы среда выполнения реализовала этот класс.
Когда среда выполнения реализует наш подкласс, что происходит до инициализации песочницы, она вызовет addSubclass для нашего недопустимого суперкласса и подкласса, что заменит isa жертвы на указатель на наш подкласс, эффективно заменяя все её методы нашим +initialize. Когда наш метод +initialize вызывается (а это произойдёт до инициализации песочницы, если мы выбрали подходящий класс-жертву), мы можем снова заменить ссылки на _libsecinit_initializer на NOP (условно или нет) и исправить изменения среды выполнения, которые мы сделали, чтобы возобновить выполнение без сбоев в дальнейшем.
Предоставленную демонстрацию можно собрать, выполнив make, создав файл по пути ~/Documents/SecretDocument.txt и запустив SnatchBox.app/Contents/MacOS/SnatchBox из терминала (создаётся пакет, поскольку это требуется com.apple.security.app-sandbox, но это всё ещё программа командной строки). Собранный бинарный файл подписан с помощью com.apple.security.app-sandbox, что обычно предотвращает доступ к ~/Documents/SecretDocument.txt (так как он не находится в нашем контейнере), но в данном случае сможет прочитать его данные. Из-за изменений структуры среды выполнения эта демонстрация не будет работать на немодифицированных macOS 10.14 и более ранних, но будет работать на 10.15 и 11.0 (протестировано: 10.15.4, 10.15.7 и 11.0 Beta (20A5354i)). Две техники эксплуатации можно объединить для работы с обеими версиями среды выполнения, но такая демонстрация не предоставлена.
Пример выполнения:
CatalinaVM:SnatchBox lior$ make
mkdir -p SnatchBox.app/Contents/MacOS/
clang -O3 -Wall -framework Foundation main.m -o SnatchBox.app/Contents/MacOS/SnatchBox
cp Info.plist SnatchBox.app/Contents/
codesign --force --sign - SnatchBox.app --entitlements ent.xml
CatalinaVM:SnatchBox lior$ echo "Quack"> ~/Documents/SecretDocument.txt
CatalinaVM:SnatchBox lior$ codesign -d --entitlements :- SnatchBox.app/Contents/MacOS/SnatchBox
Executable=/Volumes/SharedFolders/Home/Projects/SnatchBox/SnatchBox.app/Contents/MacOS/SnatchBox
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.files.user-selected.read-only</key>
<true/>
</dict>
</plist>
CatalinaVM:SnatchBox lior$ SnatchBox.app/Contents/MacOS/SnatchBox
Found libsecinit_initializer at 0x7fff72309124
Found libSystem.B.dylib at 0x7fff6f0de000
Found __DATA at 0x7fff984eeca0
Replacing libsecinit_initializer reference at 0x7fff984eed48 with a nop
2020-12-18 16:31:48.196 SnatchBox[804:8043] Attempting to read protected file: /Users/lior/Documents/SecretDocument.txt
2020-12-18 16:31:48.197 SnatchBox[804:8043] Escaped sandbox! The contents are: <51756163 6b0a>
Как упоминалось ранее, это позволяет создать приложение для Mac App Store, которое не работает в песочнице, несмотря на требование политики App Store. Уязвимость также может быть использована в фреймворке, который могут использовать в остальном легитимные приложения App Store. Наконец, её можно даже объединить с чем-то вроде «Xcode Ghost» для массового внедрения вредоносного кода, работающего вне песочницы, в приложения App Store.
Apple исправила уязвимость на этапе бета-тестирования macOS 11.0, добавив вызов malloc_size в realizeClassWithoutSwift. Это подтверждает, что если класс помечен как реализованный (RW_REALIZED, как наш поддельный класс), он действительно имеет допустимый, выделенный через malloc, указатель на данные, с правильным размером (0x20 байт). Если это не так, среда выполнения прервётся с сообщением, похожим на realized class 0x100002078 has corrupt data pointer 0x7fff88c00948. Исправление также было применено к iOS, iPadOS, tvOS и watchOS, даже если они не были напрямую затронуты.