简体   繁体   English

当从使用更安全令牌创建的进程调用时,RunAs Verb 不显示 UAC 弹出窗口且不提升

[英]RunAs Verb not showing a UAC popup and not elevating, when invoked from a process created with a Safer token

I want to start a non-elevated process from an elevated one.我想从一个提升的过程开始一个非提升的过程。 I've accomplished this using SaferComputeTokenFromLevel.我已经使用 SaferComputeTokenFromLevel 完成了这项工作。 It appears to be working,since:它似乎正在工作,因为:

C:\>whoami /groups | findstr BUILTIN\Administrators
BUILTIN\Administrators     Alias    S-1-5-32-544    Group used for deny only

Deny only is there.拒绝只是在那里。 So I am not admin.所以我不是管理员。

  • 1) Why the (supodsedly) non-admin process Console Host Window shows as "Administrator:" in the title? 1)为什么(supodsedly)非管理员进程控制台主机窗口在标题中显示为“管理员:”?

I even used SetTokenInformation to set it to medium integrity.我什至使用SetTokenInformation将其设置为中等完整性。 It works but still showing as 'Administrator' even if it's not admin.它可以工作但仍然显示为“管理员”,即使它不是管理员。

Then, if that non-admin process wants to start an elevated process again via ShellExecute with Verb=RunAs (with powershell -C Start-Process -Verb RunAs -FilePath CMD.EXE ) the UAC popup doesn't appears, and the process is started but not elevated, not-admin.然后,如果该非管理员进程想要通过ShellExecute使用Verb=RunAs (使用powershell -C Start-Process -Verb RunAs -FilePath CMD.EXE )再次启动提升的进程,则不会出现 UAC 弹出窗口,并且该进程是开始但没有提升,不是管理员。

  • 2) Why does this happens? 2)为什么会发生这种情况? (linked token maybe?) (可能是链接令牌?)
  • 3) Finally, how can I launch a non-elevated process, that can use the RunAs verb to trigger an UAC and elevate succesfully? 3)最后,我如何启动一个非提升的进程,它可以使用RunAs动词来触发 UAC 并成功提升?

Full Repro code (Run as admin!)完整的 Repro 代码(以管理员身份运行!)

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
    }
}

When your a token is elevated, it seems it can't be altered to unflag it's elevated flag status it.当您的令牌被提升时,似乎无法更改它以取消标记它的elevated标志状态。 You can remove membership from Local Admins (actually set it as Deny Only), or set Integrity to Medium, but it will still be flagged as elevated (even without admin privileges).您可以从本地管理员中删除成员资格(实际上将其设置为仅拒绝),或将完整性设置为中,但它仍将被标记为提升(即使没有管理员权限)。 With this flag set, the RunAs Verb will not elevate.设置此标志后,RunAs Verb 不会提升。

Your options are:您的选择是:

  • Get explorer.exe token and assume it's not elevated.获取explorer.exe令牌并假设它没有提升。 (its elevated if UAC is disabled or if the user . (如果 UAC 被禁用或用户 .

  • If UAC is enabled, the elevated token has a link to the unelevated token that you can fetch with GetTokenInformation with TOKEN_INFORMATION_CLASS.TokenLinkedToken .如果启用了 UAC,提升的令牌有一个指向未提升的令牌的链接,您可以使用GetTokenInformationTOKEN_INFORMATION_CLASS.TokenLinkedToken获取该链接。

  • If UAC is disabled there is no linked token.如果 UAC 被禁用,则没有链接令牌。 My question doesn't really apply, since the UAC popup is disabled, will never show.我的问题并不真正适用,因为 UAC 弹出窗口被禁用,永远不会显示。 FYI you can use SaferApi to create a non-admin token with the elevated flag set, but it's RunAs attempts will fail.仅供参考,您可以使用 SaferApi 创建一个设置了提升标志的非管理员令牌,但它的 RunAs 尝试将失败。

If you want to see how I coded this, check gsudo (a sudo for windows) that I am working on:如果你想看看我是如何编码的,请检查我正在处理的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 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

or check this other great answer或查看其他很棒的答案

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM