Skip to content

Commit c458c2f

Browse files
committed
Better handling for some edge cases
1 parent 4861aae commit c458c2f

2 files changed

Lines changed: 81 additions & 51 deletions

File tree

RunAsPleb/NativeMethods.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,17 @@ internal static partial class NativeMethods
88
{
99
public const uint TOKEN_QUERY = 0x0008;
1010
public const uint TOKEN_DUPLICATE = 0x0002;
11+
public const uint TOKEN_ASSIGN_PRIMARY = 0x0001;
12+
1113
public const uint LUA_TOKEN = 0x4;
1214
public const nint CURRENT_PROCESS_HANDLE = -1;
1315

1416
public const int STARTF_USESHOWWINDOW = 0x00000001;
1517

1618
public const short SW_SHOWNORMAL = 1;
1719

20+
public const int CREATE_UNICODE_ENVIRONMENT = 0x00000400;
21+
1822
internal enum TOKEN_INFORMATION_CLASS
1923
{
2024
// ReSharper disable once UnusedMember.Global
@@ -67,6 +71,19 @@ internal record struct TOKEN_ELEVATION
6771
public int TokenIsElevated;
6872
}
6973

74+
[LibraryImport("user32.dll", EntryPoint = "GetShellWindow")]
75+
[DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
76+
public static partial nint GetShellWindow();
77+
78+
[LibraryImport("kernel32.dll", EntryPoint = "GetEnvironmentStringsW")]
79+
[DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
80+
public static partial nint GetEnvironmentStringsW();
81+
82+
[LibraryImport("kernel32.dll", EntryPoint = "FreeEnvironmentStringsW")]
83+
[DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
84+
[return: MarshalAs(UnmanagedType.Bool)]
85+
public static partial bool FreeEnvironmentStringsW(nint lpszEnvironmentBlock);
86+
7087
[LibraryImport("advapi32.dll", EntryPoint = "OpenProcessToken", SetLastError = true)]
7188
[DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
7289
[return: MarshalAs(UnmanagedType.Bool)]

RunAsPleb/Program.cs

Lines changed: 64 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -24,34 +24,21 @@ private static void Main(string[] args)
2424
string? targetFileDirectory = Path.GetDirectoryName(targetFilePath);
2525
Debug.Assert(targetFileDirectory is not null);
2626

27-
if (!NativeMethods.OpenProcessToken(NativeMethods.CURRENT_PROCESS_HANDLE, NativeMethods.TOKEN_QUERY | NativeMethods.TOKEN_DUPLICATE, out nint tokenHandle))
27+
if (!NativeMethods.OpenProcessToken(NativeMethods.CURRENT_PROCESS_HANDLE, NativeMethods.TOKEN_QUERY, out nint tokenHandleWithTokenQueryPrivilege))
2828
{
29-
if (TryExplorerShellExecute(targetFilePath, targetFileDirectory))
30-
{
31-
return;
32-
}
33-
34-
if (!Launch(targetFilePath, targetFileDirectory))
35-
{
36-
ShellExecute(targetFilePath, targetFileDirectory);
37-
}
38-
29+
Launch(targetFilePath, targetFileDirectory);
3930
return;
4031
}
4132

4233
try
4334
{
44-
if (!IsTokenElevated(tokenHandle))
35+
if (!IsTokenElevated(tokenHandleWithTokenQueryPrivilege))
4536
{
46-
if (!Launch(targetFilePath, targetFileDirectory))
47-
{
48-
ShellExecute(targetFilePath, targetFileDirectory);
49-
}
50-
37+
Launch(targetFilePath, targetFileDirectory);
5138
return;
5239
}
5340

54-
if (TryTokenDeElevation(targetFilePath, targetFileDirectory, tokenHandle))
41+
if (TryTokenDeElevation(targetFilePath, targetFileDirectory))
5542
{
5643
return;
5744
}
@@ -61,26 +48,23 @@ private static void Main(string[] args)
6148
return;
6249
}
6350

64-
if (!Launch(targetFilePath, targetFileDirectory))
65-
{
66-
ShellExecute(targetFilePath, targetFileDirectory);
67-
}
51+
Launch(targetFilePath, targetFileDirectory);
6852
}
6953
finally
7054
{
71-
if (tokenHandle is not 0)
55+
if (tokenHandleWithTokenQueryPrivilege is not 0)
7256
{
73-
_ = NativeMethods.CloseHandle(tokenHandle);
57+
_ = NativeMethods.CloseHandle(tokenHandleWithTokenQueryPrivilege);
7458
}
7559
}
7660
}
7761

78-
private static bool IsTokenElevated(nint tokenHandle)
62+
private static bool IsTokenElevated(nint tokenHandleWithTokenQueryPrivilege)
7963
{
80-
return NativeMethods.GetTokenInformation(tokenHandle, NativeMethods.TOKEN_INFORMATION_CLASS.TokenElevation, out NativeMethods.TOKEN_ELEVATION elevation, Unsafe.SizeOf<NativeMethods.TOKEN_ELEVATION>(), out _) && elevation.TokenIsElevated is not 0;
64+
return NativeMethods.GetTokenInformation(tokenHandleWithTokenQueryPrivilege, NativeMethods.TOKEN_INFORMATION_CLASS.TokenElevation, out NativeMethods.TOKEN_ELEVATION elevation, Unsafe.SizeOf<NativeMethods.TOKEN_ELEVATION>(), out _) && elevation.TokenIsElevated is not 0;
8165
}
8266

83-
private static bool Launch(string targetFilePath, string targetFileDirectory)
67+
private static void Launch(string targetFilePath, string targetFileDirectory)
8468
{
8569
NativeMethods.STARTUPINFO startupInfo = new()
8670
{
@@ -94,60 +78,89 @@ private static bool Launch(string targetFilePath, string targetFileDirectory)
9478
{
9579
_ = NativeMethods.CloseHandle(processInformation.hProcess);
9680
_ = NativeMethods.CloseHandle(processInformation.hThread);
97-
return true;
81+
return;
9882
}
9983

100-
return false;
84+
// CreateProcessW can't run shortcuts but ShellExecuteW can
85+
ShellExecute(targetFilePath, targetFileDirectory);
10186
}
10287

103-
private static bool TryTokenDeElevation(string targetFilePath, string targetFileDirectory, nint tokenHandle)
88+
private static bool TryTokenDeElevation(string targetFilePath, string targetFileDirectory)
10489
{
90+
nint currentProcessTokenHandle = 0;
10591
nint newTokenHandle = 0;
92+
nint environment = 0;
10693
try
10794
{
108-
if (!NativeMethods.GetTokenInformation(tokenHandle, NativeMethods.TOKEN_INFORMATION_CLASS.TokenLinkedToken, out NativeMethods.TOKEN_LINKED_TOKEN linkedToken, Unsafe.SizeOf<NativeMethods.TOKEN_LINKED_TOKEN>(), out _))
95+
if (NativeMethods.OpenProcessToken(NativeMethods.CURRENT_PROCESS_HANDLE, NativeMethods.TOKEN_QUERY | NativeMethods.TOKEN_DUPLICATE | NativeMethods.TOKEN_ASSIGN_PRIMARY, out currentProcessTokenHandle))
10996
{
110-
if (!NativeMethods.CreateRestrictedToken(tokenHandle, NativeMethods.LUA_TOKEN, 0, 0, 0, 0, 0, 0, out newTokenHandle))
97+
// Requires TOKEN_QUERY
98+
if (NativeMethods.GetTokenInformation(currentProcessTokenHandle, NativeMethods.TOKEN_INFORMATION_CLASS.TokenLinkedToken, out NativeMethods.TOKEN_LINKED_TOKEN linkedToken, Unsafe.SizeOf<NativeMethods.TOKEN_LINKED_TOKEN>(), out _))
11199
{
112-
return false;
100+
newTokenHandle = linkedToken.LinkedToken;
101+
}
102+
else
103+
{
104+
// Requires TOKEN_DUPLICATE
105+
_ = NativeMethods.CreateRestrictedToken(currentProcessTokenHandle, NativeMethods.LUA_TOKEN, 0, 0, 0, 0, 0, 0, out newTokenHandle);
113106
}
114-
}
115-
else
116-
{
117-
newTokenHandle = linkedToken.LinkedToken;
118-
}
119-
120-
NativeMethods.STARTUPINFO startupInfo = new()
121-
{
122-
cb = Unsafe.SizeOf<NativeMethods.STARTUPINFO>(),
123-
dwFlags = NativeMethods.STARTF_USESHOWWINDOW,
124-
wShowWindow = NativeMethods.SW_SHOWNORMAL
125-
};
126107

127-
bool success = NativeMethods.CreateProcessWithTokenW(newTokenHandle, 0, targetFilePath, null, 0, 0, targetFileDirectory, ref startupInfo, out NativeMethods.PROCESS_INFORMATION processInfo);
128-
if (success)
129-
{
130-
_ = NativeMethods.CloseHandle(processInfo.hProcess);
131-
_ = NativeMethods.CloseHandle(processInfo.hThread);
108+
if (newTokenHandle is not 0)
109+
{
110+
NativeMethods.STARTUPINFO startupInfo = new()
111+
{
112+
cb = Unsafe.SizeOf<NativeMethods.STARTUPINFO>(),
113+
dwFlags = NativeMethods.STARTF_USESHOWWINDOW,
114+
wShowWindow = NativeMethods.SW_SHOWNORMAL
115+
};
116+
117+
environment = NativeMethods.GetEnvironmentStringsW();
118+
119+
// Requires TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY and SE_IMPERSONATE_NAME
120+
// We don't need to set AdjustTokenPrivileges since for an elevated process we already have SE_IMPERSONATE_NAME
121+
bool success = NativeMethods.CreateProcessWithTokenW(newTokenHandle, 0, targetFilePath, null, NativeMethods.CREATE_UNICODE_ENVIRONMENT, environment, targetFileDirectory, ref startupInfo, out NativeMethods.PROCESS_INFORMATION processInfo);
122+
if (success)
123+
{
124+
_ = NativeMethods.CloseHandle(processInfo.hProcess);
125+
_ = NativeMethods.CloseHandle(processInfo.hThread);
126+
}
127+
128+
return success;
129+
}
132130
}
133131

134-
return success;
132+
return false;
135133
}
136134
catch
137135
{
138136
return false;
139137
}
140138
finally
141139
{
140+
if (currentProcessTokenHandle is not 0)
141+
{
142+
_ = NativeMethods.CloseHandle(currentProcessTokenHandle);
143+
}
144+
142145
if (newTokenHandle is not 0)
143146
{
144147
_ = NativeMethods.CloseHandle(newTokenHandle);
145148
}
149+
150+
if (environment is not 0)
151+
{
152+
_ = NativeMethods.FreeEnvironmentStringsW(environment);
153+
}
146154
}
147155
}
148156

149157
private static bool TryExplorerShellExecute(string targetFilePath, string targetFileDirectory)
150158
{
159+
if (NativeMethods.GetShellWindow() is 0)
160+
{
161+
return false;
162+
}
163+
151164
try
152165
{
153166
// ReSharper disable once SuspiciousTypeConversion.Global

0 commit comments

Comments
 (0)