PostgreSQL 18.6 - Élevé - À distance - Écriture de fichier - pg_basebackup suit un lien symbolique tar absolu, résidu 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
Classe : Écriture de fichier Portée : Distante
PostgreSQL 18.6 (724edf9) - PostgreSQL Global Development Group
CVE-2026-6475 a ajouté path_is_safe_for_extraction pour les noms de membres tar. Les cibles de liens symboliques absolues contournent encore cette vérification afin que les tablespaces fonctionnent. astreamer_file.c fait ensuite un symlink() vers la cible, puis un fopen(..., "wb") ultérieur la suit. Le format par défaut de pg_basebackup est plain (-F p). create_target_symlink de pg_rewind vérifie le chemin et jamais le lien.
Une source hostile qui parle BASE_BACKUP peut planter un lien symbolique absolu et écraser n'importe quel fichier que l'UID de sauvegarde peut écrire. Même modèle de victime que 6475. Il ne s'agit pas d'un nouveau signalement des vérifications de noms de membres.
| ID | pas encore de CVE |
| Classe | Écriture de fichier (lien symbolique tar absolu résiduel de CVE-2026-6475) |
| Portée | Distante (UI:R client contre PostgreSQL hostile) |
| CWE | CWE-59, CWE-22 |
| CVSS | Élevé : 8.8 CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H |
| Produit | PostgreSQL |
| Affecté | tag 18.6 REL_18_6 (724edf9bde9d356724ad384a2e196edc3c9f80f7), pg_basebackup / pg_rewind dans l'arborescence |
| Auth | la victime exécute pg_basebackup -D <dir> par défaut contre une source attaquante |
| Licence | GNU Affero GPL v3.0 |
| Lab | 127.0.0.1 uniquement |
Quelqu'un qui peut amener un opérateur à exécuter pg_basebackup -D contre son PostgreSQL (le même modèle UI:R que CVE-2026-6475) peut écrire en dehors de -D.
| Privilège | Méthode / sink | Prouvé en lab ou déduit |
|---|---|---|
Intégrité : écrire un fichier en dehors de -D | Tar BASE_BACKUP hostile : membre typeflag '2' avec un nom relatif sûr (pg_tblspc/16384) et une cible de lien absolue, puis un membre régulier sous ce nom. extract_link puis create_file_for_extract fopen(..., "wb") sans O_NOFOLLOW | Prouvé en lab. A écrit POSTGRES-BASEBACKUP-SYMLINK-WITNESS dans /work/oracle/. Dest contient le lien symbolique. Le tar de contrôle (sans lien symbolique) a laissé l'oracle vide et n'a écrit PG_VERSION qu'à l'intérieur de -D |
Intégrité : écraser un rc de shell, authorized_keys, une unité systemd utilisateur ou une crontab | Même sink, tout chemin que l'UID client peut écrire | Déduit. Ce lab ne touche pas à ces fichiers |
| Disponibilité : détruire la configuration ou les clés du client | Même écriture | Déduit. Non démontré |
| Reconnaissance du disque du client | l'extraction est une écriture, pas une lecture | Fermé |
Parcourir .. ou des noms de membres absolus | path_is_safe_for_extraction | Fermé. C'est CVE-2026-6475 |
| SQL non privilégié côté serveur / protocole non authentifié | serveur par défaut 18.6 | Fermé. Ce résidu concerne le client |
| Sauvegarde tar uniquement | pg_basebackup -F t stocke le tar sans l'extraire | Fermé |
RCE dans postgres | pas ce sink | Fermé |
pg_rewind a le même résidu create_target_symlink / open(). Ce lab ne pilote que pg_basebackup.
J'ai épinglé postgres/postgres REL_18_6 après la vague 18.6 (28 CVE, plus l'ensemble 18.4/18.2 déjà présent sur cette arborescence). Vingt-deux chasses. Serveur par défaut, SQL non privilégié, protocole non authentifié : aucun à ce niveau. CVE-2026-6475 était le cas frère à relire.
Les noms de membres sont vérifiés. Les cibles de liens symboliques absolues ne le sont pas :
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 est toujours fopen(filename, "wb"). Le commentaire à astreamer_file.c:351-356 indique que le mapping est appliqué sans vérifier que le lien se trouve dans pg_tblspc.
Fausses pistes déjà consignées : traiter 6475 comme clos et passer à autre chose ; pointer -D vers un répertoire de données actif ; écraser le rc du shell pour le spectacle. Un serveur 18.6 honnête n'émet que des liens symboliques pg_tblspc/<oid> et place le contenu des tablespaces dans une archive séparée. Une source malveillante qui parle BASE_BACKUP peut placer le membre régulier de suivi dans le même tar. C'est là le résidu.
python3 postgres-basebackup-symlink-Abraxas-Labs.py
ou
cd lab
./run.sh
postgres:18.6 comme image source, exécutant un speaker BASE_BACKUP. Le client est le pg_basebackup intégré de la même étiquette (pg_basebackup (PostgreSQL) 18.6). Boucle locale 127.0.0.1:19370. Projet Compose postgres-basebackup-symlink. Ne pointez pas -D vers un répertoire de données actif.
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
Traiter les cibles de liens symboliques absolus comme non sûres, sauf si elles sont réécrites par --tablespace-mapping. Ouvrir les fichiers extraits avec O_NOFOLLOW / lstat afin qu'un lien implanté ne puisse pas être suivi. Appliquer path_is_safe_for_extraction pour rembobiner link ainsi que path. Rejeter un membre régulier dont le chemin traverse un lien symbolique sorti 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. Voir LICENSE. Laboratoire en boucle locale uniquement. Aucune garantie.