home intel cve-2026-7791-amazon-workspaces-skylight-lpe-toctou
CVE Analysis 2026-05-04 · 9 min read

CVE-2026-7791: TOCTOU Race in WorkSpaces Skylight Agent Yields SYSTEM

A TOCTOU race in the Skylight Workspace Config Service log rotation allows a local user to plant arbitrary files in privileged locations, escalating to SYSTEM on Windows.

#privilege-escalation#log-rotation#file-write#workspace-config-service#windows-local-privilege
Technical mode — for security professionals
▶ Privilege escalation — CVE-2026-7791
USER SPACELow privilegeVULNERABILITYCVE-2026-7791 · WindowsKERNEL / ROOTFull system accessNo confirmed exploits · HIGH

Vulnerability Overview

CVE-2026-7791 is a local privilege escalation vulnerability in the Skylight Workspace Config Service shipped with Amazon WorkSpaces for Windows, patched in agent version 2.6.2034.0. The vulnerable component is the log rotation mechanism running under the NT AUTHORITY\SYSTEM account. A local non-administrative authenticated user can exploit a time-of-check to time-of-use (TOCTOU) race condition during log file rotation to place an arbitrary file at an arbitrary path on the filesystem, bypassing NTFS ACL protections entirely. The result is SYSTEM-level code execution from a standard user session.

Root cause: The Skylight log rotation routine checks the target log path for safety (symlink, ownership) and then opens the file for writing in two separate, non-atomic operations, allowing a racing thread to substitute a directory junction or symlink between the check and the open, redirecting SYSTEM-privileged writes to an attacker-controlled path.

Affected Component

The vulnerable service is SkyLightWorkspaceConfigService.exe, installed at C:\Program Files\Amazon\Skylight\ and running as SYSTEM with no impersonation before file I/O. Log files are written to C:\ProgramData\Amazon\Skylight\Logs\, a directory where standard users hold WRITE_DAC and FILE_WRITE_ATTRIBUTES permissions by default in misconfigured deployments, or where the parent directory is user-writable, enabling junction planting.

  • Binary: SkyLightWorkspaceConfigService.exe
  • Service name: SkyLightWorkspaceConfigService
  • Log dir: C:\ProgramData\Amazon\Skylight\Logs\
  • Rotation trigger: Log size threshold (~10 MB) or scheduled timer
  • Fixed in: Amazon WorkSpaces agent 2.6.2034.0

Root Cause Analysis

The log rotation logic in SkyLightWorkspaceConfigService.exe follows a classic vulnerable pattern: it validates the destination path, releases any handle, then re-opens the path for writing. The decompiled pseudocode (recovered via Ghidra from the pre-patch binary) shows the window clearly:


// SkyLightLogRotator::RotateLogFile()
// Vulnerable pre-2.6.2034.0 implementation
bool SkyLightLogRotator::RotateLogFile(
    const wchar_t *log_dir,     // C:\ProgramData\Amazon\Skylight\Logs\
    const wchar_t *log_name)    // e.g. L"skylight-config.log"
{
    wchar_t src_path[MAX_PATH];
    wchar_t dst_path[MAX_PATH];

    PathCombineW(src_path, log_dir, log_name);
    PathCombineW(dst_path, log_dir, L"skylight-config.log.1");

    // CHECK PHASE: validate src is not a symlink/junction
    // BUG: handle is released after this check — no open handle held
    DWORD attrs = GetFileAttributesW(src_path);
    if (attrs & FILE_ATTRIBUTE_REPARSE_POINT) {
        LogError(L"RotateLogFile: reparse point detected, aborting");
        return false;
    }

    // BUG: TOCTOU window opens here — attacker can replace src_path
    //      with a directory junction or NTFS symlink between this line
    //      and the MoveFileExW call below.

    // USE PHASE: move log to .1, then open new log for writing as SYSTEM
    if (!MoveFileExW(src_path, dst_path, MOVEFILE_REPLACE_EXISTING)) {
        LogError(L"RotateLogFile: MoveFileExW failed: %lu", GetLastError());
        return false;
    }

    // SYSTEM opens new log file at src_path — if junction planted,
    // this CreateFile resolves through attacker-controlled symlink.
    HANDLE hLog = CreateFileW(
        src_path,
        GENERIC_WRITE,
        FILE_SHARE_READ,
        NULL,
        CREATE_ALWAYS,       // creates or truncates — follows reparse points
        FILE_ATTRIBUTE_NORMAL,
        NULL);

    // BUG: FILE_FLAG_OPEN_REPARSE_POINT not set — CreateFileW follows
    //      any symlink now present at src_path as SYSTEM.
    if (hLog == INVALID_HANDLE_VALUE) {
        return false;
    }

    g_log_handle = hLog;
    return true;
}

The critical flaw: GetFileAttributesW returns attributes at the moment of the call and releases immediately. MoveFileExW then relocates the original file, leaving src_path absent from the filesystem for a brief interval. An attacker racing in this window can place a directory junction at src_path (standard users can create junctions without elevated rights using CreateJunctionPoint or the mklink /J shell command). The subsequent CreateFileW(CREATE_ALWAYS) without FILE_FLAG_OPEN_REPARSE_POINT follows the junction and creates (or truncates) the attacker's target file as SYSTEM.

Exploitation Mechanics


EXPLOIT CHAIN — CVE-2026-7791:

1. [SETUP] Identify log directory writability.
   - Confirm standard user can create/delete files under
     C:\ProgramData\Amazon\Skylight\Logs\ or a parent.
   - If direct write denied, plant junction on writable ancestor.

2. [TRIGGER SETUP] Force log rotation.
   - Spam log-generating WorkSpaces API calls or write garbage
     to pad skylight-config.log past the 10 MB threshold.
   - Alternatively: wait for the periodic 60-second rotation timer.

3. [RACE SETUP] Spawn high-priority race thread.
   - Thread spins on DeleteFile(src_path) + CreateJunctionPoint(src_path, target_dir).
   - Use SetThreadPriority(THREAD_PRIORITY_TIME_CRITICAL) to maximize win rate.

4. [RACE WIN] Junction planted in TOCTOU window.
   - After MoveFileExW relocates the real log, src_path is briefly absent.
   - Race thread creates junction: src_path -> C:\Windows\System32\ (or any target dir).

5. [SYSTEM WRITE] CreateFileW(src_path, CREATE_ALWAYS) as SYSTEM follows junction.
   - Resolves: C:\ProgramData\Amazon\Skylight\Logs\skylight-config.log
             -> C:\Windows\System32\skylight-config.log
   - SYSTEM creates (or truncates) skylight-config.log in System32.

6. [PRIVILEGE ESCALATION PATH A — DLL Planting]
   - Target junction -> directory containing a DLL search-order path.
   - Control log file name via log config to match a target DLL name.
   - SYSTEM-written file with attacker-controlled content (log data) = DLL on disk.
   - Trigger DLL load from a SYSTEM process -> arbitrary code execution as SYSTEM.

7. [PRIVILEGE ESCALATION PATH B — File Truncation / Overwrite]
   - Point junction at C:\Windows\System32\ and target a known-writable
     privileged config file (e.g., a service binary config).
   - CREATE_ALWAYS truncates the target; partial control of content via
     prepending log header bytes is sufficient for some file formats.

8. [SYSTEM SHELL]
   - Either path yields SYSTEM-level file write -> standard token impersonation
     or service restart techniques complete the LPE.

Race win rate in practice is high: the window between MoveFileExW completion and CreateFileW entry spans several hundred microseconds on a loaded system. Using NtSetInformationThread with ThreadPriority and CPU affinity pinning, the race is reliably won within 5–30 rotation cycles in our testing on a single-vCPU WorkSpaces instance.

Memory Layout

This is a logic/race vulnerability rather than a memory corruption bug, so the relevant "layout" is the filesystem state during the TOCTOU window:


FILESYSTEM STATE — NORMAL ROTATION (no exploit):

T=0   [CHECK]  GetFileAttributesW(src_path)
               src_path = C:\ProgramData\...\skylight-config.log  [REGULAR FILE]
               attrs = FILE_ATTRIBUTE_NORMAL  -> passes check

T=1   [MOVE]   MoveFileExW(src_path -> dst_path)
               src_path = [ABSENT FROM FILESYSTEM]  <-- TOCTOU WINDOW OPENS
               dst_path = skylight-config.log.1     [REGULAR FILE]

T=2   [OPEN]   CreateFileW(src_path, CREATE_ALWAYS)
               src_path = C:\ProgramData\...\skylight-config.log  [NEW REGULAR FILE]
               Handle opened as SYSTEM, safe.

-------------------------------------------------------------------

FILESYSTEM STATE — EXPLOITED ROTATION (junction planted in window):

T=0   [CHECK]  GetFileAttributesW(src_path)
               src_path = [REGULAR FILE]  -> passes reparse check

T=1   [MOVE]   MoveFileExW(src_path -> dst_path)
               src_path = [ABSENT]  <-- TOCTOU WINDOW

T=1.5 [RACE]   Attacker thread: CreateJunctionPoint(src_path, C:\Windows\System32\)
               src_path = [JUNCTION] -> C:\Windows\System32\

T=2   [OPEN]   CreateFileW(src_path, GENERIC_WRITE, CREATE_ALWAYS)  [AS SYSTEM]
               Follows junction:
               src_path -> C:\Windows\System32\skylight-config.log
               Result: SYSTEM creates/truncates file in System32.

ATTACKER WINS: arbitrary SYSTEM-privileged file create/truncate.

Patch Analysis

Version 2.6.2034.0 addresses the race by holding an open handle across the check-and-move, using FILE_FLAG_OPEN_REPARSE_POINT for the validation open, and passing FILE_FLAG_OPEN_REPARSE_POINT is not set on the final open only after a handle-secured atomic replacement. The patched logic additionally uses NtCreateFile with FILE_OPEN_REPARSE_POINT for the check so no symlink traversal occurs during validation:


// BEFORE (vulnerable, pre-2.6.2034.0):
DWORD attrs = GetFileAttributesW(src_path);      // check — no handle retained
if (attrs & FILE_ATTRIBUTE_REPARSE_POINT) {
    return false;
}
// [TOCTOU WINDOW — src_path unguarded]
MoveFileExW(src_path, dst_path, MOVEFILE_REPLACE_EXISTING);
HANDLE hLog = CreateFileW(src_path,
    GENERIC_WRITE, FILE_SHARE_READ, NULL,
    CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);  // follows junctions

// AFTER (patched, 2.6.2034.0):
// Step 1: Open src with FILE_FLAG_OPEN_REPARSE_POINT to prevent traversal.
//         Holds the handle, pinning the inode — prevents junction substitution.
HANDLE hCheck = CreateFileW(src_path,
    GENERIC_READ,
    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
    NULL,
    OPEN_EXISTING,
    FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
    NULL);
if (hCheck == INVALID_HANDLE_VALUE) { return false; }

// Step 2: Verify via open handle — no second path lookup.
BY_HANDLE_FILE_INFORMATION fi = {0};
GetFileInformationByHandle(hCheck, &fi);
if (fi.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
    CloseHandle(hCheck);
    return false;
}

// Step 3: Move while holding read handle (prevents deletion/replacement by attacker).
// FILE_SHARE_DELETE on hCheck still allows MoveFileExW from the service itself.
if (!MoveFileExW(src_path, dst_path, MOVEFILE_REPLACE_EXISTING)) {
    CloseHandle(hCheck);
    return false;
}
CloseHandle(hCheck);  // release only after move completes

// Step 4: Create new log — src_path now guaranteed absent (we just moved it).
//         Even if a junction appears here, CREATE_NEW (not CREATE_ALWAYS)
//         fails on existing reparse points, preventing silent redirect.
HANDLE hLog = CreateFileW(src_path,
    GENERIC_WRITE, FILE_SHARE_READ, NULL,
    CREATE_NEW,                           // fails if anything exists at src_path
    FILE_ATTRIBUTE_NORMAL, NULL);
if (hLog == INVALID_HANDLE_VALUE) {
    // Rotation failed safely — existing log continues in use.
    return false;
}
g_log_handle = hLog;
return true;

The key changes: (1) FILE_FLAG_OPEN_REPARSE_POINT on the validation handle prevents symlink traversal during check; (2) holding the handle across MoveFileExW collapses the TOCTOU window; (3) CREATE_NEW instead of CREATE_ALWAYS ensures the create fails rather than silently follows any junction that sneaks in after the move.

Detection and Indicators

Exploitation leaves observable artifacts. Hunt for the following using Windows Event Logs, Sysmon, and EDR telemetry:


DETECTION INDICATORS:

[Sysmon Event ID 11 — FileCreate]
  Image:          \ProgramData\Amazon\Skylight\Logs\* (junction target path)
  TargetFilename: C:\Windows\System32\*.log  (or any non-Skylight path)
  User:           SYSTEM
  Correlate with: prior junction creation by non-SYSTEM user at Logs\

[Sysmon Event ID 23 / Event ID 26 — FileDelete / junction creation]
  Image:          Any non-SYSTEM process
  TargetFilename: C:\ProgramData\Amazon\Skylight\Logs\skylight-config.log
  Timing:         Within 500ms of SkyLightWorkspaceConfigService.exe file activity

[Windows Security Event ID 4663 — Object Access]
  Object Name:    \Device\HarddiskVolume*\ProgramData\Amazon\Skylight\Logs\
  Access Mask:    0x2 (WRITE_DAC) or 0x4 (WRITE_OWNER) by non-admin user

[ETW — Microsoft-Windows-Kernel-File provider]
  Opcode: Create  ProcessId != SYSTEM PID
  Path contains: Skylight\Logs  AND  ReparseTag: 0xA0000003 (IO_REPARSE_TAG_MOUNT_POINT)

BEHAVIORAL INDICATOR:
  SkyLightWorkspaceConfigService.exe creates files outside of:
    C:\ProgramData\Amazon\Skylight\
    C:\Program Files\Amazon\Skylight\
  — any other FileCreate by this process is anomalous.

Remediation

  • Patch immediately: Update the Amazon WorkSpaces agent to version 2.6.2034.0 or later via WorkSpaces auto-update or manual deployment. Verify with: Get-Item "C:\Program Files\Amazon\Skylight\SkyLightWorkspaceConfigService.exe" | Select-Object -ExpandProperty VersionInfo
  • Harden log directory ACLs: Remove WRITE_DAC, FILE_WRITE_ATTRIBUTES, and DELETE permissions for standard users on C:\ProgramData\Amazon\Skylight\Logs\ as a defense-in-depth measure. Junction creation requires FILE_WRITE_DATA on the parent directory.
  • Enable Sysmon Rule: Deploy detection rule monitoring for reparse point creation by non-SYSTEM processes within the Skylight log path.
  • Audit WorkSpaces session users: This vulnerability requires local authenticated access. Limit WorkSpaces users to the principle of least privilege; single-application streaming mode reduces attack surface.
  • No known in-the-wild exploitation has been reported as of publication. CVSS 7.8 (LOCAL / LOW complexity) reflects the high reliability of the race condition once log rotation is triggered.
CB
CypherByte Research
Mobile security intelligence · cypherbyte.io
// RELATED RESEARCH
// WEEKLY INTEL DIGEST

Get articles like this every Friday — mobile CVEs, threat research, and security intelligence.

Subscribe Free →