← Back to Feed CACHED · 2026-09-26 22:30:31 · CACHE_KEY CVE-2026-23479
CVE-2026-23479 · CWE-416 · Disclosed 2026-05-05

Redis Use-After-Free in unblockClientOnKey Leading to Authenticated RCE

ASSESSED — NOISGATE
Vendor
—
—
—
Reassessed
—
—
—
Verdict: —
Do you agree?
01 · The Real Story

Someone left a loaded gun in the Redis client-unblocking code, and a full PoC just picked it up

CVE-2026-23479 is a use-after-free in Redis's unblockClientOnKey() function in src/blocked.c. When a blocked client (e.g., one waiting on XREAD BLOCK) is evicted due to maxmemory-clients pressure during command re-execution, processCommandAndResetClient() frees the client structure — but the caller never checks the return value indicating the client was freed, and proceeds to access the dangling pointer. An authenticated attacker with @admin + @scripting + @stream + @read/@write ACL permissions can groom the heap, reclaim the freed allocation with a crafted fake client structure, and ultimately achieve arbitrary command execution as the redis-server process. All Redis versions from 7.2.0 through 8.6.2 are affected across the 7.2.x, 7.4.x, 8.2.x, 8.4.x, and 8.6.x branches. Fixed versions shipped May 5, 2026: 7.2.14, 7.4.9, 8.2.6, 8.4.3, 8.6.3, plus a backport to 6.2.22. Redis Software (enterprise) is fixed in 8.0.10-64, 7.22.2-79, 7.8.6-253, 7.4.6-279, 7.2.4-153.

The NVD assigned CVSS 3.1 8.8 with AC:L, implying this is trivially exploitable by any low-privileged user. That overstates reality. Red Hat independently rescored to 7.5 with AC:H, noting *'a highly specific sequence of events and time-dependent conditions that are not directly in control of an attacker must occur.'* Redis's own GHSA uses CVSS 4.0 and arrived at 7.7. Both of those are closer to the truth. The chain is real — a weaponized Python PoC from v12-security achieves full RCE even under full RELRO — but it requires authenticated access with admin-class permissions, heap grooming across multiple stages, and precise timing of the eviction race. In default configurations without ACLs, the default user has every permission needed, which keeps the floor firmly at HIGH. But calling this AC:L misrepresents the exploitation engineering involved.

"Authenticated RCE in Redis via use-after-free; real complexity higher than NVD says, but a working PoC exists."
02 · The Attack Path

6 steps from start to impact.

STEP 01

Authenticated session with privileged ACLs

The attacker establishes a TCP connection to the Redis port (default 6379) and authenticates. In default deployments without ACL configuration, the default user possesses @admin, @scripting, @stream, @read, and @write — all permissions required for the chain. In ACL-hardened deployments, the attacker needs a credential mapped to these categories.
Conditions required:
  • Network reachability to Redis TCP port
  • Valid credential with @admin, @scripting, @stream, @read, @write permissions
Where this breaks in practice:
  • Enterprise deployments typically bind Redis to localhost or internal VLANs — fewer than 5% of enterprise Redis instances are internet-facing
  • ACL-hardened deployments restrict CONFIG SET and EVAL to ops accounts, not application service accounts
Detection/coverage: Shodan/Censys queries for product:Redis on port 6379; internal asset inventory should flag any internet-exposed Redis instances
STEP 02

Information leak via Lua evaluation

The attacker runs EVAL "return tostring(redis.call)" 0 to extract a heap pointer from the Lua function's string representation. This provides an address anchor for subsequent heap arithmetic, defeating ASLR. The @scripting permission is required.
Conditions required:
  • Authenticated session from step 1
  • EVAL command permitted
Where this breaks in practice:
  • Requires @scripting ACL — commonly restricted in production ACL policies
  • Some Redis deployments disable Lua scripting entirely via rename-command EVAL
Detection/coverage: Monitor Redis SLOWLOG and audit logs for EVAL commands containing tostring(redis.call) — this pattern has no legitimate use
STEP 03

Heap grooming and memory-limit manipulation

The attacker uses CONFIG SET maxmemory-clients to first inflate and then sharply reduce the client memory limit. This creates conditions for a target blocked client to be evicted during command re-execution. Multiple SET and GET commands groom the heap to place attacker-controlled data adjacent to the client allocation.
Conditions required:
  • CONFIG SET access (@admin ACL)
  • Ability to issue rapid sequences of SET/GET commands
Where this breaks in practice:
  • CONFIG SET maxmemory-clients is a runtime configuration change that may trigger alerts in config-drift monitoring
  • Heap layout must be deterministic — containerized or multi-tenant Redis may have unpredictable allocation patterns
Detection/coverage: Redis CONFIG change audit logging; anomalous CONFIG SET maxmemory-clients values in monitoring (e.g., setting to 1 byte)
STEP 04

Trigger use-after-free via blocked client eviction

The attacker blocks a client on a stream key via XREAD BLOCK 0, then simultaneously drops maxmemory-clients to 1 and wakes the stream via XADD. During unblockClientOnKey(), processCommandAndResetClient() frees the blocked client as a side-effect of eviction, but the function continues accessing the dangling pointer. A pipelined SET command reclaims the freed allocation with attacker-controlled data forming a fake client structure.
Conditions required:
  • Successful heap grooming from step 3
  • Precise timing of XADD and CONFIG SET to trigger eviction during re-execution
Where this breaks in practice:
  • This is a race condition — Red Hat explicitly notes 'time-dependent conditions that are not directly in control of an attacker'
  • The v12-security PoC handles this reliably on single-threaded Redis, but multi-instance or Sentinel setups may interfere
Detection/coverage: Redis crash logs with stack traces originating from handleClientsBlockedOnKey or unblockClientOnKey; unexpected redis-server restarts
STEP 05

Arbitrary write via updateClientMemoryUsage

The fake client structure contains attacker-controlled values for last_memory_type (array index) and last_memory_usage (decrement value). When updateClientMemoryUsage() runs server.stat_clients_type_memory[c->last_memory_type] -= c->last_memory_usage, it produces an arbitrary 8-byte decrement relative to the server global in the .data section. Under partial RELRO (default in official Docker images), the GOT is writable, allowing corruption of function pointers.
Conditions required:
  • Successful use-after-free reclaim from step 4
  • Partial RELRO on the redis-server binary (default for official Docker/package builds)
Where this breaks in practice:
  • Full RELRO binaries (some hardened distros) block GOT overwrites — BUT the v12-security PoC claims to work 'with full RELRO' via an alternative code path through the command lookup dictionary
  • Stack canaries and other mitigations may require additional bypass steps
STEP 06

Remote code execution as redis-server process

The attacker corrupts the strcasecmp() GOT entry (or the command lookup dictionary function pointer under full RELRO) to point at system(). Subsequent Redis command parsing triggers arbitrary OS command execution in the context of the redis-server process. From here, the attacker can exfiltrate all cached data, pivot laterally, or establish persistence.
Conditions required:
  • Successful arbitrary write from step 5
Where this breaks in practice:
  • Redis typically runs as a dedicated redis user, not root — limits immediate host-level impact
  • SELinux/AppArmor profiles on RHEL/Ubuntu may confine post-exploitation actions
  • Container-based Redis (Docker, K8s) limits blast radius to the container unless escape is chained
Detection/coverage: EDR/host-based detection of unexpected child processes spawned by redis-server; anomalous network egress from Redis host; file integrity monitoring on Redis directories
03 · Compensating Control

1
HIGH 7.5→MEDIUM 5.5
SEVERITY REDUCED
Configure Redis ACLs to restrict @admin and @scripting permissions — The exploitation chain requires CONFIG SET (@admin) and EVAL (@scripting). Strip these from all application service accounts and grant them only to dedicated ops identities authenticated via strong credentials. This breaks the chain at step 1 for most attacker scenarios. Deploy within the noisgate mitigation SLA of 30 days for HIGH severity.
2
HIGH 7.5→MEDIUM 5.0
SEVERITY REDUCED
Bind Redis to localhost or internal-only interfaces and enforce firewall rules — Ensure Redis listens only on 127.0.0.1 or a private VLAN interface via bind directive. Layer network ACLs (security groups, iptables, firewall policies) to restrict port 6379 access to known application hosts only. This eliminates the internet-facing attack surface entirely and limits internal exposure to compromised application-tier hosts. Deploy within 30 days.
3
HIGH 7.5→HIGH 7.0
Enable Redis protected-mode and require authentication — Set protected-mode yes and configure requirepass or ACL-based authentication. This prevents unauthenticated exploitation — relevant because ~11% of internet-facing Redis instances accept unauthenticated connections. Combined with ACL restrictions, this reduces the attacker population to credential-holding insiders. Deploy within 30 days.
4
HIGH 7.5→LOW 3.0
SEVERITY REDUCED
Rename or disable dangerous commands — Use rename-command EVAL "" and rename-command CONFIG "" in redis.conf to disable the two commands essential to the exploitation chain. This is a blunt but effective measure for instances that do not use Lua scripting or runtime config changes. Verify application compatibility before deploying. Deploy within 30 days.
5
HIGH 7.5→IGNORE 0.0
SEVERITY REDUCED
Upgrade to patched Redis version — Apply the vendor fix: 7.2.14, 7.4.9, 8.2.6, 8.4.3, or 8.6.3 depending on your branch. Redis Software customers should upgrade to 8.0.10-64 or the corresponding branch fix. This is the definitive remediation. Target the noisgate remediation SLA of 180 days for HIGH severity, but prioritize production data-tier instances within 90 days.
What doesn't work
  • TLS encryption on the Redis port — TLS protects data in transit but does not prevent an authenticated attacker from executing the exploit chain over an encrypted connection. The vulnerability is post-authentication.
  • Redis Sentinel or Cluster mode — HA configurations do not mitigate the underlying use-after-free. Each node in the cluster/sentinel group runs the vulnerable code independently. Failover may mask a crash but does not prevent exploitation.
  • Container isolation alone — Running Redis in Docker/K8s limits post-exploitation lateral movement but does not prevent the RCE itself. The attacker still achieves code execution within the container and can access all data Redis holds. Network policies within the orchestrator are needed as a complementary control.
04 · Intelligence Metadata

The supporting signals.

In-the-Wild ExploitationNo evidence. Redis vendor states *'as of this publication we have no evidence of exploitation of these vulnerabilities at Redis or in customer environments.'* Not listed on CISA KEV. No GreyNoise tags observed for this CVE.
Proof-of-ConceptFull weaponized PoC available. v12-security/pocs on GitHub — Python 3, achieves RCE as redis-server process, works with full RELRO, requires only TCP command interface. No Metasploit module or nuclei template found. PoC was not indexed on pocindex.io at time of review.
EPSS0.01506 (~1.5th percentile) — low predicted exploitation probability, consistent with the authenticated + high-complexity attack chain
KEV StatusNot listed. No CISA KEV entry as of 2026-09-27.
CVSS VectorsNVD: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H → 8.8. Red Hat: CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H → 7.5. GHSA (CVSS 4.0): 7.7. The AC:L vs AC:H disagreement is the key divergence — Red Hat's AC:H is more defensible given the multi-stage heap manipulation required.
Affected VersionsIntroduced in Redis 7.2.0. Affects 7.2.0–7.2.13, 7.4.0–7.4.8, 8.2.0–8.2.5, 8.4.0–8.4.2, 8.6.0–8.6.2. Redis < 7.2.0 (including 6.x, 7.0.x) is *not* vulnerable — the code path was introduced in 7.2.
Fixed VersionsOSS/CE: 6.2.22, 7.2.14, 7.4.9, 8.2.6, 8.4.3, 8.6.3. Redis Software: 8.0.10-64, 7.22.2-79, 7.8.6-253, 7.4.6-279, 7.2.4-153. Debian bookworm: 7.0.15-1~deb12u10 (not affected — ships 7.0.x). Debian sid: 8.0.6-3. Redis Cloud: auto-patched, no customer action.
Internet ExposureCensys reports ~350,675 internet-accessible Redis services across 260,534 hosts; ~11% (39,405) require no authentication. However, enterprise Redis is overwhelmingly internal — Shodan/Censys exposure is dominated by dev instances and cloud misconfigurations.
Disclosure & CreditDisclosed 2026-05-05 by Redis. Discovered by Team Xint Code (Tim Becker @tjbecker, Jacob Newman, Juno IM) through the Wiz ZeroDay.Cloud program.
Researcher PoC AuthorWeaponized PoC by v12-security (published on GitHub). Separate from the original discovery team.

Sources.

  1. Redis Official Security Advisory
  2. GitHub Security Advisory GHSA-93m2-935m-8rj3
  3. Red Hat CVE Page — CVE-2026-23479
  4. ZeroDay.cloud Deep Dive
  5. v12-security PoC Repository
  6. Debian Security Tracker
  7. Censys — Exposed Redis Instances
  8. Security Boulevard Analysis
05 · The Call

Final Verdict
= UNCHANGED to HIGH (7.5/10)

Why this verdict

  • AC:H is correct, not AC:L. The chain requires four distinct stages — info leak, heap grooming, race-condition trigger, and GOT/dict corruption — each with specific timing and memory-layout dependencies. Red Hat's AC:H assessment at 7.5 better reflects the engineering effort, even though a PoC automates it.
  • Authentication is a real gate but a weak one in defaults. The chain requires @admin + @scripting + @stream permissions, which the default user has in unconfigured Redis. In ACL-hardened deployments, application service accounts rarely hold @admin. This narrows the reachable population to default-config and weakly-configured instances — a substantial fraction but not universal.
  • No in-the-wild exploitation, no KEV, low EPSS (1.5%). Five months post-disclosure with a public PoC and still no observed exploitation suggests the complexity barrier is real. The PoC exists but hasn't been operationalized in campaigns.
  • Network exposure is mostly internal. Enterprise Redis is overwhelmingly bound to internal interfaces. Censys's 350K internet-facing count is dominated by dev/cloud misconfigs, not enterprise production. The attacker position is typically post-initial-access for enterprise targets.
  • Role multiplier: Production data tier. Redis is canonically deployed as a production cache, session store, and message broker — fitting the *'production database engines on data tiers'* high-value role category. In this role, RCE yields host-level compromise + access to all cached sessions/tokens/application data + lateral pivot to the application tier. This blast radius at the host-to-tenant level floors the verdict at HIGH regardless of friction.
  • Role multiplier: CI/CD and container infrastructure. Redis is commonly deployed in Kubernetes clusters and CI pipelines. Compromising Redis in these roles can enable lateral movement within the orchestration layer. However, container isolation limits the blast radius to the pod/container unless a container escape is chained, which is an additional prerequisite not part of this CVE. This role supports the HIGH floor but does not elevate to CRITICAL.

Why not higher?

The chain does not reach CRITICAL because Redis is not *canonically* an identity-tier, hypervisor, or supply-chain component — it is a data-tier cache/broker. RCE on a Redis host yields host-to-tenant blast radius (session data, cached tokens), not domain/fleet/supply-chain scale. The authentication requirement with admin-class permissions, combined with high attack complexity and the predominantly internal network posture, prevents this from being a 'patch tonight' emergency. No in-the-wild exploitation after five months with a public PoC reinforces that the theoretical severity exceeds the practical risk.

Why not lower?

A weaponized PoC achieving full RCE exists and is publicly available. Default Redis configurations grant all required permissions to the default user — meaning a significant fraction of real deployments are exploitable as-is by any authenticated user. Redis holds production data (sessions, auth tokens, rate-limit state) in most enterprise deployments, making the post-exploitation impact material. The deployment-role floor analysis places Redis in the 'production database engine on data tiers' category, which floors the verdict at HIGH. Dropping to MEDIUM would ignore the demonstrated RCE capability and the data-tier blast radius.

06 · Verification

Crowdsourced verification payload.

Run this script on each host running Redis, or from an auditor workstation with redis-cli installed and network access to the target. Invoke as: bash check_cve_2026_23479.sh <redis-host> <redis-port> [redis-password]. No elevated privileges required — uses redis-cli INFO server to read the version.

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/usr/bin/env bash
# check_cve_2026_23479.sh — Detect CVE-2026-23479 (Redis UAF in unblockClientOnKey)
# Usage: bash check_cve_2026_23479.sh <host> <port> [password]
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN

set -euo pipefail

HOST="${1:-127.0.0.1}"
PORT="${2:-6379}"
PASS="${3:-}"

AUTH_ARGS=()
if [[ -n "$PASS" ]]; then
  AUTH_ARGS=(-a "$PASS" --no-auth-warning)
fi

# Fetch redis version
VERSION=$(redis-cli -h "$HOST" -p "$PORT" "${AUTH_ARGS[@]}" INFO server 2>/dev/null \
  | grep -E '^redis_version:' | cut -d: -f2 | tr -d '\r')

if [[ -z "$VERSION" ]]; then
  echo "UNKNOWN — could not connect to Redis at $HOST:$PORT or parse version."
  exit 2
fi

echo "Detected Redis version: $VERSION"

# Parse major.minor.patch
IFS='.' read -r MAJOR MINOR PATCH <<< "$VERSION"

# Versions < 7.2.0 are NOT affected (vulnerable code introduced in 7.2.0)
if (( MAJOR < 7 )) || { (( MAJOR == 7 )) && (( MINOR < 2 )); }; then
  echo "PATCHED — Redis $VERSION predates the vulnerable code (introduced in 7.2.0)."
  exit 0
fi

# Check each affected branch against its fixed version
VULN=0
if (( MAJOR == 7 && MINOR == 2 )); then
  (( PATCH < 14 )) && VULN=1
elif (( MAJOR == 7 && MINOR == 4 )); then
  (( PATCH < 9 )) && VULN=1
elif (( MAJOR == 8 && MINOR == 2 )); then
  (( PATCH < 6 )) && VULN=1
elif (( MAJOR == 8 && MINOR == 4 )); then
  (( PATCH < 3 )) && VULN=1
elif (( MAJOR == 8 && MINOR == 6 )); then
  (( PATCH < 3 )) && VULN=1
elif (( MAJOR >= 9 )); then
  # Future major version — assume patched
  VULN=0
else
  # Other 7.x or 8.x minor branches (e.g., 7.3, 8.0, 8.1) —
  # likely affected if >= 7.2 but not in a known-fixed branch.
  echo "UNKNOWN — Redis $VERSION is on an unrecognized branch. Check vendor advisory."
  exit 2
fi

if (( VULN == 1 )); then
  echo "VULNERABLE — Redis $VERSION is affected by CVE-2026-23479."
  echo "Upgrade to the fixed version for your branch (7.2.14 / 7.4.9 / 8.2.6 / 8.4.3 / 8.6.3)."
  exit 1
else
  echo "PATCHED — Redis $VERSION includes the fix for CVE-2026-23479."
  exit 0
fi