From 0ad058645afc60aa2e76e39bfcc0cc223b93a019 Mon Sep 17 00:00:00 2001
From: Tu Dinh <ngoc-tu.dinh@vates.tech>
Date: Fri, 28 Mar 2025 22:58:50 +0000
Subject: Secure Xencons monitor named pipes

* Create monitor named pipes under the
  \\.\pipe\ProtectedPrefix\Administrators prefix to prevent unprivileged
  users from creating named pipe instances under the same path;
* Assign a restrictive security descriptor to monitor named pipes to
  prevent unprivileged users from reading console inputs;
* Set PIPE_REJECT_REMOTE_CLIENTS to prevent monitor named pipes from
  being exposed to the network.

This is part of XSA-468 / CVE-2025-27462.

Signed-off-by: Tu Dinh <ngoc-tu.dinh@vates.tech>
Reviewed-by: Owen Smith <owen.smith@cloud.com>
---
 src/monitor/monitor.c | 36 ++++++++++++++++++++++++++++--------
 src/tty/tty.c         |  2 +-
 2 files changed, 29 insertions(+), 9 deletions(-)

diff --git a/src/monitor/monitor.c b/src/monitor/monitor.c
index 59e6ab6aee78..ec8a638638f8 100644
--- a/src/monitor/monitor.c
+++ b/src/monitor/monitor.c
@@ -40,6 +40,7 @@
 #include <cfgmgr32.h>
 #include <dbt.h>
 #include <setupapi.h>
+#include <sddl.h>
 #include <malloc.h>
 #include <assert.h>
 
@@ -93,7 +94,9 @@ typedef struct _MONITOR_CONNECTION {
 
 static MONITOR_CONTEXT MonitorContext;
 
-#define PIPE_BASE_NAME "\\\\.\\pipe\\xencons\\"
+#define PIPE_BASE_NAME "\\\\.\\pipe\\ProtectedPrefix\\Administrators\\xencons\\"
+// FILE_GENERIC_ALL for SYSTEM and Builtin\Administrators, nothing for the rest
+#define PIPE_SDDL "D:(A;;FA;;;SY)(A;;FA;;;BA)"
 
 #define MAXIMUM_BUFFER_SIZE 1024
 
@@ -436,6 +439,7 @@ ServerThread(
     DWORD               Object;
     PMONITOR_CONNECTION Connection;
     HRESULT             Error;
+    SECURITY_ATTRIBUTES SecurityAttributes;
 
     Log("====> %s", Console->DeviceName);
 
@@ -460,17 +464,26 @@ ServerThread(
 
     Log("%s", PipeName);
 
+    ZeroMemory(&SecurityAttributes, sizeof(SECURITY_ATTRIBUTES));
+    SecurityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
+    SecurityAttributes.bInheritHandle = FALSE;
+    if (!ConvertStringSecurityDescriptorToSecurityDescriptorA(PIPE_SDDL,
+                                                              SDDL_REVISION_1,
+                                                              &SecurityAttributes.lpSecurityDescriptor,
+                                                              NULL))
+        goto fail3;
+
     for (;;) {
         Pipe = CreateNamedPipe(PipeName,
                                PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
-                               PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE,
+                               PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_REJECT_REMOTE_CLIENTS,
                                PIPE_UNLIMITED_INSTANCES,
                                MAXIMUM_BUFFER_SIZE,
                                MAXIMUM_BUFFER_SIZE,
                                0,
-                               NULL);
+                               &SecurityAttributes);
         if (Pipe == INVALID_HANDLE_VALUE)
-            goto fail3;
+            goto fail4;
 
         (VOID) ConnectNamedPipe(Pipe,
                                 &Overlapped);
@@ -488,7 +501,7 @@ ServerThread(
 
         Connection = (PMONITOR_CONNECTION)malloc(sizeof(MONITOR_CONNECTION));
         if (Connection == NULL)
-            goto fail4;
+            goto fail5;
 
         __InitializeListHead(&Connection->ListEntry);
         Connection->Console = Console;
@@ -500,24 +513,31 @@ ServerThread(
                                           0,
                                           NULL);
         if (Connection->Thread == NULL)
-            goto fail5;
+            goto fail6;
     }
 
+    LocalFree(&SecurityAttributes.lpSecurityDescriptor);
+
     CloseHandle(Overlapped.hEvent);
 
     Log("<==== %s", Console->DeviceName);
 
     return 0;
 
+fail6:
+    Log("fail6");
+
+    free(Connection);
+
 fail5:
     Log("fail5");
 
-    free(Connection);
+    CloseHandle(Pipe);
 
 fail4:
     Log("fail4");
 
-    CloseHandle(Pipe);
+    LocalFree(&SecurityAttributes.lpSecurityDescriptor);
 
 fail3:
     Log("fail3");
diff --git a/src/tty/tty.c b/src/tty/tty.c
index c3b6f8e18345..08d372435772 100644
--- a/src/tty/tty.c
+++ b/src/tty/tty.c
@@ -44,7 +44,7 @@ typedef struct _TTY_STREAM {
     HANDLE  Write;
 } TTY_STREAM, *PTTY_STREAM;
 
-#define PIPE_NAME TEXT("\\\\.\\pipe\\xencons\\default")
+#define PIPE_NAME TEXT("\\\\.\\pipe\\ProtectedPrefix\\Administrators\\xencons\\default")
 #define MAXIMUM_BUFFER_SIZE 1024
 
 typedef struct _TTY_CONTEXT {
-- 
2.47.1

