
Entrada de blog que explora el App Sandbox de macOS, los entitlements mediante codesign y las técnicas de escape del sandbox utilizando launchd, LaunchAgents y los atributos de cuarentena.
Continuando con mi serie de publicaciones sobre mi transición a macOS, me gustaría hablar un poco sobre el sandbox de Apps de macOS.
Se recomienda encarecidamente leer primero la publicación macOS App structure: asumiré que el lector sabe la diferencia entre Apps, procesos (tareas), conoce un poco de launchd y su relación con el lanzamiento de Apps.
La primera vez que aprendí sobre el sandbox de macOS, intenté ingenuamente crear una macro de Word maliciosa.
Este (sigue siendo) un vector de entrada muy común en el ecosistema Windows, así que quería ver si podía simplemente lanzar procesos y, en general, causar estragos.
Bueno, las cosas no son tan fáciles en macOS: pude ejecutar procesos, por ejemplo, pero parecía que no podían hacer mucho.
Soltar archivos siempre me daba el críptico error Operation not permitted — ¿qué está pasando?
Empecé a leer un poco sobre macOS y Word y di con esta excelente publicación de Adam Chester (trabaja en MDSec). Recomiendo encarecidamente leer la publicación, pero resumiré los hallazgos aquí:
macOS solía tener una utilidad funcional llamada sandbox-exec que ejecutaba comandos en un sandbox. Aunque está obsoleta, podía revelar bastante. En su página de manual se ve que recibe un profile, por lo que podemos concluir que las reglas del sandbox se mantienen en perfiles. Esos perfiles pueden presentarse de varias formas: archivos, nombres predefinidos o incluso cadenas literales.
Las páginas de manual también indican que los desarrolladores deberían usar la función App Sandbox. Leyendo más al respecto, entendí que las reglas del sandbox están integradas en el binario, en nuestro caso, ubicadas en /Application/Microsoft Word.app/Contents/MacOS/Microsoft Word (si te resulta ajeno, consulta mi publicación sobre la estructura de las Apps en macOS).
Si bien puedes extraerlas fácilmente a mano, es mejor usar una herramienta: 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
...
Hay mucho que desglosar aquí, así que tomemos algunas notas de alto nivel:
-dv, que significa display y verbose. Luego, --entitlements presenta los entitlements asociados con la App o el binario (sí, codesign puede trabajar con ambos). Profundizaremos en los entitlements en una publicación diferente, pero por ahora digamos que reflejan las capacidades de la App, y uno de ellos indica que la App está en sandbox (com.apple.security.app-sandbox tiene un valor booleano True).plists (de nuevo, en mi [publicación sobre la estructura de las Apps en macOS]) sospecharán que el diccionario clave-valor es una representación de alguna lista de propiedades, y estarán en lo cierto.com.apple.security.temporary-exception.files.absolute-path.read-only menciona un array de rutas absolutas desde las que la App puede leer.com.apple.security.temporary-exception.sbpl también está aquí: sirve para crear esos notorios archivos temporales que tanto le gustan a Word.¡Observa lo poderosas que son esas reglas del sandbox!
En la publicación de MDSec de 2018 que mencioné antes, la parte deny file-write* bajo com.apple.security.temporary-exception.sbpl no existía, lo que permitía a los macros crear archivos con contenido arbitrario, como /Library/LaunchAgents/~$evil.plist. ¿Por qué eso evade el sandbox?
Los LaunchAgents y LaunchDaemons son un mecanismo de persistencia (legítimo) muy conocido en macOS. Ya los he mencionado antes, pero puedes pensar en ellos como Servicios (si vienes del mundo Windows): los LaunchDaemons se inician cuando el sistema operativo arranca (por lo tanto, viven fuera de la sesión del usuario), mientras que los LaunchAgents se inician cuando un usuario inicia sesión.
Curiosamente, ambos se describen en simples archivos plist. Aquí hay un ejemplo de mi actualizador de 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
}
Esos LaunchAgents y LaunchDaemons son lanzados por launchd (¿recuerdas ese proceso?) y, por lo tanto, se escapan del sandbox, ya que launchd no sabía si el plist fue soltado por un proceso en sandbox o no (e incluso si lo supiera, ¿cómo sabría qué reglas de sandbox aplicar?).
Este concepto de usar launchd para evadir el sandbox de macOS se usó ampliamente y, de hecho, yo mismo lo he utilizado en el pasado.
Para ahorrarte un par de clics, esta es la idea:
launchd lanza las Apps de macOS. Esas Apps pueden lanzarse haciendo doble clic sobre ellas o por otros medios; por ejemplo, al hacer clic en un archivo zip se usará Archive Utility ya que está asociado a los archivos zip.launchd es con el comando open.open es muy completo: puedes usar algunas de sus características interesantes, como seleccionar la App, seleccionar el nombre del archivo a abrir o incluso pasar argumentos completos de línea de comandos.Python integrada (que ya no existe en los dispositivos macOS nuevos de serie) para lanzar Python con un argumento stdin que esencialmente redirige la entrada estándar desde un archivo que solté (ese archivo era ~$evil.py debido a las restricciones de Word).launchd ejecutó una instancia de la App Python sin sandbox que comenzó a leer desde , el cual contenía comandos Python arbitrarios, escapando esencialmente del sandbox.Ha habido ideas similares en otras divulgaciones (un buen ejemplo está aquí), pero la idea sigue siendo la misma. ¡Estoy bastante seguro de que hay muchas más a la vista!
Una mención especial va para una gran publicación de Wojciech Regula, esta vez centrada en la app Terminal y la manipulación de variables de entorno. ¡Deberías leerla!
El problema encontrado por MDSec era específico de Office y se corrigió con reglas más estrictas.
Los que abusan de LaunchServices (que es el nombre del framework para lanzar apps con launchd) son más genéricos, por lo que Apple tuvo que corregirlos.
Una de las cosas que noté es que los archivos soltados por Word ahora se crean con el atributo extendido com.apple.quarantine; sí, el mismo que mencioné en mi introducción a Gatekeeper.
Resulta que ese atributo de cuarentena es un endurecimiento contra ciertos ataques; por ejemplo, la app Terminal se negaba a lanzar scripts de shell creados con ese atributo. Esa es la razón, por cierto, por la que tuve que usar la opción --stdin para Python.
Como señaló Gergely Kalman, Apple ha añadido comprobaciones adicionales al binario open para endurecer ese tipo de exploits. Parece que --stdin, --args y otras opciones de línea de comandos se ignoran si el proceso que llama está en sandbox. Sin embargo, open simplemente llama a LaunchServices (en launchd) mediante IPC, y convenientemente hay APIs para eso, p. ej. LSOpenURLsWithRole.
No he investigado si LaunchServices en sí también está endurecido; si no lo está, creo que se podrían lograr fácilmente evasiones similares del sandbox.
Hemos hablado brevemente de otra tecnología de macOS: el sandbox. Hemos visto lo potente y configurable que es, y cómo podría romperse.
También hemos relacionado varias cosas: cómo funcionan las apps con las reglas del sandbox, cómo el lanzamiento de apps por launchd rompe algo más que los árboles de procesos y cómo los archivos plist pueden usarse para bien o para mal, esta vez con persistencia (LaunchAgents y LaunchDaemons).
Por suerte, incluso hemos relacionado el atributo extendido com.apple.quarantine de la publicación introducción a Gatekeeper y hemos explicado cómo podría usarse como un endurecimiento adicional contra las evasiones del sandbox. ¡Nada mal!
En las próximas publicaciones exploraremos más mecanismos de seguridad en macOS y quizá hablemos de estrategias para romperlos.
¡Mantente atento!
Jonathan Bar Or (https://jonathanbaror.com)
~$whatever.docx~$evil.py