Blog

DarkReplica (CVE-2026-23631): how Redis handed over RCE through its replication mechanism

DarkReplica-CVE-2026-23631-redis
CVE / Database / Linux / Security

DarkReplica (CVE-2026-23631): how Redis handed over RCE through its replication mechanism

Redis sits behind half the modern web — session caches, task queues, rate limiting, pub/sub. That’s exactly why vulnerabilities in it rarely stay theoretical. CVE-2026-23631, named DarkReplica, is a use-after-free in the replication subsystem that under certain conditions opens the door to arbitrary code execution on the server. Researcher Yoni Sherez found it at the ZeroDay.Cloud 2025 competition in London in December last year and walked away with $30,000. The patch landed on May 5, 2026. If you haven’t updated yet — read on.

WHAT HAPPENED

CVE-2026-23631 is a use-after-free vulnerability in the Redis replication mechanism. CVSS 6.1 (Medium under CVSSv4.0), CWE-416. The official Redis description: an authenticated user can exploit the master-replica synchronization mechanism to trigger a use-after-free in the Lua functions engine, potentially leading to remote code execution. The vulnerability affects all versions of Redis with Lua scripting support — which is essentially all versions in real-world use. The one constraint: authentication is required. A proof of concept is published in the researcher’s writeup.

A note on the CVSS score: some sources cite 8.5 High — that’s an older CVSS v2 figure from a secondary source. The official score from Redis and NVD under CVSSv4.0 is 6.1 Medium, with high attack complexity (AC:H). That doesn’t mean the vulnerability is harmless. It means several conditions have to align for successful exploitation — but Yoni Sherez demonstrated that it’s achievable.

WHAT IS REDIS REPLICATION AND THE LUA ENGINE

Redis supports master-replica replication: one server is designated as the primary, the rest are replicas. A replica connects to the master, receives a full sync on startup, and then accepts a continuous stream of commands in real time. The SLAVEOF command is used to assign a master. This is the standard mechanism for high availability and read scaling.

Redis also has a built-in Lua engine — two of them, actually. The first is the scripting engine (EVAL, EVALSHA, SCRIPT LOAD) — the older model for running arbitrary Lua scripts. The second is the functions engine (FUNCTION LOAD, FCALL) — a newer design that lets you register named function libraries, which are stored in RDB/AOF and synchronized across cluster nodes. It’s this functions engine that became the entry point for DarkReplica.

HOW THE BUG WORKS

Redis is single-threaded. When a Lua function runs for too long — by default, more than 5 seconds — the server can’t process new requests in the normal way. To avoid locking up entirely, Redis installs a hook via lua_sethook before running any Lua code. This hook fires every 100,000 Lua instructions and calls processEventsWhileBlocked(), which drains pending events from the event loop and keeps the server responsive even while a slow function is running.

The critical detail: processEventsWhileBlocked() doesn’t just handle regular client requests — it processes all I/O events, including commands arriving from the master server through the replication channel. And nothing in that path checks whether a Lua function is currently executing. Regular clients trying to run a disallowed command mid-script get a BUSY Redis is busy running a script error. But the same FUNCTION FLUSH command arriving from the master over the replication channel goes right through.

The result: the Lua engine gets freed from memory by a master command at the exact moment a function is still running against it. The Lua environment is gone — but execution keeps touching memory that’s already been freed. That’s a textbook use-after-free: a freed Lua engine still in use. CWE-416 in its purest form.

HOW IT IS EXPLOITED

An attacker with authenticated access to a Redis server follows this sequence. First, they trigger a slow Lua function via FCALL — one that runs long enough for Redis to hit the timeout threshold and start calling processEventsWhileBlocked(). Then, through the replication channel — by setting up a controlled “master” — they send a FUNCTION FLUSH command. Redis accepts it and destroys the Lua engine while the function is still mid-execution.

Once use-after-free is triggered, Yoni Sherez developed a set of techniques to read and write arbitrary process memory. In his words, exploitation required deep familiarity with Redis internals and the Lua 5.1 virtual machine — this isn’t a one-click exploit. But the end result was achieved: arbitrary system commands executing on the target server under the Redis process identity. The full technical writeup is published on ZeroDay.Cloud.

WHAT HAPPENS NEXT — DEPENDS ON CONFIGURATION

The vulnerability only triggers when several conditions are met simultaneously: authenticated access to Redis, the ability to set up a replication channel, Lua scripting enabled (it’s on by default), and a replica running with replica-read-only disabled. If Redis isn’t using replication at all, the master-channel attack vector isn’t available. If Redis is only reachable from localhost or a tightly controlled private network, the risk drops significantly.

The most exposed setups are those where Redis is accessible to multiple clients, replication is in use, and authentication is weak or missing. That combination turns up regularly in dev environments and on servers that were “quickly spun up” without following any hardening guidelines. Redis Cloud instances are protected — the provider has already deployed the patches.

REAL-WORLD ATTACK CHAIN

Here’s a realistic scenario. A web application uses Redis for caching and sessions. Redis has a password, but it’s weak or has leaked. The attacker connects directly — through an exposed port 6379 or through an application vulnerability that allows arbitrary Redis calls. The server is running Redis 7.4.8, unchanged since installation. The attacker fires a slow Lua function via FCALL, simultaneously spins up a controlled Redis instance, and uses SLAVEOF to designate it as the master for the target. When the Lua timeout fires and Redis enters processEventsWhileBlocked(), the FUNCTION FLUSH from the fake master destroys the Lua engine. Use-after-free opens the path to RCE. Redis is running as the redis user — the attacker gets a shell with those privileges, reads data from memory, and gains access to session data and application cache.

TIMELINE

December 2025. The ZeroDay.Cloud 2025 competition in London. Yoni Sherez picks Redis as his target — “because of its complexity and large and interesting attack surface,” as he wrote in the writeup. He finds DarkReplica. Collects $30,000. January 2026 — CVE reserved. May 5, 2026 — Redis ships the patch across all five maintained release branches in a single day. June 2, 2026 — Yoni Sherez publishes the full technical writeup on ZeroDay.Cloud. Six months between the find and public disclosure. The patch was out a month before the technical details went public. That’s responsible disclosure working exactly as it should.

WHY THIS MATTERS

Redis isn’t a niche tool. It’s the cache sitting behind your nginx, the session store for your WordPress, the task queue your application depends on. It runs on nearly every production server doing anything more than serving static files. DarkReplica affects all versions of Redis with Lua scripting — which means every version actually deployed in production. That’s why CVSS 6.1 shouldn’t be a reason to relax: “Medium” is a measure of attack complexity, not of impact. RCE is RCE, regardless of how many conditions it takes to get there.

Then there’s Redis without authentication, or with a weak password, exposed directly to the internet. For those instances, the “authentication required” condition simply disappears from the equation. DarkReplica with replication becomes a direct path to RCE. And there are more such servers than anyone would like to admit — Redis was historically designed to run inside a trusted network, and plenty of administrators still act like that’s where it lives.

UPDATE

The patch landed on May 5, 2026 for all supported release branches. Check your current Redis version — the first command shows the client version, the second queries the running server. If Redis is password-protected, pass the password with the -a flag; if not, drop it:

redis-cli --version
redis-cli INFO server | grep redis_version
# if Redis requires a password:
redis-cli -a YOUR_PASSWORD INFO server | grep redis_version

Then check against the patched version table. If your version falls within the affected range, update immediately.

Affected branches and patched versions per the official Redis advisory:

  • Redis 7.2.x: affected 7.2.0 – 7.2.13, fixed in 7.2.14
  • Redis 7.4.x: affected 7.4.0 – 7.4.8, fixed in 7.4.9
  • Redis 8.2.x: affected 8.2.0 – 8.2.5, fixed in 8.2.6
  • Redis 8.4.x: affected 8.4.0 – 8.4.2, fixed in 8.4.3
  • Redis 8.6.x: affected 8.6.0 – 8.6.2, fixed in 8.6.3

Updating on Ubuntu/Debian depends on where Redis was installed from. If it came from the official redis.io repository, make sure that repository is connected, then update. If it hasn’t been added yet, here are the official redis.io instructions:

sudo apt-get install -y lsb-release curl gpg
curl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg
sudo chmod 644 /usr/share/keyrings/redis-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/redis.list
sudo apt-get update
sudo apt-get install redis

If the repository is already configured, updating is straightforward:

sudo apt-get update
sudo apt-get install redis
redis-cli --version

One important note: Ubuntu and Debian ship an outdated version of Redis from their standard repositories — typically 6.x or 7.0. Running sudo apt install redis-server without the official redis.io repository won’t get you to the patched version. Only packages.redis.io carries the current 7.2, 7.4, and 8.x branches. After updating, restart the service:

sudo systemctl restart redis-server
sudo systemctl status redis-server

If updating isn’t possible right now, Redis officially documents two workarounds. The first is to block Lua script execution: restrict the EVAL, EVALSHA, FCALL, and FCALL_RO commands via ACL for any users who don’t need that functionality. The exact syntax depends on your Redis version and ACL configuration — refer to the official Redis ACL documentation. The second workaround is to make sure all replicas run with replica-read-only yes. That’s the default, but it’s worth verifying explicitly. On each replica, run:

redis-cli CONFIG GET replica-read-only

If the output shows no, the replica is vulnerable. Turn it back on:

redis-cli CONFIG SET replica-read-only yes

To make the change stick across restarts, add this to /etc/redis/redis.conf:

replica-read-only yes

Both workarounds reduce exposure but don’t fix the underlying issue — updating is the only complete solution.

Regardless of update status, make sure Redis isn’t reachable from the internet directly. Port 6379 should be closed on the firewall for everyone except trusted IPs. Check via nftables:

sudo nft list ruleset | grep 6379

If the output is empty, there’s no explicit rule for that port — Redis is either protected by a bind-address in its config or exposed to everyone. Verify:

grep -E "^bind|^protected-mode" /etc/redis/redis.conf

bind 127.0.0.1 means Redis is only listening on localhost. protected-mode yes is an extra layer that blocks external connections without a password. Both should be set correctly.

CONCLUSIONS

DarkReplica is a good reminder that post-authentication doesn’t mean low risk. Authentication in Redis often means a single password in an application’s connection string, not real isolation. The replication mechanism is a part of Redis that rarely gets treated as an attack surface — which is exactly where Yoni Sherez found a use-after-free nobody was expecting. Update Redis to the patched version for your branch: 7.2.14, 7.4.9, 8.2.6, 8.4.3, or 8.6.3. Close port 6379 from the internet, review your replication settings — and this attack vector is closed.

Leave your thought here

Your email address will not be published. Required fields are marked *

Select the fields to be shown. Others will be hidden. Drag and drop to rearrange the order.
  • Image
  • SKU
  • Rating
  • Price
  • Stock
  • Availability
  • Add to cart
  • Description
  • Content
  • Weight
  • Dimensions
  • Additional information
Click outside to hide the comparison bar
Compare