Dev.to Security 🔐 Cybersecurity 👁 0 📖 3 min read

CVE-2026-114155: CVE-2026-114155: Local Elevation of Privilege via TOCTOU Race Condition in Microsoft .NET dotnet-watch

CVE-2026-114155: Local Elevation of Privilege via TOCTOU Race Condition in Microsoft .NET dotnet-watch Vulnerability ID: CVE-2026-114155 CVSS Score: 7.5 Published: 2026-11-11 CVE-2026-114155 is a local elevation of

CVE-2026-114155: Local Elevation of Privilege via TOCTOU Race Condition in Microsoft .NET dotnet-watch

Vulnerability ID: CVE-2026-114155
CVSS Score: 7.5
Published: 2026-11-11

CVE-2026-114155 is a local elevation of privilege vulnerability affecting Microsoft .NET on Linux and macOS platforms. The vulnerability exists within the dotnet-watch (Hot Reload) component and is caused by a Time-of-Check Time-of-Use (TOCTOU) race condition (CWE-367) during the generation and execution of temporary XML solution files (.slnx) in shared system temporary directories. A local attacker with low privileges can intercept and modify the temporary solution file before MSBuild evaluates it, achieving arbitrary code execution in the security context of the user running dotnet-watch.

TL;DR

A TOCTOU race condition (CWE-367) in the Microsoft .NET dotnet-watch utility on Linux and macOS allows local attackers to overwrite temporary .slnx solution files in /tmp before MSBuild processes them, leading to local privilege escalation.

Technical Details

  • Vulnerability Type: CWE-367: Time-of-check Time-of-use (TOCTOU) Race Condition
  • Attack Vector: Local (Shared /tmp directory on Linux and macOS)
  • CVSS v3.1 Score: 7.5 HIGH (CVSS:3.1/AV:L/AC:H/PR:L/UI:R/S:C/C:H/I:H/A:H)
  • Affected Component: Microsoft .NET SDK dotnet-watch (HotReloadDotNetWatcher)
  • Security Impact: Local Elevation of Privilege via arbitrary MSBuild code execution
  • Exploit Status: Unproven (No public PoC or active exploitation)
  • CISA KEV Status: Not Listed

Affected Systems

  • Microsoft .NET 10.0 on Linux
  • Microsoft .NET 10.0 on macOS
  • Microsoft .NET 11.0 on Linux
  • Microsoft .NET 11.0 on macOS
  • Microsoft.DotNet.HotReload.Watch.Aspire
  • Microsoft .NET 10.0 (Linux & macOS): >= 10.0.0, < 10.0.404 (Fixed in: 10.0.404)
  • Microsoft .NET 11.0 (Linux & macOS): >= 11.0.0-preview.1, < 11.0.100 (Fixed in: 11.0.100)
  • Microsoft.DotNet.HotReload.Watch.Aspire: >= 10.0.300, < 11.0.100 (Fixed in: 11.0.100)

Code Analysis

Commit: c46253a

Introduce TemporaryFileManager to securely create and lock temporary files in a user-private directory during dotnet-watch execution.

--- a/src/sdk/src/Dotnet.Watch/Watch/HotReload/HotReloadDotNetWatcher.cs
+++ b/src/sdk/src/Dotnet.Watch/Watch/HotReload/HotReloadDotNetWatcher.cs
@@ -1144,7 +1144,8 @@ async Task<bool> BuildAsync(BuildAction action, string? targetFramework, DeviceI
             }
             else if (projectPaths is not [])
             {
-                var solutionFile = Path.Combine(Path.GetTempFileName() + ".slnx");
+                // Keep the file open and allow other processes to read it (but not modify) after the content is written:
+                using var solutionFile = _context.TemporaryFileManager.Create(".slnx", FileShare.Read);
                 var solutionElement = new XElement("Solution");

                 foreach (var projectPath in projectPaths)
@@ -1153,25 +1154,11 @@ async Task<bool> BuildAsync(BuildAction action, string? targetFramework, DeviceI
                 }

                 var doc = new XDocument(solutionElement);
-                doc.Save(solutionFile);
+                doc.Save(solutionFile.Stream);

-                try
+                if (!await BuildFileOrProjectOrSolutionAsync(solutionFile.Stream.Name, targetFramework, device, action, cancellationToken))
                 {
-                    if (!await BuildFileOrProjectOrSolutionAsync(solutionFile, targetFramework, device, action, cancellationToken))
-                    {
-                        return false;
-                    }
-                }
-                finally
-                {
-                    try
-                    {
-                        File.Delete(solutionFile);
-                    }
-                    catch
-                    {
-                        // ignore
-                    }
+                    return false;
                 }
             }

Commit: 032f564

Merge pull request 66316 from msrc-temp-10.0.4xx into internal release branch.

Mitigation Strategies

  • Upgrade Microsoft .NET 10.0 and .NET 11.0 SDKs to the latest patched versions.
  • Configure per-user isolated temporary directories on Linux and macOS using the TMPDIR environment variable.
  • Avoid executing dotnet-watch with elevated privileges (such as root or sudo) on shared multi-user systems.
  • Enable Linux kernel protections for regular files in sticky world-writable directories (fs.protected_regular=2).

Remediation Steps:

  1. Identify all Linux and macOS developer workstations and build servers with Microsoft .NET 10.0 (< 10.0.404) or .NET 11.0 (< 11.0.100) installed.
  2. Upgrade Microsoft .NET 10.0 SDK to 10.0.404 (Runtime 10.0.15) or Microsoft .NET 11.0 SDK to 11.0.100 (Runtime 11.0.0).
  3. Update any project dependencies on Microsoft.DotNet.HotReload.Watch.Aspire to version 11.0.100 or later.
  4. If immediate patching is blocked, configure user profiles to export TMPDIR="$HOME/.tmp" with 0700 directory permissions.

References

Read the full report for CVE-2026-114155 on our website for more details including interactive diagrams and full exploit analysis.

📰 Read the original article on Dev.to Security

Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.