CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2020-1147
7.893.43%

The DataSet Trap: How Microsoft's XML Trust Issues Led to Remote Code Execution

Amit Schendel
Amit Schendel
Senior Security Researcher

Feb 10, 2026·6 min read·6 visits

WeaponizedCISA KEV Listed

Executive Summary (TL;DR)

The .NET `DataSet` class trusts inline XML schemas too much. Attackers can tell it to load a `XamlReader` disguised as a database column, leading to immediate RCE. This affects SharePoint, Visual Studio, and any .NET app using `ReadXml` on untrusted input.

A critical Remote Code Execution (RCE) vulnerability in the .NET Framework, SharePoint, and Visual Studio caused by unsafe handling of XML input in the `DataSet` and `DataTable` classes. By exploiting the `ReadXml` method, attackers can define arbitrary types via an inline schema, forcing the application to deserialize malicious gadgets that execute code in the context of the host process.

The Hook: Why We Are Here

If you've been in the .NET world for a while, you remember DataSet and DataTable. They are the relics of a bygone era—bulky, memory-hungry objects designed to represent database tables in memory. They were the darlings of the Enterprise application stack in the mid-2000s. But like many things from that era (looking at you, SOAP), they have a fatal attraction to XML.

Here is the problem: DataSet doesn't just hold data; it holds metadata. It wants to know the schema of your data so it can enforce types. To make life easy for developers, Microsoft allowed DataSet to read this schema directly from the XML input via the ReadXml() method.

CVE-2020-1147 is the realization that if you let an attacker define the schema of the data they are sending you, they can make your application instantiate objects you never intended to exist. It's the deserialization vulnerability that keeps on giving, famously plaguing SharePoint and leading to the 'ToolShell' discoveries years later.

The Flaw: Trusting the `msdata:DataType`

The root cause is a classic failure of trust boundaries. When DataSet.ReadXml() parses input, it looks for an inline XML schema (defined by xs:schema). Inside that schema, standard XML types are expected. However, Microsoft added a special attribute called msdata:DataType. This attribute tells the .NET runtime: "Hey, this column isn't just a string; it's actually a complex .NET object of this specific type."

Under the hood, DataSet sees this attribute and thinks, "Okay, the user says this column contains objects of type System.Windows.Markup.XamlReader. I should prepare the XmlSerializer to handle that."

This is where the logic falls apart. The application essentially takes a type name provided by the attacker and hands it over to the serializer. Unlike BinaryFormatter, which is dangerous by default, XmlSerializer is usually safe because it requires known types. But DataSet bypasses this safety by dynamically telling the serializer what types to expect based on the attacker's input. It creates a bridge between safe XML parsing and unsafe object instantiation.

> [!NOTE] > This isn't just about crashing the app. By specifying a gadget class, we can trick the serializer into setting properties on that class, triggering a chain reaction that ends in code execution.

The Code: Anatomy of a Gadget Chain

So, we can instantiate a type. Great. But which type? We need a "gadget"—a class that does something dangerous when its properties are set or when it is initialized. Enter the ExpandedWrapper and XamlReader combo.

This specific attack chain is a work of art. We can't just instantiate Process.Start directly because XmlSerializer is picky. We need a wrapper. The gadget chain looks like this:

  1. ExpandedWrapper: This is an internal class in System.Data.Services. It's a generic wrapper that allows us to bundle two items together. It's useful here because it's serializable and helps us satisfy type requirements.
  2. XamlReader: This is the nuclear option. The XamlReader class in WPF has a Parse() method. If you can pass a string of XAML to this method, it will compile and execute it. It's basically eval() for .NET UI markup.

The vulnerability allows us to define a column in our fake DataTable with the type ExpandedWrapper<XamlReader, ObjectDataProvider>. When the serializer processes the row data, it hydrates this wrapper. Inside the wrapper, we place a payload that the XamlReader parses.

Here is a visualization of the flow:

The Exploit: Crafting the Payload

Let's build the bomb. The payload consists of two parts: the Schema (the setup) and the DiffGram (the trigger).

First, we define the schema. We claim we are sending a DataSet named somedataset. Inside, we define a table Exp_Table with a column named pwn. The critical part is the msdata:DataType attribute, which points to our gadget chain:

<xs:element name="pwn" 
   msdata:DataType="System.Data.Services.Internal.ExpandedWrapper`2[[System.Windows.Markup.XamlReader, ...], [System.Windows.Data.ObjectDataProvider, ...]], ..." 
   type="xs:anyType" minOccurs="0"/>

Next, we provide the data in the diffgr:diffgram block. This is where the actual serialized object lives. We wrap our malicious XAML inside a CDATA block effectively hidden inside the serialized stream intended for the XamlReader.

The XAML payload typically uses ObjectDataProvider to invoke System.Diagnostics.Process.Start. The final XML looks innocent enough to a firewall—just a bunch of schema definitions and data—but to the .NET runtime, it's a command execution instruction.

When DataSet.ReadXml(payload) is called:

  1. The schema is parsed, and the dangerous type is loaded.
  2. The DiffGram is processed.
  3. The ExpandedWrapper is instantiated.
  4. XamlReader wakes up, parses the command string, and pops a shell.

The Impact: From SharePoint to Everywhere

Why was this such a big deal? Because DataSet is everywhere. It is the bedrock of legacy enterprise .NET applications.

SharePoint was the most high-profile victim. SharePoint relies heavily on XML and DataSet for internal communication and Web Parts. An attacker could send a crafted POST request to a vulnerable SharePoint endpoint and execute code as the service account (usually Network Service or a specific farm account). This typically means full compromise of the SharePoint farm.

The CVSS score of 7.8 (High) is slightly misleading; in many internal network scenarios or public-facing web apps using ReadXml on user uploads, this is a functional 9.8 (Critical). The EPSS score sits comfortably above 93%, meaning if you have this exposed, bots are likely already trying to exploit it.

Furthermore, this isn't just a "one and done" bug. As seen with the "ToolShell" research in 2025, incomplete patches and new gadget variations keep breathing life into this attack vector. Attackers leverage this to install webshells, dump databases, or pivot into the domain controller.

The Fix: Closing the Window

Microsoft's patch for CVE-2020-1147 introduced a type allowlist mechanism for DataSet and DataTable. Essentially, they stopped trusting msdata:DataType implicitly. If the type isn't on the list or clearly safe, the deserializer throws a tantrum and refuses to proceed.

However, patches are only as good as their application.

Developers must:

  1. Update .NET Framework: Ensure you are running the latest security rollups.
  2. Code Changes: If you are using ReadXml(), stop. If you can't stop, use the overload that takes XmlReadMode. Avoid XmlReadMode.Auto or XmlReadMode.DiffGram on untrusted input.
  3. Type Limiting: Implement a custom XmlReader that forbids DTD processing (DtdProcessing.Prohibit) and validate schemas strictly before passing them to DataSet.

For Security Teams: Write detection rules for the string System.Data.Services.Internal.ExpandedWrapper appearing in HTTP bodies. Normal traffic rarely, if ever, sends this specific internal type across the wire. It is a high-fidelity indicator of an attempted exploit.

Official Patches

MicrosoftOfficial Security Update Guide

Technical Appendix

CVSS Score
7.8/ 10
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
EPSS Probability
93.43%
Top 0% most exploited

Affected Systems

Microsoft SharePoint Server 2010/2013/2016/2019.NET Framework (2.0 - 4.8).NET Core (2.1, 3.1)Visual Studio 2017/2019

Affected Versions Detail

Product
Affected Versions
Fixed Version
.NET Framework
Microsoft
2.0 - 4.8July 2020 Security Rollup
SharePoint Server 2019
Microsoft
< July 2020 UpdateJuly 2020 PU
AttributeDetail
CWE IDCWE-502 (Deserialization of Untrusted Data)
CVSS v3.17.8 (High)
Attack VectorNetwork
EPSS Score0.9343 (93.43%)
Exploit StatusActive / Weaponized
Key GadgetExpandedWrapper + XamlReader

MITRE ATT&CK Mapping

T1203Exploitation for Client Execution
Execution
T1059Command and Scripting Interpreter
Execution
T1190Exploit Public-Facing Application
Initial Access
CWE-502
Deserialization of Untrusted Data

Known Exploits & Detection

ysoserial.netThe 'DataSet' gadget in ysoserial.net generates the exact XML payload required for this exploit.
GitHubVarious PoC scripts for SharePoint RCE often leverage this underlying DataSet vulnerability.

Vulnerability Timeline

Microsoft publishes CVE-2020-1147 and patches
2020-07-14
PoC exploits integrated into ysoserial.net
2020-07-20
Added to CISA Known Exploited Vulnerabilities Catalog
2021-11-03

References & Sources

  • [1]ZDI Analysis of CVE-2020-1147
  • [2]Securelist: ToolShell and SharePoint RCE
Related Vulnerabilities
CVE-2020-0618CVE-2019-0604

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.