CVE-2021-33806 (published 2021-06-03T12:15Z)

The BDew BdLib library before 1.16.1.7 for Minecraft allows remote code execution because it deserializes untrusted data in ObjectInputStream.readObject as part of its use of Java serialization.

Severity: HIGH
CVSS v2 base score: 7.5 (AV:N/AC:L/Au:N/C:P/I:P/A:P)
CVSS v3 base score: 9.8, CRITICAL (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-502
cwe_name: Deserialization of Untrusted Data
description: The product deserializes untrusted data without sufficiently verifying that the resulting data will be valid.
extended_description: 
            It is often convenient to serialize objects for communication or to save them for later use. However, deserialized data or code can often be modified without using the provided accessor functions if it does not use cryptography to protect itself. Furthermore, any cryptography would still be client-side security -- which is a dangerous security assumption.
            Data that is untrusted can not be trusted to be well-formed.
            When developers place no restrictions on "gadget chains," or series of instances and method invocations that can self-execute during the deserialization process (i.e., before the object is returned to the caller), it is sometimes possible for attackers to leverage them to perform unauthorized actions, like generating a shell.
         
         

Fix information:
repo_url: https://github.com/bdew-minecraft/bdlib
commit_details: repo_url: https://github.com/bdew-minecraft/bdlib
author: bdew
author_date: 2021-05-31 17:53:01+03:00
msg: reworked custom network code to not use java serialization
repository: repo_url: https://github.com/bdew-minecraft/bdlib
repo_name: bdew-minecraft/bdlib
description: A library for minecraft modding
repo_language: Scala
owner: bdew-minecraft
file_changes: filename: build.properties
change_type: ModificationType.MODIFY
diff: @@ -1,5 +1,5 @@
 mod.id=bdlib
-mod.version=1.15.1
+mod.version=1.16.0
 curseforge.id=70496
 minecraft.version=1.16.5
 forge.version=36.1.0

programming_language: Batchfile
filename: OutputConfig.scala
change_type: ModificationType.MODIFY
diff: @@ -1,6 +1,6 @@
 package net.bdew.lib.multiblock.data
 
-import net.bdew.lib.multiblock.network.MsgOutputCfgPayload
+import net.bdew.lib.multiblock.network.MsgOutputCfg
 import net.minecraft.nbt.CompoundNBT
 import sun.reflect.generics.reflectiveObjects.NotImplementedException
 
@@ -8,14 +8,14 @@ abstract class OutputConfig {
   def id: String
   def read(t: CompoundNBT): Unit
   def write(t: CompoundNBT): Unit
-  def handleConfigPacket(m: MsgOutputCfgPayload): Unit
+  def handleConfigPacket(m: MsgOutputCfg): Unit
 }
 
 class OutputConfigInvalid extends OutputConfig {
   override val id: String = "invalid"
   def read(t: CompoundNBT): Unit = {}
   def write(t: CompoundNBT): Unit = throw new NotImplementedException
-  def handleConfigPacket(m: MsgOutputCfgPayload): Unit = throw new NotImplementedException
+  def handleConfigPacket(m: MsgOutputCfg): Unit = throw new NotImplementedException
 }
 
 object OutputConfigManager {

programming_language: Scala
filename: OutputConfigFluid.scala
change_type: ModificationType.MODIFY
diff: @@ -2,7 +2,7 @@ package net.bdew.lib.multiblock.data
 
 import net.bdew.lib.PimpVanilla._
 import net.bdew.lib.misc.RSMode
-import net.bdew.lib.multiblock.network.{MsgOutputCfgPayload, MsgOutputCfgRSMode}
+import net.bdew.lib.multiblock.network.{MsgOutputCfg, MsgOutputCfgRSMode}
 import net.minecraft.nbt.CompoundNBT
 
 import scala.collection.mutable
@@ -33,8 +33,8 @@ class OutputConfigFluid extends OutputConfig with OutputConfigRSControllable {
     t.putInt("rsMode", rsMode.id)
   }
 
-  def handleConfigPacket(m: MsgOutputCfgPayload): Unit = m match {
-    case MsgOutputCfgRSMode(r) => rsMode = r
+  def handleConfigPacket(m: MsgOutputCfg): Unit = m match {
+    case MsgOutputCfgRSMode(_, r) => rsMode = r
     case _ => sys.error("Invalid output config packet %s to config %s".format(m, this))
   }
 }

programming_language: Scala
filename: OutputConfigFluidSlots.scala
change_type: ModificationType.MODIFY
diff: @@ -1,6 +1,6 @@
 package net.bdew.lib.multiblock.data
 
-import net.bdew.lib.multiblock.network.{MsgOutputCfgPayload, MsgOutputCfgSlot}
+import net.bdew.lib.multiblock.network.{MsgOutputCfg, MsgOutputCfgSlot}
 import net.minecraft.nbt.CompoundNBT
 
 trait OutputConfigSlots {
@@ -18,8 +18,8 @@ class OutputConfigFluidSlots(val slotsDef: SlotSet) extends OutputConfigFluid wi
     super.write(t)
     t.putString("slot", slot.id)
   }
-  override def handleConfigPacket(m: MsgOutputCfgPayload): Unit = m match {
-    case MsgOutputCfgSlot(id) => slot = slotsDef.get(id)
+  override def handleConfigPacket(m: MsgOutputCfg): Unit = m match {
+    case MsgOutputCfgSlot(_, id) => slot = slotsDef.get(id)
     case _ => super.handleConfigPacket(m)
   }
 }

programming_language: Scala
filename: OutputConfigItems.scala
change_type: ModificationType.MODIFY
diff: @@ -1,7 +1,7 @@
 package net.bdew.lib.multiblock.data
 
 import net.bdew.lib.misc.RSMode
-import net.bdew.lib.multiblock.network.{MsgOutputCfgPayload, MsgOutputCfgRSMode}
+import net.bdew.lib.multiblock.network.{MsgOutputCfg, MsgOutputCfgRSMode}
 import net.minecraft.nbt.CompoundNBT
 
 class OutputConfigItems extends OutputConfig with OutputConfigRSControllable {
@@ -16,8 +16,8 @@ class OutputConfigItems extends OutputConfig with OutputConfigRSControllable {
     t.putInt("rsMode", rsMode.id)
   }
 
-  def handleConfigPacket(m: MsgOutputCfgPayload): Unit = m match {
-    case MsgOutputCfgRSMode(r) => rsMode = r
+  def handleConfigPacket(m: MsgOutputCfg): Unit = m match {
+    case MsgOutputCfgRSMode(_, r) => rsMode = r
     case _ => sys.error("Invalid output config packet %s to config %s".format(m, this))
   }
 

programming_language: Scala
filename: OutputConfigPower.scala
change_type: ModificationType.MODIFY
diff: @@ -1,7 +1,7 @@
 package net.bdew.lib.multiblock.data
 
 import net.bdew.lib.misc.RSMode
-import net.bdew.lib.multiblock.network.{MsgOutputCfgPayload, MsgOutputCfgRSMode}
+import net.bdew.lib.multiblock.network.{MsgOutputCfg, MsgOutputCfgRSMode}
 import net.minecraft.nbt.CompoundNBT
 
 class OutputConfigPower(var unit: String = "fe") extends OutputConfig with OutputConfigRSControllable {
@@ -27,8 +27,8 @@ class OutputConfigPower(var unit: String = "fe") extends OutputConfig with Outpu
     t.putString("unit", unit)
   }
 
-  def handleConfigPacket(m: MsgOutputCfgPayload): Unit = m match {
-    case MsgOutputCfgRSMode(r) => rsMode = r
+  def handleConfigPacket(m: MsgOutputCfg): Unit = m match {
+    case MsgOutputCfgRSMode(_, r) => rsMode = r
     case _ => sys.error("Invalid output config packet %s to config %s".format(m, this))
   }
 }

programming_language: Scala
filename: WidgetRSConfig.scala
change_type: ModificationType.MODIFY
diff: @@ -6,7 +6,7 @@ import net.bdew.lib.gui.{Point, Rect, Texture}
 import net.bdew.lib.misc.RSMode
 import net.bdew.lib.multiblock.data.OutputConfigRSControllable
 import net.bdew.lib.multiblock.interact.CIOutputFaces
-import net.bdew.lib.multiblock.network.{MsgOutputCfg, MsgOutputCfgRSMode, MultiblockNetHandler}
+import net.bdew.lib.multiblock.network.{MsgOutputCfgRSMode, MultiblockNetHandler}
 import net.minecraft.util.text.ITextComponent
 
 import java.util.Locale
@@ -34,6 +34,6 @@ class WidgetRSConfig(te: CIOutputFaces, output: Int, p: Point) extends WidgetSub
   )
 
   def clicked(b: WidgetButtonIcon): Unit = {
-    MultiblockNetHandler.sendToServer(MsgOutputCfg(output, MsgOutputCfgRSMode(next(cfg.rsMode))))
+    MultiblockNetHandler.sendToServer(MsgOutputCfgRSMode(output, next(cfg.rsMode)))
   }
 }

programming_language: Scala
filename: MsgOutputCfg.scala
change_type: ModificationType.MODIFY
diff: @@ -1,5 +1,5 @@
 package net.bdew.lib.multiblock.network
 
-trait MsgOutputCfgPayload extends Serializable
-
-case class MsgOutputCfg(output: Int, payload: MsgOutputCfgPayload) extends MultiblockNetHandler.Message
+trait MsgOutputCfg {
+  def output: Int
+}

programming_language: Scala
filename: MsgOutputCfgRSMode.scala
change_type: ModificationType.MODIFY
diff: @@ -1,5 +1,16 @@
 package net.bdew.lib.multiblock.network
 
 import net.bdew.lib.misc.RSMode
+import net.minecraft.network.PacketBuffer
 
-case class MsgOutputCfgRSMode(rsMode: RSMode.Value) extends MsgOutputCfgPayload
+case class MsgOutputCfgRSMode(output: Int, rsMode: RSMode.Value) extends MultiblockNetHandler.Message with MsgOutputCfg
+
+object CodecOutputCfgRSMode extends MultiblockNetHandler.Codec[MsgOutputCfgRSMode] {
+  override def encodeMsg(m: MsgOutputCfgRSMode, p: PacketBuffer): Unit = {
+    p.writeVarInt(m.output)
+    p.writeByte(m.rsMode.id)
+  }
+
+  override def decodeMsg(p: PacketBuffer): MsgOutputCfgRSMode =
+    MsgOutputCfgRSMode(p.readVarInt(), RSMode(p.readByte()))
+}

programming_language: Scala
filename: MsgOutputCfgSlot.scala
change_type: ModificationType.MODIFY
diff: @@ -1,3 +1,16 @@
 package net.bdew.lib.multiblock.network
 
-case class MsgOutputCfgSlot(slot: String) extends MsgOutputCfgPayload
+import net.minecraft.network.PacketBuffer
+
+case class MsgOutputCfgSlot(output: Int, slot: String) extends MultiblockNetHandler.Message with MsgOutputCfg
+
+object CodecOutputCfgSlot extends MultiblockNetHandler.Codec[MsgOutputCfgSlot] {
+  override def encodeMsg(m: MsgOutputCfgSlot, p: PacketBuffer): Unit = {
+    p.writeVarInt(m.output)
+    p.writeUtf(m.slot)
+  }
+
+  override def decodeMsg(p: PacketBuffer): MsgOutputCfgSlot = {
+    MsgOutputCfgSlot(p.readVarInt(), p.readUtf(100))
+  }
+}

programming_language: Scala
filename: MultiblockNetHandler.scala
change_type: ModificationType.MODIFY
diff: @@ -3,9 +3,17 @@ package net.bdew.lib.multiblock.network
 import net.bdew.lib.multiblock.interact.ContainerOutputFaces
 import net.bdew.lib.network.NetChannel
 
-object MultiblockNetHandler extends NetChannel("multiblock", "1") {
-  regServerContainerHandler(1, classOf[MsgOutputCfg], classOf[ContainerOutputFaces]) { (msg, cont, _) =>
-    cont.te.outputConfig(msg.output).handleConfigPacket(msg.payload)
+object MultiblockNetHandler extends NetChannel("multiblock", "2") {
+  private def forwardConfigMsg(m: MsgOutputCfg, cont: ContainerOutputFaces): Unit = {
+    cont.te.outputConfig(m.output).handleConfigPacket(m)
     cont.te.outputConfig.updated()
   }
+
+  regServerContainerHandler(1, CodecOutputCfgRSMode, classOf[ContainerOutputFaces]) { (msg, cont, _) =>
+    forwardConfigMsg(msg, cont)
+  }
+
+  regServerContainerHandler(2, CodecOutputCfgSlot, classOf[ContainerOutputFaces]) { (msg, cont, _) =>
+    forwardConfigMsg(msg, cont)
+  }
 }

programming_language: Scala
filename: BaseMessage.scala
change_type: ModificationType.MODIFY
diff: @@ -1,3 +1,3 @@
 package net.bdew.lib.network
 
-abstract class BaseMessage[CH <: NetChannel] extends Serializable
+abstract class BaseMessage[CH <: NetChannel]

programming_language: Markdown
filename: CompoundNBTSerialize.scala
change_type: ModificationType.DELETE
diff: @@ -1,23 +0,0 @@
-package net.bdew.lib.network
-
-import net.minecraft.nbt.CompoundNBT
-
-import java.io.{ObjectInputStream, ObjectOutputStream}
-
-class CompoundNBTSerialize(var tag: CompoundNBT = new CompoundNBT) extends Serializable {
-  private def writeObject(out: ObjectOutputStream): Unit = {
-    out.writeObject(NBTHelper.toBytes(tag))
-  }
-
-  private def readObject(in: ObjectInputStream): Unit = {
-    tag = NBTHelper.fromBytes(in.readObject().asInstanceOf[Array[Byte]])
-  }
-}
-
-object CompoundNBTSerialize {
-
-  import scala.language.implicitConversions
-
-  implicit def ser2content(v: CompoundNBTSerialize): CompoundNBT = v.tag
-  implicit def content2ser(v: CompoundNBT): CompoundNBTSerialize = new CompoundNBTSerialize(v)
-}

programming_language: unknown
filename: NetChannel.scala
change_type: ModificationType.MODIFY
diff: @@ -1,6 +1,5 @@
 package net.bdew.lib.network
 
-import io.netty.buffer.{ByteBufInputStream, ByteBufOutputStream}
 import net.bdew.lib.{BdLib, Misc}
 import net.minecraft.entity.player.ServerPlayerEntity
 import net.minecraft.inventory.container.Container
@@ -10,8 +9,8 @@ import net.minecraft.world.World
 import net.minecraftforge.fml.network.simple.SimpleChannel
 import net.minecraftforge.fml.network.{NetworkDirection, NetworkEvent, NetworkRegistry, PacketDistributor}
 
-import java.io.{ObjectInputStream, ObjectOutputStream}
 import java.util.Optional
+import scala.reflect.ClassTag
 
 class NetChannel(val name: String, val version: String) {
   val modId: String = Misc.getActiveModId
@@ -21,33 +20,29 @@ class NetChannel(val name: String, val version: String) {
 
   type Message = BaseMessage[this.type]
 
-  private def encodeMsg[M <: Message](m: Message, p: PacketBuffer): Unit = {
-    val writer = new ObjectOutputStream(new ByteBufOutputStream(p))
-    writer.writeObject(m)
-  }
-
-  private def decodeMsg[M <: Message](p: PacketBuffer): M = {
-    val reader = new ObjectInputStream(new ByteBufInputStream(p))
-    reader.readObject().asInstanceOf[M]
+  abstract class Codec[M <: Message : ClassTag] {
+    val cls: Class[M] = implicitly[ClassTag[M]].runtimeClass.asInstanceOf[Class[M]]
+    def encodeMsg(m: M, p: PacketBuffer): Unit
+    def decodeMsg(p: PacketBuffer): M
   }
 
   def init(): Unit = {
     BdLib.logInfo("Initialized network channel '%s' for mod '%s'", name, modId)
   }
 
-  def regServerHandler[M <: Message](id: Int, cls: Class[M])(handler: (M, NetworkEvent.Context) => Unit): Unit = {
-    channel.registerMessage[M](id, cls, (m, p) => encodeMsg[M](m, p), m => decodeMsg(m),
+  def regServerHandler[M <: Message](id: Int, codec: Codec[M])(handler: (M, NetworkEvent.Context) => Unit): Unit = {
+    channel.registerMessage[M](id, codec.cls, codec.encodeMsg, codec.decodeMsg,
       (m, cs) => handler(m, cs.get()), Optional.of(NetworkDirection.PLAY_TO_SERVER))
   }
 
-  def regServerContainerHandler[M <: Message, C <: Container](id: Int, cls: Class[M], cont: Class[C])(handler: (M, C, NetworkEvent.Context) => Unit): Unit = {
-    regServerHandler(id, cls) { (msg, ctx) =>
+  def regServerContainerHandler[M <: Message, C <: Container](id: Int, codec: Codec[M], cont: Class[C])(handler: (M, C, NetworkEvent.Context) => Unit): Unit = {
+    regServerHandler(id, codec) { (msg, ctx) =>
       Misc.asInstanceOpt(ctx.getSender.containerMenu, cont) foreach (cont => handler(msg, cont, ctx))
     }
   }
 
-  def regClientHandler[M <: Message](id: Int, cls: Class[M])(handler: M => Unit): Unit = {
-    channel.registerMessage[M](id, cls, (m, p) => encodeMsg[M](m, p), m => decodeMsg(m),
+  def regClientHandler[M <: Message](id: Int, codec: Codec[M])(handler: M => Unit): Unit = {
+    channel.registerMessage[M](id, codec.cls, codec.encodeMsg, codec.decodeMsg,
       (m, _) => handler(m), Optional.of(NetworkDirection.PLAY_TO_CLIENT))
   }
 

programming_language: Scala