Skip to content
KitploitKITPLOIT
ToolsBlog
Submit
ToolsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
Tools/GitHubGitHub/dinosn/cve-2026-25243
Memory ForensicsVulnerability AnalysisExploitationReverse EngineeringPenetration TestingRemote Access ToolPayload DevelopmentBinary Exploitation
GitHubdinosn/cve-2026-25243

CVE-2026-25243

CVE-2026-25243 — Redis RESTORE zipmap double-free → remote code execution (ASLR on).

View Repository
132 months agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

CVE-2026-25243 — Redis RESTORE zipmap double-free → remote code execution

TL;DR. A malformed DUMP payload passed to RESTORE triggers a heap double-free in Redis's legacy hash-zipmap loader. Under default jemalloc the double-free is silent (the server keeps running), which turns it into a controllable type-confusion primitive. This repo chains that into remote code execution with ASLR enabled — the Redis worker calls system("<attacker string>") and keeps serving. Not a 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

The leak that defeats ASLR uses no DEBUG — it reads the redis.call Lua C-closure address (EVAL 'return tostring(redis.call)'), the same self-contained, DEBUG-free technique as our prior Redis exploit. blob_base/blob_robj and the PIE base are then derived at runtime from the over-read (no fixed offset). The PIE-ROP finish calls execve@plt through a JOP stack-pivot, so it uses zero libc addresses — the only build-specific constants are PIE-relative gadget offsets read from the redis-server binary, exactly like our prior HLL exploit's per-build gadget table. Verified uid=0(root), ASLR on, 8/8, on a default DEBUG-disabled server.

Two finishes are provided. poc_rce_aslr_pie_rop.py (above) is the most self-contained — no libc, fully self-calibrated — but execve replaces the worker (use a reverse-shell --cmd; best for a real shell). poc_rce_aslr_selfcal.py keeps the worker alive (system() forks) at the cost of two libc-version offsets. Pick by whether you need the server to survive.


The bug

RESTORE key 0 <DUMP-payload> deserializes a serialized object. For the legacy RDB_TYPE_HASH_ZIPMAP (0x09) type, the validator and the converter disagree on how many bytes a length field occupies:

  • zipmapValidateIntegrity() walks with the actual encoded size (5 for the overlong 0xFE prefix);
  • zipmapNext() during zipmap → listpack conversion uses 1 byte for any decoded length < 254.

A small length written in the overlong 5-byte form passes validation but makes zipmapNext() mis-stride by 4 bytes. Two consequences flow from the same mis-stride: a heap over-read (zipmap.c) and, in Redis only, a heap double-free in the rdb.c hash-zipmap loader:

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 guards this (if (!field_added) sdsfree(field)); upstream Redis did not, so the double-free is Redis-only. The fix rejects the overlong-encoded short length and reorders the load-time checks.

The exploit chain (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)

The fake dictType is planted inside a 16 MB string (SETRANGE) at the one offset whose address low bytes match the attacker string's sds header. ASLR is defeated entirely at runtime:

  • system — a single heap-pointer leak. The recommended path is DEBUG-free: the address of the redis.call Lua C-closure (EVAL 'return tostring(redis.call)') — the same self-contained leak our prior Redis exploit uses. The jemalloc arenas sit at a constant offset from libc, so system = leaked_robj + Δlibc + system_off. (DEBUG OBJECT is only a lab convenience when scripting happens to be disabled but DEBUG is enabled — the rarer config.)
  • the 16 MB blob's address (an independently-randomized mmap) — read with the bug's own arbitrary-read: forge a SET's dict so its member is a 107 KB SDS_TYPE_32 sds, SMEMBERS over-reads the adjacent heap, and the blob's robj.ptr is read at its known offset.

See WRITEUP.md for the full primitive-by-primitive analysis and the hard-won jemalloc / Redis-8.x details (class-64 boundary, dict entry tagging, hash-field mstr, keyspace pre-grow).

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"

The ASLR-defeat leak (no DEBUG)

The bootstrap heap-pointer leak follows the same approach as our prior Redis exploit: leak the redis.call Lua C-closure address with EVAL 'return tostring(redis.call)'. Lua scripting is on by default; DEBUG is off by default (enable-debug-command no) — so the Lua leak is the realistic primary, and DEBUG OBJECT (poc_rce_aslr.py) is only a lab convenience. poc_rce_aslr_selfcal.py then derives blob_base and blob_robj at runtime from the over-read (scan for the 16 MB-blob robj signature), so DLUA/DFOBJ only need to be close.

For the system finish the small-heap over-read contains no libc pointers, so libc can't be self-derived — poc_rce_aslr_selfcal.py keeps two libc-version offsets (DLIBC, SYSTEM_OFF). The poc_rce_aslr_pie_rop.py finish removes that dependency entirely: the same over-read does carry PIE pointers (a shared dictType appears repeatedly), so the PIE base is self-calibrated as most-common-PIE-value − DICTTYPE_OFF, and the chain ends in execve@plt via a JOP stack-pivot (mov rbp,rdi; call *0x8(rax) → leave;ret pivots rsp onto the attacker-controlled HGET field, which is the execve("/bin/sh","-c",<cmd>) ROP chain). The only build-specific constants are the PIE-relative gadget offsets read from — extract them per target with /, exactly as our prior HLL exploit keys a gadget table per ELF Build-ID. No libc address is used.

Screenshots

screenshots/05-pie-rop-libc-free.png (the NO libc self-cal + uid=0, the most self-contained run), 01-rce-aslr-on.png (the uid=0 money shot), 02-reliability.png (5/5), 03-exploit-chain.png (the code), and 04-debug-free-selfcal.png (the DEBUG-free, self-calibrating system run on a default Redis).

Impact & affected versions

  • Remote, no special privilege. RESTORE is an ordinary command — on an unauthenticated/exposed Redis (no requirepass) any connected client can run it; on an authed instance any user without an ACL -restore deny. Same access profile as the data-structure commands used by other Redis RCEs.
  • Confirmed: silent heap double-free, type confusion, arbitrary process-memory read (info-leak that exfiltrates keys/secrets/pointers), and remote code execution (this repo).
  • Affected: Redis < {6.2.22, 7.2.14, 7.4.9, 8.2.6, 8.4.3, 8.6.3} — i.e. 6.2.x through current 8.x; Valkey is DoS/over-read only (its field_added guard blocks the double-free).

Mitigation

Upgrade to a fixed release. If you cannot: restrict RESTORE (ACL … -restore), never expose Redis unauthenticated, and disable DEBUG.


Authorized security research, published for defender awareness. Do not run against systems you do not own or have explicit permission to test.

Download Tool
filewhat it demonstrates
★ exploits/poc_rce_aslr_pie_rop.pymost self-contained — RCE on a DEFAULT Redis (DEBUG off), NO libc offsets; self-calibrates blob_base/blob_robj/pie_base; execve@plt via JOP pivot (worker is replaced). 8/8.
★ exploits/poc_rce_aslr_selfcal.pyworker-surviving — same chain but hashFunction=&system (forks); costs two libc-version offsets (DLIBC/SYSTEM_OFF). Lua-closure leak, self-calibrating blob_base/blob_robj.
exploits/poc_rce_aslr_nodebug.pyDEBUG-free (Lua leak), but with fixed offsets (DEBUG-on calibrated)
exploits/poc_rce_aslr.pylab-convenience variant: DEBUG OBJECT bootstrap leak (needs DEBUG enabled)
exploits/poc_rce_aslr_off.pyRCE with ASLR off (calibrated addresses)
exploits/poc_typeconfusion.pydouble-free → two keys share one heap buffer (no tooling)
exploits/poc_doublefree.pythe double-free (ASan: heap-use-after-free in sdsfree)
exploits/poc_dos_overread.pythe over-read crash (ASan)
redis-server
ROPgadget
objdump