Skip to content

Commit c40b79f

Browse files
committed
Restricted process and thread handles in TokenSwitchMode.
Restricted ACL of the elevated process to High Integrity.
1 parent 8867555 commit c40b79f

8 files changed

+184
-71
lines changed

src/gsudo/Helpers/ProcessFactory.cs

+36-5
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ private static SafeProcessHandle CreateProcessWithToken(IntPtr newToken, string
332332
return new SafeProcessHandle(processInformation.hProcess, true);
333333
}
334334

335-
internal static SafeProcessHandle CreateProcessAsUserWithFlags(string lpApplicationName, string args, ProcessApi.CreateProcessFlags dwCreationFlags, out PROCESS_INFORMATION pInfo)
335+
internal static void CreateProcessForTokenReplacement(string lpApplicationName, string args, ProcessApi.CreateProcessFlags dwCreationFlags, out SafeProcessHandle processHandle, out SafeHandle threadHandle, out int processId)
336336
{
337337
var sInfoEx = new ProcessApi.STARTUPINFOEX();
338338
sInfoEx.StartupInfo.cb = Marshal.SizeOf(sInfoEx);
@@ -342,8 +342,10 @@ internal static SafeProcessHandle CreateProcessAsUserWithFlags(string lpApplicat
342342
pSec.nLength = Marshal.SizeOf(pSec);
343343
tSec.nLength = Marshal.SizeOf(tSec);
344344

345-
// Set more restrictive Security Descriptor
345+
// Set more restrictive Security Descriptor:
346+
// Changing the owner fails, changing the Mandatory integrity level too. So let's do this on the elevated side
346347
string sddl = "D:(D;;GAFAWD;;;S-1-1-0)"; // Deny Generic-All, File-All, and Write-Dac to everyone.
348+
//string sddl = "D:(D;;GAFAWD;;;S-1-1-0)S:(ML;;NW;;;HI)"; // Deny Generic-All, File-All, and Write-Dac to everyone.
347349

348350
IntPtr sd_ptr = new IntPtr();
349351
UIntPtr sd_size_ptr = new UIntPtr();
@@ -354,15 +356,44 @@ internal static SafeProcessHandle CreateProcessAsUserWithFlags(string lpApplicat
354356

355357
var command = $"{lpApplicationName} {args}";
356358

357-
Logger.Instance.Log($"{nameof(CreateProcessAsUserWithFlags)}: {lpApplicationName} {args}", LogLevel.Debug);
359+
PROCESS_INFORMATION pInfo;
360+
Logger.Instance.Log($"Creating target process: {lpApplicationName} {args}", LogLevel.Debug);
358361
if (!ProcessApi.CreateProcess(null, command, ref pSec, ref tSec, false, dwCreationFlags, IntPtr.Zero, null, ref sInfoEx, out pInfo))
359362
{
360363
throw new Win32Exception((int)ConsoleApi.GetLastError());
361364
}
362365

363-
return new SafeProcessHandle(pInfo.hProcess, true);
364-
}
366+
var currentProcessHandle = ProcessApi.GetCurrentProcess();
367+
368+
if(!DuplicateHandle(
369+
currentProcessHandle, // Source process handle is the current process
370+
pInfo.hProcess, // The handle to duplicate
371+
currentProcessHandle, // Target process handle is also the current process
372+
out var restrictedProcessHandle, // The duplicated handle with desired access rights
373+
0x1000 | 0x00100000 | 0x0001, // Desired access: PROCESS_QUERY_LIMITED_INFORMATION | SYNCHRONIZE | PROCESS_TERMINATE
374+
false, // The handle is not inheritable
375+
1)) // dwOptions: auto close pInfo.hProcess.
376+
{
377+
throw new Win32Exception(Marshal.GetLastWin32Error());
378+
}
365379

380+
if (!DuplicateHandle(
381+
currentProcessHandle, // Source process handle is the current process
382+
pInfo.hThread, // The thread handle to duplicate
383+
currentProcessHandle, // Target process handle is also the current process
384+
out var restrictedThreadHandle, // The duplicated handle with desired access rights
385+
0x0002, // Desired access: THREAD_SUSPEND_RESUME
386+
false, // The handle is not inheritable
387+
1)) // dwOptions: auto close pInfo.hThread.
388+
{
389+
throw new Win32Exception(Marshal.GetLastWin32Error());
390+
}
391+
392+
processHandle = new SafeProcessHandle(restrictedProcessHandle, true);
393+
threadHandle = new Native.SafeThreadHandle(restrictedThreadHandle);
394+
395+
processId = pInfo.dwProcessId;
396+
}
366397
}
367398
}
368399

src/gsudo/Tokens/NativeMethods.cs src/gsudo/Native/NativeMethods.cs

+38-18
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
using gsudo.Native;
2-
using System;
1+
using System;
32
using System.Runtime.ConstrainedExecution;
43
using System.Runtime.InteropServices;
54
using System.Security.Principal;
65
using static gsudo.Native.TokensApi;
76

8-
namespace gsudo.Tokens
7+
namespace gsudo.Native
98
{
109
internal static partial class NativeMethods
1110
{
@@ -21,19 +20,19 @@ internal static partial class NativeMethods
2120
internal const int SE_PRIVILEGE_DISABLED = 0x00000000;
2221
internal const int ERROR_NOT_ALL_ASSIGNED = 0x00000514;
2322

24-
internal const UInt32 STANDARD_RIGHTS_REQUIRED = 0x000F0000;
25-
internal const UInt32 STANDARD_RIGHTS_READ = 0x00020000;
26-
internal const UInt32 TOKEN_ASSIGN_PRIMARY = 0x0001;
27-
internal const UInt32 TOKEN_DUPLICATE = 0x0002;
28-
internal const UInt32 TOKEN_IMPERSONATE = 0x0004;
29-
internal const UInt32 TOKEN_QUERY = 0x0008;
30-
internal const UInt32 TOKEN_QUERY_SOURCE = 0x0010;
31-
internal const UInt32 TOKEN_ADJUST_PRIVILEGES = 0x0020;
32-
internal const UInt32 TOKEN_ADJUST_GROUPS = 0x0040;
33-
internal const UInt32 TOKEN_ADJUST_DEFAULT = 0x0080;
34-
internal const UInt32 TOKEN_ADJUST_SESSIONID = 0x0100;
35-
internal const UInt32 TOKEN_READ = (STANDARD_RIGHTS_READ | TOKEN_QUERY);
36-
internal const UInt32 TOKEN_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED |
23+
internal const uint STANDARD_RIGHTS_REQUIRED = 0x000F0000;
24+
internal const uint STANDARD_RIGHTS_READ = 0x00020000;
25+
internal const uint TOKEN_ASSIGN_PRIMARY = 0x0001;
26+
internal const uint TOKEN_DUPLICATE = 0x0002;
27+
internal const uint TOKEN_IMPERSONATE = 0x0004;
28+
internal const uint TOKEN_QUERY = 0x0008;
29+
internal const uint TOKEN_QUERY_SOURCE = 0x0010;
30+
internal const uint TOKEN_ADJUST_PRIVILEGES = 0x0020;
31+
internal const uint TOKEN_ADJUST_GROUPS = 0x0040;
32+
internal const uint TOKEN_ADJUST_DEFAULT = 0x0080;
33+
internal const uint TOKEN_ADJUST_SESSIONID = 0x0100;
34+
internal const uint TOKEN_READ = STANDARD_RIGHTS_READ | TOKEN_QUERY;
35+
internal const uint TOKEN_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED |
3736
TOKEN_ASSIGN_PRIMARY |
3837
TOKEN_DUPLICATE |
3938
TOKEN_IMPERSONATE |
@@ -42,7 +41,7 @@ internal static partial class NativeMethods
4241
TOKEN_ADJUST_PRIVILEGES |
4342
TOKEN_ADJUST_GROUPS |
4443
TOKEN_ADJUST_DEFAULT |
45-
TOKEN_ADJUST_SESSIONID);
44+
TOKEN_ADJUST_SESSIONID;
4645

4746
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
4847
internal static extern IntPtr GetCurrentProcess();
@@ -65,7 +64,13 @@ internal static extern bool OpenProcessToken(IntPtr processHandle,
6564

6665
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
6766
[return: MarshalAs(UnmanagedType.Bool)]
68-
internal static extern Boolean CloseHandle(IntPtr hObject);
67+
internal static extern bool CloseHandle(IntPtr hObject);
68+
69+
[DllImport("advapi32.dll", SetLastError = true)]
70+
internal static extern bool GetKernelObjectSecurity(IntPtr Handle, uint securityInformation, IntPtr pSecurityDescriptor, uint nLength, out uint lpnLengthNeeded);
71+
72+
[DllImport("advapi32.dll", SetLastError = true)]
73+
internal static extern bool SetKernelObjectSecurity(IntPtr Handle, uint securityInformation, IntPtr pSecurityDescriptor);
6974

7075
[StructLayout(LayoutKind.Sequential)]
7176
public struct LUID
@@ -100,5 +105,20 @@ public struct TOKEN_PRIVILEGES
100105

101106
public LUID_AND_ATTRIBUTES[] Privileges { get => privileges; set => privileges = value; }
102107
}
108+
103109
}
110+
111+
[Flags]
112+
internal enum SECURITY_INFORMATION : uint
113+
{
114+
OWNER_SECURITY_INFORMATION = 0x00000001,
115+
GROUP_SECURITY_INFORMATION = 0x00000002,
116+
DACL_SECURITY_INFORMATION = 0x00000004,
117+
SACL_SECURITY_INFORMATION = 0x00000008,
118+
UNPROTECTED_SACL_SECURITY_INFORMATION = 0x10000000,
119+
UNPROTECTED_DACL_SECURITY_INFORMATION = 0x20000000,
120+
PROTECTED_SACL_SECURITY_INFORMATION = 0x40000000,
121+
PROTECTED_DACL_SECURITY_INFORMATION = 0x80000000
122+
}
123+
104124
}

src/gsudo/Native/ProcessApi.cs

+19-3
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,10 @@ public enum CreateProcessFlags : uint
120120
internal static extern bool DeleteProcThreadAttributeList(IntPtr lpAttributeList);
121121

122122
[DllImport("kernel32.dll", SetLastError = true)]
123-
internal static extern bool CloseHandle(IntPtr hObject);
123+
internal static extern bool CloseHandle(IntPtr hObject);
124+
125+
[DllImport("kernel32.dll", SetLastError = true)]
126+
public static extern IntPtr LocalFree(IntPtr hMem);
124127

125128
[DllImport("kernel32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto, SetLastError = true)]
126129
internal static extern bool GetExitCodeProcess(Microsoft.Win32.SafeHandles.SafeProcessHandle processHandle, out int exitCode);
@@ -190,7 +193,9 @@ protected override bool ReleaseHandle()
190193
#region Query Process Info
191194
public const UInt32 PROCESS_QUERY_INFORMATION = 0x0400;
192195
public const UInt32 PROCESS_SET_INFORMATION = 0x0200;
193-
196+
public const UInt32 READ_CONTROL = 0x00020000;
197+
public const UInt32 WRITE_DAC = 0x40000;
198+
194199
[DllImport("kernel32.dll", SetLastError = true)]
195200
internal static extern IntPtr OpenProcess(UInt32 dwDesiredAccess, Boolean bInheritHandle, UInt32 dwProcessId);
196201

@@ -225,13 +230,24 @@ internal static extern bool CheckRemoteDebuggerPresent(
225230
internal static extern bool CreatePipe(out SafeFileHandle hReadPipe, out SafeFileHandle hWritePipe, SECURITY_ATTRIBUTES lpPipeAttributes, int nSize);
226231

227232
[DllImport("kernel32.dll", SetLastError = true)]
228-
internal static extern uint ResumeThread(IntPtr hThread);
233+
internal static extern int ResumeThread(IntPtr hThread);
229234

230235
[DllImport("kernel32.dll", SetLastError = true)]
231236
[return: MarshalAs(UnmanagedType.Bool)]
232237
internal static extern bool TerminateProcess(IntPtr hProcess, uint uExitCode);
233238

234239
[DllImport("kernel32.dll", SetLastError = true)]
235240
internal static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds);
241+
242+
[DllImport("kernel32.dll", SetLastError = true)]
243+
[return: MarshalAs(UnmanagedType.Bool)]
244+
internal static extern bool DuplicateHandle(
245+
IntPtr hSourceProcessHandle,
246+
IntPtr hSourceHandle,
247+
IntPtr hTargetProcessHandle,
248+
out IntPtr lpTargetHandle,
249+
uint dwDesiredAccess,
250+
[MarshalAs(UnmanagedType.Bool)] bool bInheritHandle,
251+
uint dwOptions);
236252
}
237253
}

src/gsudo/Native/SafeTokenHandle.cs

+15
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,19 @@ protected override bool ReleaseHandle()
2121
return Native.ProcessApi.CloseHandle(base.handle);
2222
}
2323
}
24+
25+
internal sealed class SafeThreadHandle : SafeHandleZeroOrMinusOneIsInvalid
26+
{
27+
internal SafeThreadHandle(IntPtr handle)
28+
: base(true)
29+
{
30+
base.SetHandle(handle);
31+
}
32+
33+
override protected bool ReleaseHandle()
34+
{
35+
return Native.ProcessApi.CloseHandle(handle);
36+
}
37+
38+
}
2439
}

src/gsudo/Native/TokensApi.cs

+14-2
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,19 @@ public struct LUID
414414
#endregion
415415

416416
[DllImport("Advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
417-
internal static extern bool ConvertStringSecurityDescriptorToSecurityDescriptor(string StringSecurityDescriptor, uint StringSDRevision, out IntPtr SecurityDescriptor, out UIntPtr SecurityDescriptorSize);
418-
417+
internal static extern bool ConvertStringSecurityDescriptorToSecurityDescriptor(
418+
string StringSecurityDescriptor,
419+
uint StringSDRevision,
420+
out IntPtr SecurityDescriptor,
421+
out UIntPtr SecurityDescriptorSize);
422+
423+
// Additional imports for DACL manipulation
424+
[DllImport("advapi32.dll", SetLastError = true)]
425+
internal static extern bool ConvertSecurityDescriptorToStringSecurityDescriptor(
426+
IntPtr SecurityDescriptor,
427+
uint StringSDRevision,
428+
SECURITY_INFORMATION SecurityInformation,
429+
out IntPtr StringSecurityDescriptor,
430+
out uint StringSecurityDescriptorLen);
419431
}
420432
}

src/gsudo/ProcessRenderers/TokenSwitchRenderer.cs

+19-26
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
using Microsoft.Win32.SafeHandles;
55
using System;
66
using System.Collections.Generic;
7+
using System.ComponentModel;
78
using System.IO;
89
using System.Linq;
10+
using System.Runtime.InteropServices;
911
using System.Threading;
1012
using System.Threading.Tasks;
1113

@@ -19,8 +21,8 @@ class TokenSwitchRenderer : IProcessRenderer
1921
{
2022
private readonly Connection _connection;
2123
private readonly ElevationRequest _elevationRequest;
22-
private readonly SafeProcessHandle _process;
23-
private readonly ProcessApi.PROCESS_INFORMATION _processInformation;
24+
private readonly SafeProcessHandle _processHandle;
25+
private readonly SafeHandle _threadHandle;
2426
private readonly ManualResetEventSlim tokenSwitchSuccessEvent = new ManualResetEventSlim(false);
2527

2628
internal TokenSwitchRenderer(Connection connection, ElevationRequest elevationRequest)
@@ -55,9 +57,9 @@ internal TokenSwitchRenderer(Connection connection, ElevationRequest elevationRe
5557
args = elevationRequest.Arguments;
5658
}
5759

58-
_process = ProcessFactory.CreateProcessAsUserWithFlags(exeName, args, dwCreationFlags, out _processInformation);
60+
ProcessFactory.CreateProcessForTokenReplacement(exeName, args, dwCreationFlags, out _processHandle, out _threadHandle, out int processId);
5961

60-
elevationRequest.TargetProcessId = _processInformation.dwProcessId;
62+
elevationRequest.TargetProcessId = processId;
6163
if (!elevationRequest.NewWindow)
6264
ConsoleApi.SetConsoleCtrlHandler(ConsoleHelper.IgnoreConsoleCancelKeyPress, true);
6365
}
@@ -68,7 +70,7 @@ public Task<int> Start()
6870
{
6971
var t1 = new StreamReader(_connection.ControlStream).ConsumeOutput(HandleControlStream);
7072

71-
WaitHandle.WaitAny(new WaitHandle[] { tokenSwitchSuccessEvent.WaitHandle, _process.GetProcessWaitHandle(), _connection.DisconnectedWaitHandle });
73+
WaitHandle.WaitAny(new WaitHandle[] { tokenSwitchSuccessEvent.WaitHandle, _processHandle.GetProcessWaitHandle(), _connection.DisconnectedWaitHandle });
7274

7375
if (!tokenSwitchSuccessEvent.IsSet)
7476
{
@@ -87,43 +89,34 @@ public Task<int> Start()
8789
_connection.DataStream.Close();
8890
_connection.ControlStream.Close();
8991

90-
return GetResult();
91-
}
92-
finally
93-
{
94-
ConsoleApi.SetConsoleCtrlHandler(ConsoleHelper.IgnoreConsoleCancelKeyPress, false);
95-
}
96-
}
97-
98-
public void TerminateProcess()
99-
{
100-
ProcessApi.TerminateProcess(_process.DangerousGetHandle(), 0);
101-
}
92+
if (ProcessApi.ResumeThread(_threadHandle.DangerousGetHandle()) < 0)
93+
throw new Win32Exception();
10294

103-
public Task<int> GetResult()
104-
{
105-
try
106-
{
107-
_ = ProcessApi.ResumeThread(_processInformation.hThread);
108-
Native.FileApi.CloseHandle(_processInformation.hThread);
95+
_threadHandle.Close();
10996

11097
if (_elevationRequest.Wait)
11198
{
112-
_process.GetProcessWaitHandle().WaitOne();
113-
if (ProcessApi.GetExitCodeProcess(_process, out int exitCode))
99+
_processHandle.GetProcessWaitHandle().WaitOne();
100+
if (ProcessApi.GetExitCodeProcess(_processHandle, out int exitCode))
114101
return Task.FromResult(exitCode);
115102

116-
Native.FileApi.CloseHandle(_processInformation.hProcess);
103+
_processHandle.Close();
117104
}
118105

119106
return Task.FromResult(0);
120107
}
121108
finally
122109
{
110+
_processHandle?.Close();
111+
_threadHandle?.Close();
123112
ConsoleApi.SetConsoleCtrlHandler(ConsoleHelper.IgnoreConsoleCancelKeyPress, false);
124113
}
125114
}
126115

116+
public void TerminateProcess()
117+
{
118+
ProcessApi.TerminateProcess(_processHandle.DangerousGetHandle(), 0);
119+
}
127120

128121
enum Mode { Normal, Error};
129122
Mode CurrentMode = Mode.Normal;

src/gsudo/Tokens/PrivilegeManager.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
using System.Runtime.InteropServices;
44
using System.Security.Principal;
55
using static gsudo.Native.TokensApi;
6-
using static gsudo.Tokens.NativeMethods;
6+
using static gsudo.Native.NativeMethods;
7+
using gsudo.Native;
78

89
namespace gsudo.Tokens
910
{

0 commit comments

Comments
 (0)