
Запись в блоге, исследующая macOS App Sandbox, энтайтлменты через codesign и техники выхода из песочницы с использованием launchd, LaunchAgents и атрибутов карантина.
Продолжая серию постов о моём переходе на macOS, я хотел бы немного обсудить песочницу приложений macOS.
Настоятельно рекомендуется сначала прочитать пост о структуре приложений macOS — я буду предполагать, что читатель знает разницу между приложениями и процессами (задачами), немного разбирается в launchd и его связи с запуском приложений.
Когда я впервые узнал о песочнице macOS, я наивно попытался создать вредоносный макрос Word.
Это (до сих пор) очень распространённый вектор атак в экосистеме Windows, поэтому я хотел проверить, смогу ли я просто запускать процессы и в целом сеять хаос.
Что ж, на macOS всё не так просто — например, процессы я запускать мог, но, похоже, они мало что могли сделать.
При попытке записать файлы я постоянно получал загадочную ошибку Operation not permitted — что происходит?
Я начал читать о macOS и Word и наткнулся на этот отличный пост от Адама Честера (работает в MDSec). Я очень рекомендую прочитать его, но здесь я кратко изложу выводы:
В macOS раньше была рабочая утилита sandbox-exec, которая выполняла команды в песочнице. Хотя она устарела, она могла многое прояснить. На её странице руководства видно, что она принимает profile, поэтому можно заключить, что правила песочницы хранятся в профилях. Эти профили могут быть разных видов и форм — файлы, предопределённые имена или даже строковые литералы.
На страницах руководства также сказано, что разработчикам следует использовать функцию App Sandbox. Читая дальше, я понял, что правила песочницы вшиты в бинарник; в нашем случае он находится по пути /Application/Microsoft Word.app/Contents/MacOS/Microsoft Word (если это кажется вам незнакомым, загляните в мой пост о структуре приложений macOS).
Хотя их можно легко извлечь вручную, лучше использовать инструмент codesign:
```jbo@McJbo ~ % codesign -dv --entitlements - /Applications/Microsoft\ Word.app/Contents/MacOS/Microsoft\ Word
Executable=/Applications/Microsoft Word.app/Contents/MacOS/Microsoft Word
Identifier=com.microsoft.Word
Format=app bundle with Mach-O universal (x86_64 arm64)
CodeDirectory v=20500 size=351454 flags=0x10000(runtime) hashes=10972+7 location=embedded
Signature size=8980
Timestamp=Apr 10, 2023 at 8:09:50 AM
Info.plist entries=52
TeamIdentifier=UBF8T346G9
Runtime Version=13.1.0
Sealed Resources version=2 rules=13 files=28766
Internal requirements count=1 size=180
[Dict]
[Key] com.apple.application-identifier
[Value]
[String] UBF8T346G9.com.microsoft.Word
[Key] com.apple.developer.aps-environment
[Value]
[String] production
[Key] com.apple.developer.team-identifier
[Value]
[String] UBF8T346G9
[Key] com.apple.security.app-sandbox
[Value]
[Bool] true
...
[Key] com.apple.security.temporary-exception.files.absolute-path.read-only
[Value]
[Array]
[String] /Library/Preferences/com.microsoft.office.licensingV2.plist
[String] /Library/Application Support/Microsoft/
...
[Key] com.apple.security.temporary-exception.sbpl
[Value]
[Array]
[String] (allow file-read* file-write* (require-all (vnode-type REGULAR-FILE) (regex #"(^|/)~\$[^/]+$")) )
[String] (deny file-write* (subpath (string-append (param "_HOME") "/Library/Application Scripts")) (subpath (string-append (param "_HOME") "/Library/LaunchAgents")) )
[Key] com.apple.security.temporary-exception.shared-preference.read-only
[Value]
[Array]
[String] com.ThomsonResearchSoft.EndNote
...
Тут нужно многое распаковать, поэтому давайте сделаем несколько заметок высокого уровня:
-dv, который означает display и verbose. Затем --entitlements показывает entitlements (права), связанные с приложением или бинарником (да, codesign может работать с обоими). Мы углубимся в entitlements в отдельном посте, а пока скажем, что они отражают возможности приложения, и одно из них указывает, что приложение находится в песочнице (com.apple.security.app-sandbox имеет логическое значение True).plist (опять же, в моём [посте о структуре приложений macOS]) возможно, подозревают, что словарь «ключ-значение» является представлением некоего property list, и они будут правы.com.apple.security.temporary-exception.files.absolute-path.read-only содержит массив абсолютных путей, из которых приложению разрешено читать.com.apple.security.temporary-exception.sbpl — оно нужно для создания тех печально известных временных файлов ~$whatever.docx, которые так любит Word.В посте MDSec от 2018 года, который я упоминал ранее, части deny file-write* в com.apple.security.temporary-exception.sbpl не существовало, что позволяло макросам создавать файлы с произвольным содержимым, например /Library/LaunchAgents/~$evil.plist. Почему это приводит к побегу из песочницы?
LaunchAgents и LaunchDaemons — хорошо известный (легитимный) механизм персистентности в macOS. Я упоминал их раньше, но вы можете думать о них как о службах (если вы пришли из мира Windows): LaunchDaemons запускаются при загрузке ОС (и поэтому работают вне пользовательской сессии), а LaunchAgents запускаются при входе пользователя в систему.
Интересно, что оба описываются в простых plist-файлах. Вот пример для моего обновлятора OneDrive:
jbo@McJbo ~ % plutil -p /Library/LaunchAgents/com.microsoft.OneDriveStandaloneUpdater.plist
{
"Label" => "com.microsoft.OneDriveStandaloneUpdater"
"Program" => "/Applications/OneDrive.app/Contents/StandaloneUpdater.app/Contents/MacOS/OneDriveStandaloneUpdater"
"ProgramArguments" => [
]
"RunAtLoad" => 1
"StartInterval" => 86400
}
Эти LaunchAgents и LaunchDaemons запускаются launchd (помните этот процесс?), и поэтому это приводит к побегу из песочницы, поскольку launchd не знал, был ли plist записан из процесса, находящегося в песочнице, или нет (а даже если бы знал — как бы он понял, какие правила песочницы применять?).