Jun 25, 2026·7 min read·22 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 symbolic link directory traversal vulnerability was identified in go-git, a pure Go implementation of the Git specification. This vulnerability allows an attacker to construct a repository that, when checked out or processed, bypasses directory boundaries to write or overwrite arbitrary files on the host filesystem.
CVE-2026-71557 is a path traversal vulnerability in go-git, a pure-Go implementation of Git. In vulnerable versions, the filesystem-backed storage engine fails to validate reference names before mapping them to on-disk paths. An attacker hosting a malicious Git server can advertise references containing directory traversal sequences, such as 'refs/heads/../../config', to write or overwrite files outside the intended reference storage directory.
An information disclosure vulnerability in the Nuxt development server allows adjacent network attackers to retrieve the absolute project root directory and a persistent workspace UUID by querying the unprotected Chrome DevTools workspace endpoint. This occurs when the development server is bound to a network-reachable interface, allowing requests that bypass the header-based security verification checks.
A Regular Expression Denial of Service (ReDoS) vulnerability exists in SvelteKit's content negotiation header parser prior to version 2.70.2. An unauthenticated remote attacker can exploit this vulnerability by sending a crafted Accept header with highly repetitive malformed values. This triggers catastrophic backtracking on the single-threaded Node.js/Bun event loop, leading to CPU exhaustion and full denial of service.
An OS command injection vulnerability exists in the npm package loading component of the jsii-diff CLI tool within the AWS jsii framework. Prior to version 1.131.0, when parsing package specifiers prefixed with `npm:`, the tool concatenated user-controlled inputs directly into a shell execution string via child_process.exec. This allows attackers to execute arbitrary shell commands under the context of the running Node.js process.
CodeIgniter4 versions prior to v4.7.4 contain a protocol-spoofing vulnerability due to improper verification of upstream reverse proxy forwarding headers. Remote, unauthenticated attackers can inject headers like X-Forwarded-Proto to deceive the framework into identifying an insecure HTTP request as a secure HTTPS connection.