A remote, authenticated code-execution exploit for Redis, reached through the RedisBloom module's TDigest RDB deserializer using nothing but the Redis protocol. No file upload, no debugger, no host access — and no hardcoded offsets: every address is resolved at runtime from the running process.
The entire chain — from finding the bug to a working exploit — was discovered and built end-to-end by an autonomous AI agent, with no manual reverse engineering or offset research.
⚠️ Still reproducible on the latest Redis release. Verified against the officialredis:latestimage, pinned to the exact buildsha256:aa049e689e141a4358ad1d4562dc49c88a89fbab711fd8fcc33f684c80b26301(the:latestsnapshot at time of testing). Default config, RedisBloom loaded by default, no config changes.
When Redis loads a TDigest key from an RDB payload, TDigestRdbLoad sizes its internal nodes_mean[] / nodes_weight[] arrays from a compression field (6 * compression + 10 entries). It then overwrites its own cap with a value read straight out of the payload, and the bounds check that guards the subsequent writes is performed against that attacker-controlled cap instead of the real allocation. Pair a small allocation with a large attacker-supplied merged_nodes/cap and you get a clean heap out-of-bounds write.
That single primitive is escalated — heap pointer leak → arbitrary read → libc / module resolution → overwrite a writable function pointer → system() — into code execution as the redis user.
I reported this to Redis on 2026-06-04. Redis's response:
The original report was submitted on 2025-12-29 and is currently being evaluated by the program's security team. Two other reports (#3618562 and #3676797) describing this same vulnerability have also been closed as duplicates of the original submission.
In other words: the bug was first reported to Redis on 2025-12-29 — about seven months ago — and is still being evaluated. My report, along with two other independent reports, was closed as a duplicate of that original submission, which has yet to produce a fix in the latest release. It is published here for educational purposes given that prolonged unfixed window.
# 1. Start the exact tested image, pinned by digest (:latest is a moving tag)
./redis_setup.sh # launches the pinned redis:latest snapshot on :6379
# equivalent manual run:
# docker run -d --name redis-latest -p 6379:6379 \
# redis@sha256:aa049e689e141a4358ad1d4562dc49c88a89fbab711fd8fcc33f684c80b26301
# 2. Run the exploit (pure remote — no docker exec, no host access)
python3 exploit_chain_8.8.0.py --host 127.0.0.1 --port 6379
# 3. Check the proof
docker exec redis-latest cat /tmp/pwned # uid=999(redis) ...Remote or custom target:
python3 exploit_chain_8.8.0.py --host 10.0.0.5 --port 6379 -v-v traces each phase; the script retries the full chain automatically if the server restarts mid-run.
- Python 3.6+ — standard library only, no pip packages
- Docker (to spin up the demo target), or any reachable Redis + RedisBloom instance
- Authenticated Redis protocol access — any client that can issue commands to the target (a no-auth instance is the trivial case)
- Target allows
RESTORE,DUMP,DEL,TDIGEST.ADD,EVAL,CONFIG,PING(all default-enabled) - Target architecture: x86_64 / amd64 only — not validated on aarch64/arm64 (see Notes)
exploit_chain_8.8.0.py— full RCE exploit (pure Python, stdlib only)redis_setup.sh— demo: start the pinnedredis:latestsnapshot and verify preconditions (prints the image digest and version for confirmation)
- Reproducibility. The
:latesttag is mutable; the demo script and the digest above pin the exact vulnerable build.redis_setup.shprints the image'sRepoDigestsat startup — confirm it matchessha256:aa049e689e141a4358ad1d4562dc49c88a89fbab711fd8fcc33f684c80b26301. - Latest Redis is affected. Confirmed against
redis:latest(pinned digest above) and against theredis:8.8.0/redis:8.6.3tags; RCE achieved with the server surviving the run. - x86_64 only. Verified solely on x86_64 / amd64. It is not expected to work as-is on aarch64/arm64: the runtime address-resolution heuristics (libc/code pointer address-range filters and code-pointer page-remainder signatures used to locate
system()and themoduleType->freetrampoline) are tuned for x86_64 Linux, and the jemalloc slab-adjacency assumptions have not been validated on aarch64. Porting would require re-tuning those heuristics — untested. - The exploit sets
sanitize-dump-payload noitself viaCONFIG SET. This is already the default in Redis 7.x / 8.x, so official images need no change. - No offsets or calibration required. All addresses (libc base,
system(), the writable function pointer used as thesystem()trampoline) are resolved at runtime from the running process, so it adapts to other builds.
This repository is provided strictly for educational and defensive security research. Run it only against systems you own or are explicitly authorized to test. Unauthorized use against any third-party system is prohibited.