Safeguard
Vulnerability Analysis

CVE-2019-0981: .NET Core remote code execution (second va...

CVE-2019-0981, the second variant of the April 2019 .NET Core RCE pair, let attackers run arbitrary code via a malicious file. Here's what to patch and why.

Vikram Iyer
Security Researcher
7 min read

In April 2019, Microsoft closed out a pair of closely related .NET remote code execution bugs in the same Patch Tuesday cycle: CVE-2019-0980 and CVE-2019-0981. Both vulnerabilities lived in how the .NET Framework and .NET Core handled objects in memory, and both could let an attacker who convinced a victim to open a specially crafted file or run a malicious application execute arbitrary code in the context of the logged-in user. CVE-2019-0981 is the second of that pair — a distinct code path triggering the same class of memory-handling flaw — which is why it's commonly referred to as the "second variant" of the April 2019 .NET Core RCE issue. If the affected user was running with administrative rights, successful exploitation meant an attacker could install programs, view or delete data, or create new accounts with full user privileges. For any organization running .NET Framework or .NET Core applications — which, in practice, is most enterprises with a Windows or mixed-platform application estate — this pair of CVEs was a meaningful reminder that the .NET runtime itself is part of the software supply chain and needs the same patch discipline as any other dependency.

Affected Versions and Components

CVE-2019-0981 affected supported builds of the Microsoft .NET Framework and .NET Core available at the time of disclosure, and Microsoft shipped corresponding updates for both runtime families as part of the same security bulletin. Because Visual Studio bundles its own copy of the .NET Core SDK and runtime, installations of Visual Studio 2017 and 2019 that had not applied the corresponding servicing updates were also exposed indirectly, since developer machines building or debugging .NET applications were running the vulnerable runtime locally. The practical exposure surface, then, wasn't limited to production servers — it extended to developer workstations, build agents, and any CI infrastructure invoking the vulnerable .NET Core or .NET Framework components to process untrusted input or open untrusted files.

As is typical for this class of Microsoft advisory, exact build numbers were published per-platform in the vendor's security update guide rather than as a single version range, which is why we're not restating specific point-release numbers here — the reliable source of truth is Microsoft's own advisory and the update catalog for your specific OS and .NET version combination. The operative takeaway for defenders is simpler than chasing individual build numbers: if your .NET Framework or .NET Core installations had not received the April 2019 cumulative or security-only updates (or a later superseding update), they were vulnerable to both CVE-2019-0980 and CVE-2019-0981.

CVSS, EPSS, and KEV Context

Public vulnerability databases list CVE-2019-0981 as a high-severity issue, consistent with a network-adjacent attack vector, low attack complexity, no privileges required, but a required element of user interaction — the victim has to open a malicious file or launch a malicious application for the exploit chain to fire. That user-interaction requirement is the main reason this bug, like most .NET "object in memory" RCEs from that era, was rated high rather than critical-with-no-caveats: it constrained attackers to phishing-style delivery and social engineering rather than a fully unauthenticated, wormable network exploit.

CVE-2019-0981 does not appear on CISA's Known Exploited Vulnerabilities (KEV) catalog, and there is no well-documented history of widespread, in-the-wild exploitation tied specifically to this CVE. Consistent with that, EPSS scoring for this vulnerability sits well below the range associated with active mass exploitation — reflective of the fact that a working exploit requires successful social engineering rather than a scriptable, unauthenticated network trigger. That combination — high CVSS impact, low observed exploitation probability — is a pattern worth internalizing: it's exactly the kind of vulnerability that quietly persists in unpatched fleets for years because it never generates the headline-driving incident that forces an emergency patch cycle, even though the underlying risk (arbitrary code execution) is severe if it is ever weaponized against a specific, targeted user.

Timeline

  • April 9, 2019 — Microsoft's April Patch Tuesday release includes fixes for CVE-2019-0980 and CVE-2019-0981, both described as .NET Framework and .NET Core remote code execution vulnerabilities arising from improper handling of objects in memory.
  • April 2019 onward — Downstream distributions and tooling that bundle the .NET runtime, including specific Visual Studio servicing baselines, issue corresponding updates so that developer environments and CI/CD build agents pick up the fixed runtime alongside server and desktop deployments.
  • Post-disclosure — No confirmed public proof-of-concept exploit or widespread exploitation campaign specific to CVE-2019-0981 has been documented, and the vulnerability has not been added to CISA's KEV catalog in the years since. It remains, however, a standing item that legacy-runtime audits and dependency inventories should catch when reconciling historical patch levels against currently deployed .NET builds.

Remediation Steps

  1. Apply the April 2019 .NET Framework and .NET Core security updates (or any later superseding update) for every affected OS and runtime combination in your environment. Treat this as a baseline patch-level check rather than a one-time event — new machine images, restored backups, and cloned VMs can silently reintroduce a pre-patch runtime.
  2. Update Visual Studio installations to a servicing baseline that includes the fixed .NET Core SDK/runtime, since developer machines and self-hosted build agents running an outdated bundled runtime are exposed the same way production hosts are.
  3. Inventory every .NET Framework and .NET Core version running across servers, containers, developer workstations, and CI/CD infrastructure. Because this bug class affects the runtime itself rather than an application-level package, standard application dependency scans can easily miss it if they don't also track host and container base-image runtime versions.
  4. Restrict execution of untrusted files and applications as a compensating control while patching rolls out, particularly on systems where users routinely handle files from external or unauthenticated sources — the exploitation path for CVE-2019-0981 depends on exactly that kind of interaction.
  5. Rebuild and redeploy container images and golden VM images that bake in a vulnerable .NET runtime, rather than relying solely on in-place patching of running instances, so that newly provisioned infrastructure doesn't reintroduce the flaw.
  6. Validate remediation by confirming installed .NET Framework/.NET Core build numbers against Microsoft's published fixed versions for your specific OS, rather than assuming a general "Windows Update is current" status covers it — .NET servicing can be decoupled from OS-level cumulative updates depending on install method.

How Safeguard Helps

CVE-2019-0981 is a useful illustration of a problem that's bigger than any single patch: runtime and framework versions are part of your software supply chain, and they age out of support just like open-source packages do, but they're frequently invisible to tools that only scan application-level dependency manifests. Safeguard is built to close exactly that gap.

Safeguard continuously inventories the components running across your build pipelines, container images, and deployed workloads — including underlying runtimes like .NET Framework and .NET Core, not just the packages your application code declares — so that a vulnerability like CVE-2019-0981 surfaces automatically instead of depending on someone remembering a seven-year-old patch bulletin. When a known-vulnerable runtime version is detected, Safeguard correlates it against CVE, CVSS, EPSS, and KEV data to help your team prioritize based on actual exploitation risk rather than raw severity scores alone, which matters for exactly this kind of high-CVSS-but-low-exploitation-likelihood finding. Safeguard also lets you enforce policy gates in CI/CD so that builds relying on an outdated, vulnerable .NET SDK are flagged or blocked before they ship, and it tracks remediation status across your fleet so you can prove — for an audit, a customer questionnaire, or your own peace of mind — that legacy runtime exposure has actually been closed out, not just patched on the machines someone happened to remember.

If your organization has .NET applications with a long history, there's a good chance some corner of your infrastructure — a forgotten build agent, an old container base image, a developer's local environment — is still running a runtime older than you'd like to admit. Safeguard is designed to find that corner before an attacker does.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.