Jun 25, 2026·7 min read·3 visits
MessagePack-CSharp prior to 2.5.301 and 3.1.7 fails to recursively validate elements of generic collections or arrays against type blocklists, enabling unauthenticated remote code execution via nested deserialization gadgets.
A critical vulnerability exists in MessagePack-CSharp's typeless deserialization mechanism where configured blocklists fail to recursively inspect nested types. An attacker can bypass security restrictions by wrapping unauthorized types in arrays or generic collections, allowing insecure deserialization and remote code execution.
MessagePack-CSharp is a high-performance binary serialization library utilized widely within the .NET ecosystem to achieve rapid serialization and deserialization of structured data. The library is commonly integrated into distributed services, caching layers, and high-throughput web applications where data interchange performance is a critical factor. To accommodate dynamic programming scenarios and polymorphic type hierarchies, MessagePack-CSharp implements a "typeless" serialization mode that encodes assembly-qualified type names alongside the serialized data payloads, allowing the system to resolve the target types dynamically at runtime.\n\nThis typeless serialization capability significantly increases the attack surface of the application by allowing the deserialization engine to instantiate arbitrary classes based on untrusted inputs. If an attacker can specify arbitrary assembly-qualified names, they can instruct the engine to instantiate known dangerous classes—often referred to as execution gadgets—present in the application's runtime context. To defend against these insecure deserialization pathways (CWE-502), MessagePack-CSharp implements a blocklist validator, ThrowIfDeserializingTypeIsDisallowed, which performs validation checks on incoming type signatures prior to object initialization.\n\nCVE-2026-48517 describes an architectural validation bypass flaw within this safety mechanism. The validation routines were historically built to assess only the top-level outer type of the incoming serialized object. Consequently, if an attacker encapsulates a blacklisted type inside a benign container—such as an array or a generic collection—the validator approves the container type because it is not explicitly present on the static blocklist. This non-recursive validation structure permits the parsing engine to subsequently resolve and instantiate the underlying restricted gadget types, leading to arbitrary code execution.
The root cause of CVE-2026-48517 lies in the non-recursive implementation of the type verification logic within the default options class. Prior to the patch, the ThrowIfDeserializingTypeIsDisallowed(Type) method checked only the literal string representation of the root-level type name against a static list of disallowed names. This architecture assumes that any potentially malicious classes are parsed as isolated, top-level objects, failing to account for object models where dangerous classes are embedded within structural types.\n\nUnder typeless serialization, when an incoming payload represents a generic collection or an array, the parser resolves the root-level type first. For instance, serializing a blocked gadget like System.Diagnostics.Process within an array yields System.Diagnostics.Process[] as the primary type signature. For generic lists, the runtime resolves the collection as System.Collections.Generic.List1[[System.Diagnostics.Process, ...]]. The validator receives this composite type structure and performs its string-matching evaluation against the full name of the root collection class.\n\nBecause common collection structures such as System.Array and System.Collections.Generic.List1 are benign and necessary for normal application functionality, they cannot be placed on a global blocklist. Since the validation routine evaluated only the top-level collection class and completely omitted recursive checking of underlying generic argument types or array element types, the validation check completed without throwing an exception. The formatter engine then executed standard elements deserialization, which resolved, allocated, and instantiated the inner, unauthorized gadget classes on the heap.\n\nmermaid\ngraph LR\n A["Payload: List(DangerousType)"] --> B["Parser Reads Type Header"]\n B --> C["Evaluates Top-Level: List`1"]\n C --> D["Check Blocklist: Allowed"]\n D --> E["Deserialize Inner Elements"]\n E --> F["Instantiate DangerousType"]\n F --> G["Arbitrary Code Execution"]\n
The original code path evaluated the input type by performing a direct, flat lookup against the DisallowedTypes list. It lacked any mechanism to unpack generic type parameters or identify array element structures. Any object wrapper designed around a blocked type was evaluated as a safe type, allowing the deserialization process to proceed without restriction:\n\ncsharp\n// Vulnerable Implementation\npublic virtual void ThrowIfDeserializingTypeIsDisallowed(Type type)\n{\n if (type.FullName is string fullName && DisallowedTypes.Contains(fullName))\n {\n throw new MessagePackSerializationException($"Deserialization attempted to create the type {fullName} which is not allowed.");\n }\n}\n\n\nThe security patch introduced in commit f093bdc1207f6576088d39801fa43e92cec1e5c1 alters this behavior by implementing a recursive traversal algorithm. The updated code resolves the core top-level verification via a secondary helper and then directly inspects the structural attributes of the type to determine if it is an array or a constructed generic type:\n\ncsharp\n// Patched Implementation in MessagePackSerializerOptions.cs\npublic virtual void ThrowIfDeserializingTypeIsDisallowed(Type type)\n{\n this.ThrowIfDeserializingTypeIsDisallowedCore(type);\n\n if (type.HasElementType && type.GetElementType() is Type elementType)\n {\n this.ThrowIfDeserializingTypeIsDisallowed(elementType);\n }\n\n if (type.IsConstructedGenericType)\n {\n foreach (Type genericTypeArgument in type.GenericTypeArguments)\n {\n this.ThrowIfDeserializingTypeIsDisallowed(genericTypeArgument);\n }\n }\n}\n\n\nBy leveraging type.HasElementType and calling type.GetElementType(), the framework successfully extracts and recursively validates the base type of arrays, pointers, and reference types. Furthermore, querying type.IsConstructedGenericType allows the engine to iterate over type.GenericTypeArguments and recursively pass each generic type parameter back into the validation routine. This ensures that any deep nested structures, such as lists of arrays of lists of blocked gadgets, are completely traversed and validated against the static blocklist before any allocation or initialization occurs.
To exploit this vulnerability, an attacker must target an application that has enabled typeless serialization and accepts untrusted inputs over network interfaces or message queues. The attacker must first map the target's dependencies to identify available execution gadgets in the application context. Standard .NET gadgets like System.Windows.Data.ObjectDataProvider are frequently selected because they allow arbitrary method invocation upon property assignment during deserialization.\n\nUnder standard configurations, a direct exploit payload using ObjectDataProvider would be intercepted and rejected by the default blocklist validator. To bypass this, the attacker packages the payload inside a constructed generic collection, such as System.Collections.Generic.List<System.Windows.Data.ObjectDataProvider>. When the binary payload is transmitted to the target server, the outermost type signature is registered as the generic list class, allowing it to navigate past the initial non-recursive blocklist check.\n\nOnce the validator permits the outer type, the parser proceeds to deserialize the collection elements. As the engine handles each entry in the generic list, it resolves System.Windows.Data.ObjectDataProvider, dynamically allocates the class instance, and populates its parameters. This triggers the execution of the target method mapped within the gadget, leading to arbitrary code execution within the security context of the application pool.
During the resolution of CVE-2026-48517, several related vulnerabilities and security limitations were identified and addressed in the same release. A significant companion vulnerability involved a validation bypass where properties of type System.Type were resolved directly via the .NET runtime's Type.GetType() method without routing through the library's custom type loader or blocklist checks. The updated framework enforces strict verification of System.Type values through the options security provider before resolving them.\n\nResource exhaustion and Denial of Service (DoS) risks were also mitigated in the updated library. The integration of LZ4 decompression now incorporates the MaximumDecompressedSize validation setting, which immediately aborts parsing if an incoming stream specifies an unreasonably large uncompressed size, avoiding decompression bomb attacks. Similarly, multidimensional array allocations are now protected by checked arithmetic multiplication blocks to ensure the expected element count aligns with the allocated heap boundaries, preventing memory starvation.\n\nFurthermore, nested parser loops that previously relied on recursive method execution were refactored. The MessagePackReader.TrySkip() method was modified to use an iterative tracking structure instead of deep recursion, eliminating the risk of uncatchable stack overflow exceptions when skipping deeply nested or malicious payloads. These comprehensive security enhancements establish a robust layer of defense across multiple attack vectors.
The impact of insecure deserialization through typeless formatting is typically arbitrary code execution. An attacker who successfully exploits this vulnerability can compromise the confidentiality, integrity, and availability of the hosting system, potentially reading system secrets, modifying application state, or achieving lateral movement across the internal network. The vulnerability holds a CVSS score of 7.5 under standard parameters, but can escalate to 9.8 depending on the available gadgets and the administrative privileges of the executing application pool.\n\nImmediate remediation requires upgrading the MessagePack-CSharp dependency to version 2.5.301 or 3.1.7. Administrators and developers must exercise caution when utilizing custom options subclasses. Since ThrowIfDeserializingTypeIsDisallowed was kept virtual to maintain backwards compatibility, any custom class that overrides this method without explicitly invoking base.ThrowIfDeserializingTypeIsDisallowed will bypass the new recursive checks, leaving the application vulnerable.\n\nThe most secure architecture involves completely disabling typeless serialization. Applications should shift to contract-based or schema-based formatting where only statically defined types are allowed during deserialization. In environments where typeless formatting is unavoidable, developers must explicitly configure options using the MessagePackSecurity.UntrustedData standard to enforce strict limits and restrict type instantiation patterns.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
MessagePack-CSharp MessagePack | < 2.5.301 | 2.5.301 |
MessagePack-CSharp MessagePack | >= 3.0.3, < 3.1.7 | 3.1.7 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-502 |
| Attack Vector | Network |
| CVSS v3.1 Score | 7.5 (High) |
| EPSS Score | 0.00246 |
| Impact | Remote Code Execution |
| Exploit Status | Proof of Concept / Theoretical |
| KEV Status | Not Listed |
The product deserializes untrusted data without sufficiently verifying that the resulting data will be safe, allowing the instantiation of dangerous classes.
A critical prototype pollution vulnerability exists in the i18next-fs-backend Node.js package (prior to version 2.6.6) through its translation persistence layer. When handling missing translation keys, insecure traversal of JSON objects via the getLastOfPath function allows remote, unauthenticated attackers to mutate Object.prototype, potentially leading to denial of service, security bypasses, or remote code execution.
CVE-2026-48708 details a critical concurrency synchronization flaw in OliveTin versions < 3000.13.0. A shared package-level text/template.Template instance is accessed concurrently across multiple goroutines without proper synchronization. When concurrent request processing occurs, a race condition causes Go runtime panics or command contamination across separate sessions, enabling denial of service or execution of contaminated commands.
A missing authorization vulnerability in the OliveTin system allows unauthenticated remote actors to query the ValidateArgumentType RPC endpoint. By exploiting this flaw, attackers can execute systematic brute-force and side-channel validation attacks to enumerate active action binding IDs, parameter structures, and operational metadata, bypassing configured guest authentication barriers.
An observable timing discrepancy vulnerability (CWE-208) in Filament's administrative login page allows unauthenticated remote attackers to determine the existence of registered email addresses. This timing side-channel arises from short-circuiting logic that skips expensive password hashing checks when a queried email address is not found in the database. Attackers can execute statistical timing attacks to map active administrator accounts, facilitating subsequent targeted brute-force or credential-stuffing campaigns.
Filament's ImageColumn (used in tables) and ImageEntry (used in infolists) components render database values inside HTML attributes without validation or sanitization. This allows an attacker to inject arbitrary HTML attributes, leading to Stored Cross-Site Scripting (XSS).
The Netty incubator codec for Oblivious HTTP (OHTTP) fails to verify that a cryptographically signed final chunk is received before the outer HTTP body terminates. This missing validation allows an on-path adversary to truncate chunked-OHTTP messages cleanly at a non-final chunk boundary, leading to undetected data truncation and compromising message integrity. The vulnerability affects multiple versions of the maven package io.netty.incubator:netty-incubator-codec-ohttp prior to 0.0.22.Final.