Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
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
CVE-2026-25243 — Stable POC for CVE-2026-25243 (Redis RESTORE double-free -> remote code execution) | Kitploit
Tools/GitHubGitHub/captain-woof/cve-2026-25243
Vulnerability AnalysisExploitationPost-ExploitationPenetration TestingRed TeamingDatabase SecurityBinary Exploitation
GitHubcaptain-woof/cve-2026-25243

CVE-2026-25243

Stable POC for CVE-2026-25243 (Redis RESTORE double-free -> remote code execution)

View Repository
1131 month 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 double-free → remote code execution

Verified against Rocky Linux 8.10, aarch64, Redis 8.6.2, jemalloc 5.3.0.

Reference: https://www.zeroday.cloud/blog/redis-cve-2026-25243-deep-dive

TLDR; Stable exploit, works against variety of OS distro and architecture.


Executive Summary

What is this? A memory corruption vulnerability in Redis that lets an authenticated attacker run arbitrary commands as the Redis user. The attack is real-world and requires just a single RESTORE command — a normal Redis operation, not admin-only. This exploit demonstrates full RCE in under one second.

Impact? Any authenticated Redis client can trigger it, and the damage is total: arbitrary code execution in the Redis process (often running as root in containers). There is no way to mitigate without patching Redis itself.

How does it work at a glance? Redis has a serialization feature (RESTORE) that takes a blob of binary data and reconstructs it as a Redis object. The code that validates the blob's format and the code that deserializes it disagree on how to parse certain sequences — a bug that the attacker exploits to corrupt the heap. Once the heap is corrupted, the attacker gains the ability to read and write any memory address in the Redis process, and from there hijacks the server's internal state to execute a shell command.

The real exploit technique: This is not a simple crash. It's a heap exploitation chain: corrupt → overlap → arbitrary R/W → information leak → find the server struct → hijack function pointers → RCE. The exploit runs 9 stages and requires leaking multiple addresses at runtime, parsing binary structures, and detecting memory aliasing. What makes it work across architectures (x86-64, aarch64, etc.) is that all the addresses are leaked from the target itself, not assumed.


1. The vulnerability — in detail

CVE-2026-25243 is a pair of double-free bugs reachable from a single authenticated RESTORE command. RESTORE key ttl <serialized-value> deserializes an attacker-controlled RDB blob; both bugs live in the gap between the validator that checks the blob and the converter that materializes it.

Bug 1 — legacy zipmap conversion (CWE-415, the path this exploit uses). The zipmap validator (zipmapValidateIntegrity()) and the converter (zipmapNext()) disagree about a redundant length encoding. The small length 4 can legally be written in the long five-byte form FE 04 00 00 00. The validator consumes one number of bytes, the converter another — a 4-byte parsing desynchronisation. The converter therefore walks a different structure than the one that was validated, lpSafeToAdd() fails after the field has already been inserted into the dictionary, and the cleanup path frees the field twice: once via dictRelease() and again via sdsfree().

Bug 2 — stream consumer PEL loading (CWE-415). In rdbLoadStreamConsumersGroup(), a consumer PEL containing a duplicate entry ID makes the second raxTryInsert() fail, which calls streamFreeNACK() on a streamNACK that is still owned by the group's global PEL. Freed twice. (Selectable with --vuln-type stream.)

Either bug hands the attacker a chunk of memory that is simultaneously free and referenced — the classic starting point for a heap-overlap exploit.

Impact: an authenticated Redis client (no admin rights, RESTORE is a normal data command) gains arbitrary code execution as the redis user — root in the default container image.

2. How the exploit works

Nine stages, each of which turns a weaker primitive into a stronger one:

StagePrimitive gainedMechanism
0target profileINFO server / INFO memory → version, arch, distro, pid, executable path, start time, allocator
1double freemalformed zipmap (or stream) RESTORE
2two keys sharing memoryspray marker keys onto the freed chunk, detect aliasing, then overwrite one key's SDS header through its twin to inflate it to a 1 MB "memview"
3arbitrary R/Wfind an INCRBYFLOAT object inside the memview, hijack its ptr field: GETRANGE/SETRANGE on that key now read/write any address
4image pointerscan heap backwards for a value inside the redis-server image
5&serverwalk down to the ELF header, parse program headers, dump the writable segment, match server.pid
6payload in memorywrite "/bin/sh", "-c", "<cmd>" plus an argv array into the memview
7hijacked structoverwrite server.executable, server.exec_argv, and server.enable_debug_cmd
8RCEDEBUG CRASH-AND-RECOVER → restartServer() → execve(server.executable, server.exec_argv, environ)

How to trigger

python3 exploit.py --host 127.0.0.1 --port 6379 \
    --password mypassword --cmd 'id > /tmp/pwned123.txt'

Verify:

cat /tmp/pwned123.txt
# uid=0(root) gid=0(root) groups=0(root)

3. Change log

2026-08-06 — portability, reliability and speed rework

Starting point: the exploit was x86-64-only and died in stage 3 on the aarch64 target. End state: full RCE on aarch64 Rocky Linux 8.10 in under one second, 116 Redis commands.

a) Runtime target fingerprinting (new, stage 0). Nothing about the target is assumed any more. INFO server + INFO memory yield the Redis version, CPU architecture (from the os: line), distro family (inferred from gcc_version), allocator, and — most importantly — three validation anchors: process_id, executable, and the exact stat_starttime (server_time_usec/1e6 - uptime_in_seconds). Later stages compare against these instead of guessing.

b) Architecture-independent memory layout. The four hardcoded x86-64 constants (BINARY_ADDR_MIN/MAX, HEAP_ADDR_MIN/MAX) are replaced by a per-architecture table (ARCH_PROFILES) covering x86_64, aarch64 (both 39- and 48-bit VA), riscv64, ppc64le and s390x, with both the ET_EXEC and ET_DYN placements for each, plus a wide generic fallback for anything unlisted. This was the actual reason the exploit failed on this target: the leaked pointer 0x0000ffff8a5fdf32 is a perfectly good aarch64 mmap address that the x86-64 range check rejected.

c) Consensus-based leak validation (stage 3). Rather than trusting a hardcoded heap window, the scan now collects every structurally valid 1337.NNNNNN object in the memview and requires at least two of them to derive the same memview base address (ptr - offset_of_value). In practice 502 candidates agree, which is proof no range table can offer. The confirmed pointer then calibrates the heap window at runtime. Format validation was also moved before the (round-trip-expensive) write-control test.

d) Stage 3 scan bound (bug fix). The scan ran to a hardcoded 10 MB while the memview is 1 MB, so it read past the end, got an empty reply and aborted with AssertionError: Empty data from memview. It is now bounded by the memview's real STRLEN, reads 256 KB per round-trip instead of 64 KB, and the pointless 6×1s retry-sleep loop is gone.

e) Stage 5 rewritten: ELF-guided, crash-free (the big one). The old implementation scanned forward from an image pointer, probing addresses and reading whatever length a garbage SDS header claimed. On this target it walked straight off the end of the read-only segment into the unmapped hole at 0x715000 and killed the server (SIGSEGV in getrangeCommand → memcpy). Blind scanning cannot be made safe. The replacement is deterministic:

Download Tool