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-2026-114155

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

Alon Barad
Alon Barad
Software Engineer

Nov 11, 2026·9 min read·1 visit

Executive Summary (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.

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.

Vulnerability Overview

dotnet-watch is a file watcher and development tool bundled with the Microsoft .NET SDK that automatically recompiles and reloads applications when source code changes are detected. When monitoring multiple projects simultaneously under Hot Reload, dotnet-watch aggregates the target project references into an on-the-fly XML solution file (.slnx) and invokes MSBuild to evaluate and build the solution.

In affected versions of .NET 10.0 and .NET 11.0, the HotReloadDotNetWatcher component generates these temporary .slnx files directly within the default system temporary directory (/tmp on Unix-like operating systems). Because /tmp is a shared, world-writable namespace on Linux and macOS, creating files in this directory without strict permission enforcement or exclusive file locking exposes those files to manipulation by other local users.

This behavior introduces a Time-of-Check Time-of-Use (TOCTOU) race condition tracked as CVE-2026-114155 (CWE-367). Between the moment dotnet-watch writes the .slnx file to disk and the moment the child MSBuild process parses it, the file handle is closed and left unlocked. A local unprivileged user can exploit this timing window to replace or modify the .slnx file, injecting malicious MSBuild directives that execute arbitrary commands under the privileges of the developer or automated build service running dotnet-watch.

Root Cause Analysis

The root cause of CVE-2026-114155 lies in the insecure use of the Path.GetTempFileName() API combined with premature stream closure in src/sdk/src/Dotnet.Watch/Watch/HotReload/HotReloadDotNetWatcher.cs. When BuildAsync processes a non-empty list of projectPaths, it constructs a path for the temporary solution file using the expression Path.Combine(Path.GetTempFileName() + ".slnx").

When Path.GetTempFileName() executes on POSIX systems, it creates an empty file in /tmp with a unique name (for example, /tmp/tmpa1b2.tmp) using exclusive creation flags (O_CREAT | O_EXCL) and restrictive permissions. However, appending the string ".slnx" to the returned path references a second, distinct file path (/tmp/tmpa1b2.tmp.slnx) that has not yet been created or reserved on the filesystem. Furthermore, the original /tmp/tmpa1b2.tmp file is left behind as an orphaned artifact whose name is immediately visible to any local user listing /tmp.

Once dotnet-watch populates the XDocument object in memory, it writes the XML content to the .slnx path by calling doc.Save(solutionFile). This method opens a file stream, writes the serialized XML, and immediately closes and disposes of the underlying file handle. After doc.Save() returns, dotnet-watch invokes BuildFileOrProjectOrSolutionAsync(solutionFile, ...), which spawns or signals MSBuild to read and evaluate the file from disk.

Because the .slnx file is neither created with O_EXCL inside a private directory nor held open with a write-restricting lock, two distinct attack vectors emerge during the TOCTOU window. First, an attacker watching /tmp via inotify (Linux) or kqueue (macOS) can detect the creation of /tmp/tmpXXXX.tmp, immediately predict the target filename /tmp/tmpXXXX.tmp.slnx, and pre-create the .slnx file or a symbolic link before doc.Save() runs. Second, if the user's umask is permissive, or if the attacker pre-created the file and granted write permissions to the victim, the attacker can overwrite the contents of /tmp/tmpXXXX.tmp.slnx after doc.Save() closes the stream but before MSBuild opens it.

Code Analysis

Prior to the patch, HotReloadDotNetWatcher.cs managed the lifecycle of the temporary .slnx file manually using string concatenation and a try/finally deletion block. The vulnerable implementation is shown below:

// Vulnerable implementation in src/sdk/src/Dotnet.Watch/Watch/HotReload/HotReloadDotNetWatcher.cs
else if (projectPaths is not [])
{
    // 1. Creates /tmp/tmpXXXX.tmp, but derives unreserved path /tmp/tmpXXXX.tmp.slnx
    var solutionFile = Path.Combine(Path.GetTempFileName() + ".slnx");
    var solutionElement = new XElement("Solution");
 
    foreach (var projectPath in projectPaths)
    {
        // populate solution elements
    }
 
    var doc = new XDocument(solutionElement);
    // 2. Writes file to /tmp and immediately closes the file stream
    doc.Save(solutionFile);
 
    try
    {
        // 3. TOCTOU Window: MSBuild reads the unlocked file by path name
        if (!await BuildFileOrProjectOrSolutionAsync(solutionFile, targetFramework, device, action, cancellationToken))
        {
            return false;
        }
    }
    finally
    {
        try
        {
            File.Delete(solutionFile);
        }
        catch
        {
            // ignore
        }
    }
}

In commit c46253a634a2725fd34820ad8a36a3f106f5d01d, Microsoft remediated the vulnerability by introducing a dedicated TemporaryFileManager class and refactoring HotReloadDotNetWatcher.cs to write directly to an open, locked FileStream:

// Patched implementation in src/sdk/src/Dotnet.Watch/Watch/HotReload/HotReloadDotNetWatcher.cs
else if (projectPaths is not [])
{
    // 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)
    {
        // populate solution elements
    }
 
    var doc = new XDocument(solutionElement);
    // Save directly to the open FileStream without closing the handle
    doc.Save(solutionFile.Stream);
 
    if (!await BuildFileOrProjectOrSolutionAsync(solutionFile.Stream.Name, targetFramework, device, action, cancellationToken))
    {
        return false;
    }
}

The new TemporaryFileManager class eliminates both the shared directory exposure and the unlocked file state:

// New file: src/sdk/src/Dotnet.Watch/Watch/TemporaryFileManager.cs
namespace Microsoft.DotNet.Watch;
 
internal sealed partial class TemporaryFileManager : IDisposable
{
    internal readonly struct TemporaryFile(FileStream stream) : IDisposable
    {
        public FileStream Stream => stream;
 
        public void Dispose()
        {
            try { stream.Dispose(); } catch { }
            try { File.Delete(stream.Name); } catch { }
        }
    }
 
    private const string TempDirectoryPrefix = "dotnet-watch-";
 
    // Creates a subdirectory with 0700 (owner-only) permissions on Unix
    private readonly string _directory = Directory.CreateTempSubdirectory(TempDirectoryPrefix).FullName;
 
    private int _uniqueFileId;
 
    public void Dispose()
    {
        try { Directory.Delete(_directory, recursive: true); } catch { }
    }
 
    public TemporaryFile Create(string extension, FileShare share)
        => new(new FileStream(
            path: Path.Combine(_directory, Interlocked.Increment(ref _uniqueFileId) + extension),
            mode: FileMode.CreateNew,
            access: FileAccess.ReadWrite,
            share: share));
}

> [!NOTE] > Fix Completeness Assessment: The fix is comprehensive on POSIX systems because Directory.CreateTempSubdirectory invokes mkdtemp, which atomically creates a directory with 0700 (rwx------) permissions owned by the effective user ID. Because the parent directory lacks group and other execute/search bits, unprivileged local users cannot traverse into the dotnet-watch-* subdirectory to pre-create, read, or modify files regardless of file locking semantics.

Exploitation Mechanics

Exploiting CVE-2026-114155 requires local code execution capabilities as a low-privileged user on the same Linux or macOS host where a victim user (such as a senior developer, system administrator, or CI/CD runner) executes dotnet watch across multiple projects. Windows environments are not affected under default configurations because Path.GetTempPath() resolves to a user-isolated directory (%USERPROFILE%\AppData\Local\Temp).

To reliably win the race condition, an attacker sets up a filesystem monitoring loop on /tmp using the Linux inotify API (specifically watching for IN_CREATE events). When the victim runs dotnet watch, Path.GetTempFileName() creates /tmp/tmpXXXXXX.tmp. This immediately triggers an IN_CREATE event before dotnet-watch even begins constructing the XElement tree in memory.

Upon receiving the notification for /tmp/tmpXXXXXX.tmp, the attacker's process immediately creates /tmp/tmpXXXXXX.tmp.slnx with world-writable permissions (0666) before doc.Save(solutionFile) is reached. Because doc.Save(string) opens existing files with FileMode.Create (which truncates rather than failing if the file already exists), dotnet-watch writes the legitimate XML into the attacker-owned file and closes the stream. Immediately after IN_CLOSE_WRITE fires on /tmp/tmpXXXXXX.tmp.slnx, the attacker truncates and overwrites the file with a malicious solution or MSBuild project payload.

Alternatively, the attacker can replace the .slnx reference with a project file path containing an MSBuild <Target> definition that executes an <Exec> task:

<Project Sdk="Microsoft.NET.Sdk">
  <Target Name="Build">
    <Exec Command="/bin/sh -c 'cp /bin/sh /tmp/rootsh &amp;&amp; chmod +s /tmp/rootsh'" />
  </Target>
</Project>

When BuildFileOrProjectOrSolutionAsync invokes MSBuild on /tmp/tmpXXXXXX.tmp.slnx (or a referenced project path injected into the .slnx file), MSBuild parses the attacker-controlled XML and executes the embedded shell commands under the UID and GID of the victim user.

Impact Assessment

Successful exploitation of CVE-2026-114155 grants the attacker arbitrary code execution within the security context of the user running dotnet-watch. In multi-user development servers, shared academic or enterprise build hosts, or containerized CI/CD environments where multiple agents share a host /tmp mount, an attacker can cross user boundaries and compromise higher-privileged accounts.

Microsoft assigned CVE-2026-114155 a CVSS v3.1 base score of 7.5 (High) with the vector CVSS:3.1/AV:L/AC:H/PR:L/UI:R/S:C/C:H/I:H/A:H. The Scope: Changed (S:C) metric reflects that a vulnerability in the .NET SDK build tool allows an unprivileged local user to escape their OS user boundary and execute code as another user or service account.

Confidentiality, Integrity, and Availability impacts are all rated High (C:H/I:H/A:H). Once code execution is achieved in the victim's context, the attacker can exfiltrate source code, steal cloud credentials, SSH keys, or NuGet API tokens stored in the victim's home directory, modify build artifacts to introduce supply chain backdoors, or terminate the victim's processes.

At the time of publication, there are no known public proof-of-concept exploits in the wild, and the vulnerability is not listed in the CISA Known Exploited Vulnerabilities (KEV) catalog. The Attack Complexity is rated High (AC:H) because exploitation depends on both the victim running dotnet watch in a multi-project configuration and the attacker winning the filesystem race window.

Remediation and Mitigation

The primary remediation is to update the Microsoft .NET SDK and associated NuGet packages to the patched releases that incorporate the TemporaryFileManager isolation mechanism. Specifically, administrators and developers on Linux and macOS should apply the following updates:

  • Microsoft .NET 10.0: Upgrade SDK to version 10.0.404 (Runtime 10.0.15) or later.
  • Microsoft .NET 11.0: Upgrade SDK to version 11.0.100 (Runtime 11.0.0) or later.
  • Microsoft.DotNet.HotReload.Watch.Aspire: Update package references to version 11.0.100 or later.

For environments where immediate patching is not possible, system administrators and developers can completely neutralize the vulnerability by configuring a private, per-user temporary directory via the TMPDIR environment variable. Because .NET's Path.GetTempPath() and Path.GetTempFileName() respect TMPDIR on POSIX systems, pointing TMPDIR to a mode 0700 directory inside the user's home directory prevents other local users from accessing or racing temporary files:

mkdir -p -m 0700 "$HOME/.tmp"
export TMPDIR="$HOME/.tmp"

In automated build pipelines or shared Linux hosts, administrators can also implement systemd PrivateTmp=yes directives for build services, or enable the Linux kernel fs.protected_regular sysctl parameter (sysctl -w fs.protected_regular=2), which restricts O_CREAT file opens in world-writable sticky directories (/tmp) when the file is owned by another user.

Official Patches

MicrosoftMicrosoft Security Response Center (MSRC) Official Advisory for CVE-2026-114155
Microsoft.NET Release Tag v10.0.404
Microsoft.NET Release Tag v11.0.100

Fix Analysis (2)

Technical Appendix

CVSS Score
7.5/ 10
CVSS:3.1/AV:L/AC:H/PR:L/UI:R/S:C/C:H/I:H/A:H

Affected Systems

Microsoft .NET 10.0 on LinuxMicrosoft .NET 10.0 on macOSMicrosoft .NET 11.0 on LinuxMicrosoft .NET 11.0 on macOSMicrosoft.DotNet.HotReload.Watch.Aspire

Affected Versions Detail

Product
Affected Versions
Fixed Version
Microsoft .NET 10.0 (Linux & macOS)
Microsoft
>= 10.0.0, < 10.0.40410.0.404
Microsoft .NET 11.0 (Linux & macOS)
Microsoft
>= 11.0.0-preview.1, < 11.0.10011.0.100
Microsoft.DotNet.HotReload.Watch.Aspire
Microsoft
>= 10.0.300, < 11.0.10011.0.100
AttributeDetail
Vulnerability TypeCWE-367: Time-of-check Time-of-use (TOCTOU) Race Condition
Attack VectorLocal (Shared /tmp directory on Linux and macOS)
CVSS v3.1 Score7.5 HIGH (CVSS:3.1/AV:L/AC:H/PR:L/UI:R/S:C/C:H/I:H/A:H)
Affected ComponentMicrosoft .NET SDK dotnet-watch (HotReloadDotNetWatcher)
Security ImpactLocal Elevation of Privilege via arbitrary MSBuild code execution
Exploit StatusUnproven (No public PoC or active exploitation)
CISA KEV StatusNot Listed

MITRE ATT&CK Mapping

T1068Exploitation for Privilege Escalation
Privilege Escalation
T1574.008Hijack Execution Flow: Path Interception
Persistence
T1059Command and Scripting Interpreter
Execution
T1222.002File and Directory Permissions Modification: Linux and Mac File and Directory Permissions Modification
Defense Evasion
CWE-367
Time-of-check Time-of-use (TOCTOU) Race Condition

The product checks the state of a resource before using that resource, but the resource's state can change between the check and the use in a way that invalidates the results of the check. This can cause the product to perform invalid actions when the resource is in an unexpected state.

Vulnerability Timeline

Security fix committed by Tomáš Matoušek (c46253a634a2725fd34820ad8a36a3f106f5d01d)
2026-10-06
Fix merged into release branch via Pull Request 66316 (032f564872d408f03fba11c8d03e5cd7386164cd)
2026-10-12
CVE-2026-114155 officially published by Microsoft
2026-11-10
Public CVE record metadata updated
2026-11-11

References & Sources

  • [1]Microsoft Security Response Center (MSRC) Advisory: CVE-2026-114155
  • [2]Official CVE Record (CVE.org): CVE-2026-114155
  • [3]Fix Commit in dotnet/dotnet (c46253a634a2725fd34820ad8a36a3f106f5d01d)
  • [4]Merge Pull Request Commit in dotnet/dotnet (032f564872d408f03fba11c8d03e5cd7386164cd)
  • [5]GitHub Pull Request 10885 (dotnet/dotnet)
  • [6]GitHub Pull Request 10886 (dotnet/dotnet)
  • [7].NET Core Release Tag v10.0.15
  • [8].NET Core Release Tag v11.0.0
  • [9].NET Release Tag v10.0.404
  • [10].NET Release Tag v11.0.100

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.

More Reports

•about 1 hour ago•CVE-2026-118748
7.5

CVE-2026-118748: Unbounded Memory Allocation Denial of Service in ASP.NET Core Blazor Static-SSR

CVE-2026-118748 is a remote Denial of Service (DoS) vulnerability in Microsoft ASP.NET Core (.NET 11.0 preview releases prior to 11.0.0) and Microsoft Visual Studio 2026 (prior to 18.12.0). The flaw originates from unbounded resource allocation (CWE-770) within the Blazor static server-side rendering (static-SSR) validation pipeline, where dynamic or attacker-supplied form field identifiers are cached in a singleton-scoped memory structure without size limits or eviction policies. Unauthenticated remote attackers can exploit this condition over the network by sending repeated HTTP requests with unique field names, exhausting heap memory and terminating the host process via an unhandled OutOfMemoryException.

Alon Barad
Alon Barad
3 views•9 min read
•about 2 hours ago•CVE-2026-114150
5.9

CVE-2026-114150: Remote Information Disclosure via Uninitialized Pooled Memory in ASP.NET Core Response Buffering

CVE-2026-114150 is a network-accessible information disclosure vulnerability affecting Microsoft ASP.NET Core in .NET 11.0 preview releases and Microsoft Visual Studio 2026 prior to version 18.12.0. The flaw stems from the use of uninitialized resources (CWE-908) within response buffering components, specifically ResponseBufferingStream used by middleware such as HTTP Logging and Output Caching. Because rented memory segments from ArrayPool<byte> or MemoryPool<byte> are not cleared upon return or strictly bounded to the exact written byte count during subsequent reads, residual response payloads from earlier HTTP requests remain in the buffer tail. Downstream consumers or log sinks reading these buffers expose confidential cross-session data, including authentication tokens and personally identifiable information.

Amit Schendel
Amit Schendel
2 views•11 min read
•about 3 hours ago•CVE-2026-114146
9.0

CVE-2026-114146: Elevation of Privilege in ASP.NET Core SignalR Connection Dispatcher

An incorrect authorization vulnerability (CWE-863) exists in the ASP.NET Core SignalR HTTP Connection Dispatcher (`Microsoft.AspNetCore.Http.Connections`). The connection negotiation process failed to bind the authenticated caller identity to the generated connection state, and subsequent transport or message dispatch handlers failed to verify caller consistency. Consequently, an authenticated low-privilege attacker who obtains a valid connection identifier or token can inject messages into or hijack another user's SignalR session, achieving an elevation of privilege across security boundaries.

Alon Barad
Alon Barad
5 views•11 min read
•about 4 hours ago•CVE-2026-109478
7.5

CVE-2026-109478: Algorithmic Complexity Denial of Service in ASP.NET Core Header Parsing

CVE-2026-109478 is a network-accessible Denial of Service (DoS) vulnerability in ASP.NET Core caused by inefficient algorithmic complexity (CWE-407) when parsing comma-separated HTTP request headers. Prior to the patch, the internal helper method ParsingHelpers.GetHeaderSplit repeatedly called StringValues.Concat inside a loop iterating over each comma-separated token. Because StringValues allocates a new array of size k + 1 and copies all existing elements upon each concatenation, processing N tokens incurs O(N^2) time and space complexity. An unauthenticated remote attacker can send HTTP requests with headers containing thousands of short, comma-separated values to exhaust CPU cycles and induce severe Garbage Collection (GC) pauses, rendering the web application unresponsive.

Alon Barad
Alon Barad
6 views•11 min read
•about 5 hours ago•CVE-2026-114147
7.5

CVE-2026-114147: Denial of Service via Memory Leak in ASP.NET Core SignalR Redis Backplane

CVE-2026-114147 is a high-severity Denial of Service (DoS) vulnerability affecting ASP.NET Core and specifically the Microsoft.AspNetCore.SignalR.StackExchangeRedis scale-out provider across .NET 8.0, 9.0, 10.0, and 11.0 preview releases. The vulnerability originates from a string comparison mismatch between internal per-connection group collections and the centralized Redis channel subscription registry, compounded by a race condition during connection teardown. Remote unauthenticated attackers can exploit this flaw to leave orphaned connection contexts and active Redis pub/sub listeners pinned in memory, leading to unbounded memory exhaustion and application process termination.

Amit Schendel
Amit Schendel
3 views•10 min read
•about 6 hours ago•CVE-2026-114156
6.5

CVE-2026-114156: Path Equivalence Security Feature Bypass in Microsoft ASP.NET Core

CVE-2026-114156 is an improper resolution of path equivalence vulnerability (CWE-41) in Microsoft ASP.NET Core affecting .NET 11.0 preview releases and Microsoft Visual Studio 2026. The vulnerability stems from a discrepancy between how path prefix validation helpers (such as PathString.StartsWithSegments) and downstream routing mechanisms handle alternate path separators and percent-encoded characters. An unauthenticated remote attacker can exploit this mismatch over the network to bypass path-based authorization middleware and access restricted endpoints.

Amit Schendel
Amit Schendel
5 views•8 min read