PostgreSQL 18.6 - Alta - Remota - Escritura de archivos - pg_basebackup sigue un enlace simbólico tar absoluto remanente de CVE-2026-6475
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · postgres-basebackup-symlink
Write-up: https://abraxaslabs.tech/research/postgres-basebackup-symlink
Clase: Escritura de archivos Alcance: Remoto
PostgreSQL 18.6 (724edf9) - PostgreSQL Global Development Group
CVE-2026-6475 añadió path_is_safe_for_extraction para los nombres de miembros tar. Los destinos de symlink absolutos todavía omiten esa comprobación para que los tablespaces funcionen. astreamer_file.c luego hace symlink() al destino y posteriormente fopen(..., "wb") lo sigue. El formato predeterminado de pg_basebackup es plain (-F p). pg_rewind create_target_symlink comprueba la ruta y nunca el enlace.
Una fuente hostil que habla BASE_BACKUP puede plantar un symlink absoluto y sobrescribir cualquier archivo que el UID del backup pueda escribir. Mismo modelo de víctima que 6475. Esto no es una re-presentación de las comprobaciones de nombres de miembros.
| ID | aún sin CVE |
| Clase | Escritura de archivos (symlink tar absoluto remanente de CVE-2026-6475) |
| Alcance | Remoto (UI:R cliente vs PostgreSQL hostil) |
| CWE | CWE-59, CWE-22 |
| CVSS | Alto: 8.8 CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H |
| Producto | PostgreSQL |
| Afectado | 18.6 tag REL_18_6 (724edf9bde9d356724ad384a2e196edc3c9f80f7), pg_basebackup / pg_rewind en el árbol |
| Autenticación | la víctima ejecuta pg_basebackup -D <dir> por defecto contra una fuente del atacante |
| Licencia | GNU Affero GPL v3.0 |
| Laboratorio | solo 127.0.0.1 |
Alguien que consiga que un operador ejecute pg_basebackup -D contra su PostgreSQL (el mismo modelo UI:R que CVE-2026-6475) puede escribir fuera de -D.
| Privilegio | Método / sink | Probado en laboratorio o inferido |
|---|---|---|
Integridad: escribir un archivo fuera de -D | Tar BASE_BACKUP hostil: miembro con typeflag '2' con un nombre relativo seguro (pg_tblspc/16384) y un destino de enlace absoluto, luego un miembro regular bajo ese nombre. extract_link y después create_file_for_extract fopen(..., "wb") sin O_NOFOLLOW | Probado en laboratorio. Escribió POSTGRES-BASEBACKUP-SYMLINK-WITNESS en /work/oracle/. El destino contiene el symlink. El tar de control (sin symlink) dejó el oracle vacío y escribió PG_VERSION solo dentro de -D |
Integridad: sobrescribir shell rc, authorized_keys, una unidad de usuario de systemd o crontab | Mismo sink, cualquier ruta que el UID del cliente pueda escribir | Inferido. Este laboratorio no toca esos archivos |
| Disponibilidad: destrozar la configuración o las claves del cliente | Misma escritura | Inferido. No demostrado |
| Reconocimiento del disco del cliente | la extracción es escritura, no lectura | Cerrado |
Recorrer .. o nombres de miembros absolutos | path_is_safe_for_extraction | Cerrado. Eso es CVE-2026-6475 |
| SQL sin privilegios del lado del servidor / protocolo sin autenticación | servidor por defecto 18.6 | Cerrado. Este remanente es el cliente |
| Backup solo tar | pg_basebackup -F t almacena el tar sin extraerlo | Cerrado |
RCE dentro de postgres | no es este sink | Cerrado |
pg_rewind tiene el mismo remanente de create_target_symlink / open(). Este laboratorio solo ejecuta pg_basebackup.
Fijé postgres/postgres REL_18_6 tras la oleada de 18.6 (28 CVE, más el conjunto 18.4/18.2 ya presente en este árbol). Veintidós búsquedas. Servidor por defecto, SQL sin privilegios, protocolo sin autenticación: ninguno a este nivel. CVE-2026-6475 era el hermano que había que releer.
Los nombres de miembros se comprueban. Los destinos de symlink absolutos no:
if (!is_absolute_path(linktarget) &&
!path_is_safe_for_extraction(member->linktarget))
{
pg_fatal("link target has unsafe path name: \"%s\"",
member->linktarget);
}
extract_link(mystreamer->filename, linktarget);
create_file_for_extract sigue siendo fopen(filename, "wb"). El comentario en astreamer_file.c:351-356 dice que el mapeo se aplica sin comprobar que el enlace esté dentro de pg_tblspc.
Errores ya registrados: tratar 6475 como cerrado y marcharse; apuntar -D a un directorio de datos en vivo; sobrescribir el rc del shell por puro teatro. Un servidor 18.6 honesto solo emite enlaces simbólicos pg_tblspc/<oid> y coloca el contenido del tablespace en un archivo separado. Una fuente maliciosa que habla BASE_BACKUP puede colocar el miembro regular de seguimiento en el mismo tar. Eso es lo que queda.
python3 postgres-basebackup-symlink-Abraxas-Labs.py
o
cd lab
./run.sh
postgres:18.6 como imagen de origen, ejecutando un speaker BASE_BACKUP. El cliente es pg_basebackup del propio árbol desde la misma etiqueta (pg_basebackup (PostgreSQL) 18.6). Loopback 127.0.0.1:19370. Proyecto de Compose postgres-basebackup-symlink. No apuntes -D a un directorio de datos en uso.
SUCCESS postgres-basebackup-symlink control-inside=yes oracle-outside=yes dest-is-symlink=yes image=postgres:18.6 how=pg_basebackup POSTGRES-BASEBACKUP-SYMLINK-WITNESS
Tratar los destinos de enlaces simbólicos absolutos como inseguros a menos que se reescriban mediante --tablespace-mapping. Abrir los archivos extraídos con O_NOFOLLOW / lstat para que no se pueda seguir un enlace plantado. Aplicar path_is_safe_for_extraction para rebobinar link así como path. Rechazar un miembro regular cuya ruta atraviese un enlace simbólico que haya salido de -D.
724edf9bde9d356724ad384a2e196edc3c9f80f7)src/fe_utils/astreamer_file.c · src/fe_utils/astreamer_tar.csrc/bin/pg_rewind/file_ops.cGNU Affero GPL v3.0. Consulte LICENSE. Solo laboratorio de loopback. Sin garantía.