从使用更安全令牌创建的进程调用时,RunAs Verb 不显示 UAC 弹出窗口且不提升
Posted
技术标签:
【中文标题】从使用更安全令牌创建的进程调用时,RunAs Verb 不显示 UAC 弹出窗口且不提升【英文标题】:RunAs Verb not showing a UAC popup and not elevating, when invoked from a process created with a Safer token 【发布时间】:2020-02-28 15:38:38 【问题描述】:我想从提升的进程开始一个非提升的进程。 我已经使用 SaferComputeTokenFromLevel 完成了这项工作。它似乎正在工作,因为:
C:\>whoami /groups | findstr BUILTIN\Administrators
BUILTIN\Administrators Alias S-1-5-32-544 Group used for deny only
仅拒绝。所以我不是管理员。
1) 为什么(疑似)非管理员进程控制台主机窗口在标题中显示为“管理员:”?我什至使用SetTokenInformation
将其设置为中等完整性。它可以工作,但即使不是管理员,它仍然显示为“管理员”。
然后,如果该非管理员进程想要通过ShellExecute
和Verb=RunAs
(使用powershell -C Start-Process -Verb RunAs -FilePath CMD.EXE
)再次启动提升的进程,则不会出现 UAC 弹出窗口,并且该进程已启动但未提升,而不是-管理员。
RunAs
动词来触发 UAC 并成功提升?
完整的复制代码(以管理员身份运行!)
using Microsoft.Win32.SafeHandles;
using System;
using System.ComponentModel;
using System.Globalization;
using System.Runtime.InteropServices;
namespace SaferRepro
class Program
static void Main(string[] args)
string appToRun = "CMD.EXE";
string arguments = String.Empty;
bool newWindow = true, hidden = false;
var startupFolder = Environment.CurrentDirectory;
int mediumIntegrity = 8192;
using (var newToken = GetTokenFromSaferApi())
AdjustedTokenIntegrity(newToken, mediumIntegrity); // optional, launch as medium integrity process.
var process = StartWithToken(newToken, appToRun, arguments, startupFolder, newWindow, hidden);
GetProcessWaitHandle(process.DangerousGetHandle()).WaitOne();
private static SafeTokenHandle GetTokenFromSaferApi()
IntPtr hSaferLevel;
SafeTokenHandle hToken;
SaferLevels level = SaferLevels.NormalUser;
if (!NativeMethods.SaferCreateLevel(SaferScopes.User, level, 1, out hSaferLevel, IntPtr.Zero))
throw new Win32Exception();
if (!NativeMethods.SaferComputeTokenFromLevel(hSaferLevel, IntPtr.Zero, out hToken, SaferComputeTokenFlags.None, IntPtr.Zero))
throw new Win32Exception();
if (!NativeMethods.SaferCloseLevel(hSaferLevel))
throw new Win32Exception();
return hToken;
private static SafeProcessHandle StartWithToken(SafeTokenHandle newToken, string appToRun, string args, string startupFolder, bool newWindow, bool hidden)
var si = new STARTUPINFO();
if (newWindow)
si.dwFlags = 0x00000001; // STARTF_USESHOWWINDOW
si.wShowWindow = (short)(hidden ? 0 : 1);
si.cb = Marshal.SizeOf(si);
var pi = new PROCESS_INFORMATION();
uint dwCreationFlags = newWindow ? (uint)0x00000010 /*CREATE_NEW_CONSOLE*/: 0;
if (!NativeMethods.CreateProcessAsUser(newToken, null, $"appToRun args",
IntPtr.Zero, IntPtr.Zero, false, dwCreationFlags, IntPtr.Zero, startupFolder, ref si,
out pi))
throw new Win32Exception();
NativeMethods.CloseHandle(pi.hThread);
return new SafeProcessHandle(pi.hProcess, true);
private static bool AdjustedTokenIntegrity(SafeTokenHandle newToken, int integrityLevel)
string integritySid = "S-1-16-" + (integrityLevel.ToString(CultureInfo.InvariantCulture));
IntPtr pIntegritySid;
if (!NativeMethods.ConvertStringSidToSid(integritySid, out pIntegritySid))
return false;
TOKEN_MANDATORY_LABEL TIL = new TOKEN_MANDATORY_LABEL();
TIL.Label.Attributes = 0x00000020 /* SE_GROUP_INTEGRITY */;
TIL.Label.Sid = pIntegritySid;
var pTIL = Marshal.AllocHGlobal(Marshal.SizeOf<TOKEN_MANDATORY_LABEL>());
Marshal.StructureToPtr(TIL, pTIL, false);
if (!NativeMethods.SetTokenInformation(newToken.DangerousGetHandle(),
TOKEN_INFORMATION_CLASS.TokenIntegrityLevel,
pTIL,
(uint)(Marshal.SizeOf<TOKEN_MANDATORY_LABEL>() + NativeMethods.GetLengthSid(pIntegritySid))))
return false;
return true;
public static System.Threading.AutoResetEvent GetProcessWaitHandle(IntPtr processHandle) =>
new System.Threading.AutoResetEvent(false)
SafeWaitHandle = new SafeWaitHandle(processHandle, ownsHandle: false)
;
internal class SafeTokenHandle : SafeHandleZeroOrMinusOneIsInvalid
internal SafeTokenHandle(IntPtr handle) : base(true)
base.SetHandle(handle);
private SafeTokenHandle() : base(true)
protected override bool ReleaseHandle()
return NativeMethods.CloseHandle(base.handle);
static class NativeMethods
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool CloseHandle(IntPtr hObject);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CreateProcessAsUser(
SafeTokenHandle hToken,
string applicationName,
string commandLine,
IntPtr pProcessAttributes,
IntPtr pThreadAttributes,
bool bInheritHandles,
uint dwCreationFlags,
IntPtr pEnvironment,
string currentDirectory,
ref STARTUPINFO startupInfo,
out PROCESS_INFORMATION processInformation);
#region Safer
[DllImport("advapi32", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool SaferCreateLevel(
SaferScopes dwScopeId,
SaferLevels dwLevelId,
int OpenFlags,
out IntPtr pLevelHandle,
IntPtr lpReserved);
[DllImport("advapi32", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool SaferCloseLevel(
IntPtr pLevelHandle);
[DllImport("advapi32", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool SaferComputeTokenFromLevel(
IntPtr levelHandle,
IntPtr inAccessToken,
out SafeTokenHandle outAccessToken,
SaferComputeTokenFlags dwFlags,
IntPtr lpReserved
);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool ConvertStringSidToSid(
string StringSid,
out IntPtr ptrSid
);
[DllImport("advapi32.dll")]
public static extern int GetLengthSid(IntPtr pSid);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern Boolean SetTokenInformation(IntPtr TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass,
IntPtr TokenInformation, UInt32 TokenInformationLength);
#endregion
[Flags]
public enum SaferLevels : uint
Disallowed = 0,
Untrusted = 0x1000,
Constrained = 0x10000,
NormalUser = 0x20000,
FullyTrusted = 0x40000
[Flags]
public enum SaferComputeTokenFlags : uint
None = 0x0,
NullIfEqual = 0x1,
CompareOnly = 0x2,
MakeIntert = 0x4,
WantFlags = 0x8
[Flags]
public enum SaferScopes : uint
Machine = 1,
User = 2
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal struct STARTUPINFO
public Int32 cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public Int32 dwX;
public Int32 dwY;
public Int32 dwXSize;
public Int32 dwYSize;
public Int32 dwXCountChars;
public Int32 dwYCountChars;
public Int32 dwFillAttribute;
public Int32 dwFlags;
public Int16 wShowWindow;
public Int16 cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
[StructLayout(LayoutKind.Sequential)]
internal struct PROCESS_INFORMATION
public IntPtr hProcess;
public IntPtr hThread;
public int dwProcessId;
public int dwThreadId;
[StructLayout(LayoutKind.Sequential)]
public struct TOKEN_MANDATORY_LABEL
public SID_AND_ATTRIBUTES Label;
[StructLayout(LayoutKind.Sequential)]
public struct SID_AND_ATTRIBUTES
public IntPtr Sid;
public uint Attributes;
// Integrity Levels
public enum TOKEN_INFORMATION_CLASS
TokenUser = 1, TokenGroups, TokenPrivileges, TokenOwner, TokenPrimaryGroup, TokenDefaultDacl, TokenSource, TokenType, TokenImpersonationLevel, TokenStatistics, TokenRestrictedSids, TokenSessionId, TokenGroupsAndPrivileges, TokenSessionReference, TokenSandBoxInert, TokenAuditPolicy, TokenOrigin, TokenElevationType, TokenLinkedToken, TokenElevation, TokenHasRestrictions, TokenAccessInformation, TokenVirtualizationAllowed, TokenVirtualizationEnabled, TokenIntegrityLevel, TokenUIAccess, TokenMandatoryPolicy, TokenLogonSid, MaxTokenInfoClass
【问题讨论】:
blog.nextxpert.com/2012/06/09/… 有趣的@HansPassant。WhoAmI
不可信。使用 Process Explorer
时也只有拒绝。
相关comment 现在想知道如何在提升的进程上获取SeTcbPrivilege
。想知道模拟系统是否可行。
见How can I launch an unelevated process from my elevated process and vice versa?和How can I launch an unelevated process from my elevated process, redux
@GerardoGrignoli 这个问题解决了吗?
【参考方案1】:
当您的 a 令牌被提升时,似乎无法更改它以取消标记它的 elevated
标记状态。您可以从本地管理员中删除成员资格(实际上将其设置为仅拒绝),或将完整性设置为中,但它仍将被标记为提升(即使没有管理员权限)。设置此标志后,RunAs 动词将不会提升。
您的选择是:
获取explorer.exe
令牌并假设它没有被提升。 (如果 UAC 被禁用或用户 .
如果启用了 UAC,则提升的令牌具有指向未提升令牌的链接,您可以使用 GetTokenInformation
和 TOKEN_INFORMATION_CLASS.TokenLinkedToken
获取该链接。
如果禁用 UAC,则没有链接令牌。我的问题并不真正适用,因为 UAC 弹出窗口被禁用,永远不会显示。仅供参考,您可以使用 SaferApi 创建设置了提升标志的非管理员令牌,但它的 RunAs 尝试将失败。
如果您想了解我是如何编码的,请查看我正在处理的 gsudo
(Windows 的 sudo):
https://github.com/gerardog/gsudo/blob/dev/src/gsudo/Tokens/TokenManager.cs https://github.com/gerardog/gsudo/blob/dev/src/gsudo/Helpers/ProcessFactory.cs#L182
或查看其他great answer
【讨论】:
以上是关于从使用更安全令牌创建的进程调用时,RunAs Verb 不显示 UAC 弹出窗口且不提升的主要内容,如果未能解决你的问题,请参考以下文章
使用 Active Directory 令牌从 Angular 到 Web API 应用程序进行安全 API 调用