2023-12-13 16:18:04 +01:00
|
|
|
From d7ec1a5e11b1b0d806f2a0bdcf6a881064081651 Mon Sep 17 00:00:00 2001
|
2021-01-31 23:04:10 +01:00
|
|
|
From: "Five (Xer)" <admin@fivepb.me>
|
|
|
|
Date: Sat, 30 Jan 2021 18:04:14 +0100
|
|
|
|
Subject: [PATCH] Additional DoS mitigations
|
|
|
|
|
|
|
|
Some stricter length checks and cached exceptions to withstand more illegitimate connections
|
|
|
|
Courtesy of Tux and the Velocity Contributors. See:
|
|
|
|
https://github.com/VelocityPowered/Velocity/commit/5ceac16a821ea35572ff11412ace8929fd06e278
|
|
|
|
|
|
|
|
diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/DefinedPacket.java b/protocol/src/main/java/net/md_5/bungee/protocol/DefinedPacket.java
|
2023-12-05 19:31:22 +01:00
|
|
|
index 8751f271..8c9714fd 100644
|
2021-01-31 23:04:10 +01:00
|
|
|
--- a/protocol/src/main/java/net/md_5/bungee/protocol/DefinedPacket.java
|
|
|
|
+++ b/protocol/src/main/java/net/md_5/bungee/protocol/DefinedPacket.java
|
2023-12-05 19:31:22 +01:00
|
|
|
@@ -81,6 +81,7 @@ public abstract class DefinedPacket
|
2021-01-31 23:04:10 +01:00
|
|
|
int len = readVarInt( buf );
|
2022-08-20 18:43:03 +02:00
|
|
|
if ( len > maxLen * 3 )
|
2021-01-31 23:04:10 +01:00
|
|
|
{
|
|
|
|
+ if(!MinecraftDecoder.DEBUG) throw STRING_TOO_MANY_BYTES_EXCEPTION; // Waterfall start: Additional DoS mitigations
|
2022-08-20 18:43:03 +02:00
|
|
|
throw new OverflowPacketException( "Cannot receive string longer than " + maxLen * 3 + " (got " + len + " bytes)" );
|
2021-01-31 23:04:10 +01:00
|
|
|
}
|
|
|
|
|
2023-12-05 19:31:22 +01:00
|
|
|
@@ -89,6 +90,7 @@ public abstract class DefinedPacket
|
2022-08-20 18:43:03 +02:00
|
|
|
|
2021-01-31 23:04:10 +01:00
|
|
|
if ( s.length() > maxLen )
|
|
|
|
{
|
|
|
|
+ if(!MinecraftDecoder.DEBUG) throw STRING_TOO_LONG_EXCEPTION; // Waterfall start: Additional DoS mitigations
|
2021-09-29 04:37:21 +02:00
|
|
|
throw new OverflowPacketException( "Cannot receive string longer than " + maxLen + " (got " + s.length() + " characters)" );
|
2021-01-31 23:04:10 +01:00
|
|
|
}
|
|
|
|
|
2023-12-05 19:31:22 +01:00
|
|
|
@@ -550,4 +552,21 @@ public abstract class DefinedPacket
|
2021-01-31 23:04:10 +01:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public abstract String toString();
|
|
|
|
+
|
|
|
|
+ // Waterfall start: Additional DoS mitigations, courtesy of Velocity
|
|
|
|
+ private static final OverflowPacketException STRING_TOO_LONG_EXCEPTION
|
|
|
|
+ = new OverflowPacketException("A string was longer than allowed. For more "
|
|
|
|
+ + "information, launch Waterfall with -Dwaterfall.packet-decode-logging=true");
|
|
|
|
+ private static final OverflowPacketException STRING_TOO_MANY_BYTES_EXCEPTION
|
|
|
|
+ = new OverflowPacketException("A string had more data than allowed. For more "
|
|
|
|
+ + "information, launch Waterfall with -Dwaterfall.packet-decode-logging=true");
|
|
|
|
+
|
|
|
|
+ public int expectedMaxLength(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public int expectedMinLength(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+ // Waterfall end
|
|
|
|
}
|
|
|
|
diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/MinecraftDecoder.java b/protocol/src/main/java/net/md_5/bungee/protocol/MinecraftDecoder.java
|
2023-11-03 22:09:57 +01:00
|
|
|
index 52f76cd9..3a4a735c 100644
|
2021-01-31 23:04:10 +01:00
|
|
|
--- a/protocol/src/main/java/net/md_5/bungee/protocol/MinecraftDecoder.java
|
|
|
|
+++ b/protocol/src/main/java/net/md_5/bungee/protocol/MinecraftDecoder.java
|
|
|
|
@@ -3,7 +3,7 @@ package net.md_5.bungee.protocol;
|
|
|
|
import io.netty.buffer.ByteBuf;
|
|
|
|
import io.netty.buffer.ByteBufUtil;
|
|
|
|
import io.netty.channel.ChannelHandlerContext;
|
|
|
|
-import io.netty.handler.codec.DecoderException;
|
|
|
|
+import io.netty.handler.codec.CorruptedFrameException;
|
|
|
|
import io.netty.handler.codec.MessageToMessageDecoder;
|
|
|
|
import java.util.List;
|
|
|
|
import lombok.AllArgsConstructor;
|
2023-09-21 12:33:23 +02:00
|
|
|
@@ -58,10 +58,16 @@ public class MinecraftDecoder extends MessageToMessageDecoder<ByteBuf>
|
2021-01-31 23:04:10 +01:00
|
|
|
if ( packet != null )
|
|
|
|
{
|
|
|
|
packetTypeInfo = packet.getClass();
|
|
|
|
+ doLengthSanityChecks(in, packet, prot.getDirection(), packetId); // Waterfall: Additional DoS mitigations
|
2023-11-03 22:09:57 +01:00
|
|
|
packet.read( in, protocol, prot.getDirection(), protocolVersion );
|
2021-01-31 23:04:10 +01:00
|
|
|
|
|
|
|
if ( in.isReadable() )
|
|
|
|
{
|
|
|
|
+ // Waterfall start: Additional DoS mitigations
|
|
|
|
+ if(!DEBUG) {
|
|
|
|
+ throw PACKET_NOT_READ_TO_END;
|
|
|
|
+ }
|
|
|
|
+ // Waterfall end
|
2021-12-23 20:56:40 +01:00
|
|
|
throw new BadPacketException( "Packet " + protocol + ":" + prot.getDirection() + "/" + packetId + " (" + packet.getClass().getSimpleName() + ") larger than expected, extra bytes: " + in.readableBytes() );
|
2021-01-31 23:04:10 +01:00
|
|
|
}
|
|
|
|
} else
|
2023-11-03 22:09:57 +01:00
|
|
|
@@ -72,6 +78,25 @@ public class MinecraftDecoder extends MessageToMessageDecoder<ByteBuf>
|
2023-09-25 23:30:58 +02:00
|
|
|
out.add( new PacketWrapper( packet, slice, protocol ) );
|
2021-01-31 23:04:10 +01:00
|
|
|
slice = null;
|
|
|
|
} catch (BadPacketException | IndexOutOfBoundsException e) {
|
|
|
|
+ // Waterfall start: Additional DoS mitigations
|
|
|
|
+ if(!DEBUG) {
|
|
|
|
+ throw e;
|
|
|
|
+ }
|
|
|
|
+ // Waterfall end
|
2023-11-03 22:09:57 +01:00
|
|
|
+ final String packetTypeStr;
|
|
|
|
+ if (packetTypeInfo instanceof Integer) {
|
|
|
|
+ packetTypeStr = "id " + Integer.toHexString((Integer) packetTypeInfo);
|
|
|
|
+ } else if (packetTypeInfo instanceof Class) {
|
|
|
|
+ packetTypeStr = "class " + ((Class) packetTypeInfo).getSimpleName();
|
|
|
|
+ } else {
|
|
|
|
+ packetTypeStr = "unknown";
|
|
|
|
+ }
|
|
|
|
+ throw new FastDecoderException("Error decoding packet " + packetTypeStr + " with contents:\n" + ByteBufUtil.prettyHexDump(slice), e); // Waterfall
|
|
|
|
+ // Waterfall start
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
+ if (!DEBUG) {
|
|
|
|
+ throw e;
|
|
|
|
+ }
|
2021-01-31 23:04:10 +01:00
|
|
|
final String packetTypeStr;
|
|
|
|
if (packetTypeInfo instanceof Integer) {
|
|
|
|
packetTypeStr = "id " + Integer.toHexString((Integer) packetTypeInfo);
|
2023-11-03 22:09:57 +01:00
|
|
|
@@ -81,6 +106,7 @@ public class MinecraftDecoder extends MessageToMessageDecoder<ByteBuf>
|
|
|
|
packetTypeStr = "unknown";
|
|
|
|
}
|
|
|
|
throw new FastDecoderException("Error decoding packet " + packetTypeStr + " with contents:\n" + ByteBufUtil.prettyHexDump(slice), e); // Waterfall
|
|
|
|
+ // Waterfall end
|
|
|
|
} finally
|
|
|
|
{
|
|
|
|
if ( slice != null )
|
|
|
|
@@ -89,4 +115,52 @@ public class MinecraftDecoder extends MessageToMessageDecoder<ByteBuf>
|
2021-01-31 23:04:10 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
+
|
|
|
|
+ // Waterfall start: Additional DoS mitigations, courtesy of Velocity
|
|
|
|
+ public static final boolean DEBUG = Boolean.getBoolean("waterfall.packet-decode-logging");
|
|
|
|
+
|
|
|
|
+ // Cached Exceptions:
|
|
|
|
+ private static final CorruptedFrameException PACKET_LENGTH_OVERSIZED =
|
|
|
|
+ new CorruptedFrameException("A packet could not be decoded because it was too large. For more "
|
|
|
|
+ + "information, launch Waterfall with -Dwaterfall.packet-decode-logging=true");
|
|
|
|
+ private static final CorruptedFrameException PACKET_LENGTH_UNDERSIZED =
|
|
|
|
+ new CorruptedFrameException("A packet could not be decoded because it was smaller than allowed. For more "
|
|
|
|
+ + "information, launch Waterfall with -Dwaterfall.packet-decode-logging=true");
|
|
|
|
+ private static final BadPacketException PACKET_NOT_READ_TO_END =
|
|
|
|
+ new BadPacketException("Couldn't read all bytes from a packet. For more "
|
|
|
|
+ + "information, launch Waterfall with -Dwaterfall.packet-decode-logging=true");
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ private void doLengthSanityChecks(ByteBuf buf, DefinedPacket packet,
|
|
|
|
+ ProtocolConstants.Direction direction, int packetId) throws Exception {
|
|
|
|
+ int expectedMinLen = packet.expectedMinLength(buf, direction, protocolVersion);
|
|
|
|
+ int expectedMaxLen = packet.expectedMaxLength(buf, direction, protocolVersion);
|
|
|
|
+ if (expectedMaxLen != -1 && buf.readableBytes() > expectedMaxLen) {
|
|
|
|
+ throw handleOverflow(packet, expectedMaxLen, buf.readableBytes(), packetId);
|
|
|
|
+ }
|
|
|
|
+ if (buf.readableBytes() < expectedMinLen) {
|
|
|
|
+ throw handleUnderflow(packet, expectedMaxLen, buf.readableBytes(), packetId);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private Exception handleOverflow(DefinedPacket packet, int expected, int actual, int packetId) {
|
|
|
|
+ if (DEBUG) {
|
|
|
|
+ throw new CorruptedFrameException( "Packet " + packet.getClass() + " " + packetId
|
|
|
|
+ + " Protocol " + protocolVersion + " was too big (expected "
|
|
|
|
+ + expected + " bytes, got " + actual + " bytes)");
|
|
|
|
+ } else {
|
|
|
|
+ return PACKET_LENGTH_OVERSIZED;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private Exception handleUnderflow(DefinedPacket packet, int expected, int actual, int packetId) {
|
|
|
|
+ if (DEBUG) {
|
|
|
|
+ throw new CorruptedFrameException( "Packet " + packet.getClass() + " " + packetId
|
|
|
|
+ + " Protocol " + protocolVersion + " was too small (expected "
|
|
|
|
+ + expected + " bytes, got " + actual + " bytes)");
|
|
|
|
+ } else {
|
|
|
|
+ return PACKET_LENGTH_UNDERSIZED;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ // Waterfall end
|
|
|
|
}
|
|
|
|
diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/EncryptionResponse.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/EncryptionResponse.java
|
2022-12-07 17:30:18 +01:00
|
|
|
index 63e9d18d..545eec72 100644
|
2021-01-31 23:04:10 +01:00
|
|
|
--- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/EncryptionResponse.java
|
|
|
|
+++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/EncryptionResponse.java
|
2022-06-07 20:01:42 +02:00
|
|
|
@@ -63,4 +63,17 @@ public class EncryptionResponse extends DefinedPacket
|
2022-06-07 19:12:20 +02:00
|
|
|
private final long salt;
|
|
|
|
private final byte[] signature;
|
2021-01-31 23:04:10 +01:00
|
|
|
}
|
|
|
|
+
|
|
|
|
+ // Waterfall start: Additional DoS mitigations, courtesy of Velocity
|
|
|
|
+ public int expectedMaxLength(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
|
|
|
|
+ // It turns out these come out to the same length, whether we're talking >=1.8 or not.
|
|
|
|
+ // The length prefix always winds up being 2 bytes.
|
2022-06-07 20:01:42 +02:00
|
|
|
+ if (protocolVersion >= ProtocolConstants.MINECRAFT_1_19) return -1;
|
2021-01-31 23:04:10 +01:00
|
|
|
+ return 260;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public int expectedMinLength(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
|
|
|
|
+ return expectedMaxLength(buf, direction, protocolVersion);
|
|
|
|
+ }
|
|
|
|
+ // Waterfall end
|
|
|
|
}
|
|
|
|
diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/LoginRequest.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/LoginRequest.java
|
2023-09-21 12:33:23 +02:00
|
|
|
index e62a3a03..9789215c 100644
|
2021-01-31 23:04:10 +01:00
|
|
|
--- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/LoginRequest.java
|
|
|
|
+++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/LoginRequest.java
|
2023-09-21 12:33:23 +02:00
|
|
|
@@ -71,4 +71,13 @@ public class LoginRequest extends DefinedPacket
|
2021-01-31 23:04:10 +01:00
|
|
|
{
|
|
|
|
handler.handle( this );
|
|
|
|
}
|
|
|
|
+
|
|
|
|
+ // Waterfall start: Additional DoS mitigations, courtesy of Velocity
|
|
|
|
+ public int expectedMaxLength(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
|
|
|
|
+ // Accommodate the rare (but likely malicious) use of UTF-8 usernames, since it is technically
|
|
|
|
+ // legal on the protocol level.
|
2022-06-07 20:01:42 +02:00
|
|
|
+ if (protocolVersion >= ProtocolConstants.MINECRAFT_1_19) return -1;
|
2023-03-02 15:08:07 +01:00
|
|
|
+ return 1 + (16 * 3);
|
2021-01-31 23:04:10 +01:00
|
|
|
+ }
|
|
|
|
+ // Waterfall end
|
|
|
|
}
|
|
|
|
diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/PingPacket.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/PingPacket.java
|
|
|
|
index 5f24d425..3163a771 100644
|
|
|
|
--- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/PingPacket.java
|
|
|
|
+++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/PingPacket.java
|
|
|
|
@@ -7,6 +7,7 @@ import lombok.EqualsAndHashCode;
|
|
|
|
import lombok.NoArgsConstructor;
|
|
|
|
import net.md_5.bungee.protocol.AbstractPacketHandler;
|
|
|
|
import net.md_5.bungee.protocol.DefinedPacket;
|
|
|
|
+import net.md_5.bungee.protocol.ProtocolConstants;
|
|
|
|
|
|
|
|
@Data
|
|
|
|
@NoArgsConstructor
|
|
|
|
@@ -34,4 +35,14 @@ public class PingPacket extends DefinedPacket
|
|
|
|
{
|
|
|
|
handler.handle( this );
|
|
|
|
}
|
|
|
|
+
|
|
|
|
+ // Waterfall start: Additional DoS mitigations, courtesy of Velocity
|
|
|
|
+ public int expectedMaxLength(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
|
|
|
|
+ return 8;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public int expectedMinLength(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
|
|
|
|
+ return 8;
|
|
|
|
+ }
|
|
|
|
+ // Waterfall end
|
|
|
|
}
|
|
|
|
diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/StatusRequest.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/StatusRequest.java
|
|
|
|
index 738f0c92..ec33d337 100644
|
|
|
|
--- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/StatusRequest.java
|
|
|
|
+++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/StatusRequest.java
|
|
|
|
@@ -6,6 +6,7 @@ import lombok.EqualsAndHashCode;
|
|
|
|
import lombok.NoArgsConstructor;
|
|
|
|
import net.md_5.bungee.protocol.AbstractPacketHandler;
|
|
|
|
import net.md_5.bungee.protocol.DefinedPacket;
|
|
|
|
+import net.md_5.bungee.protocol.ProtocolConstants;
|
|
|
|
|
|
|
|
@Data
|
|
|
|
@NoArgsConstructor
|
|
|
|
@@ -28,4 +29,10 @@ public class StatusRequest extends DefinedPacket
|
|
|
|
{
|
|
|
|
handler.handle( this );
|
|
|
|
}
|
|
|
|
+
|
|
|
|
+ // Waterfall start: Additional DoS mitigations, courtesy of Velocity
|
|
|
|
+ public int expectedMaxLength(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+ // Waterfall end
|
|
|
|
}
|
|
|
|
--
|
2023-12-13 16:18:04 +01:00
|
|
|
2.43.0
|
2021-01-31 23:04:10 +01:00
|
|
|
|