ms_windows_ms_sql:web_gui_for_powershell_scripts

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
ms_windows_ms_sql:web_gui_for_powershell_scripts [2019/05/15 09:49]
admin [Еще немного полезных ссылок]
ms_windows_ms_sql:web_gui_for_powershell_scripts [2019/05/15 14:03] (current)
admin [Запуск процесса (powershell.exe) от имени конкретного пользователя]
Line 92: Line 92:
 \\ \\
 Вот тут написано как из приложения ASP.NET запустить процесс от имени импесонированного пользователя,​ указав имя и пароль (**Impersonate a Specific User in Code**): https://​support.microsoft.com/​en-us/​help/​306158/​how-to-implement-impersonation-in-an-asp-net-application Вот тут написано как из приложения ASP.NET запустить процесс от имени импесонированного пользователя,​ указав имя и пароль (**Impersonate a Specific User in Code**): https://​support.microsoft.com/​en-us/​help/​306158/​how-to-implement-impersonation-in-an-asp-net-application
 +\\
 +Вот тут понятные классы,​ которые помогают запустить процесс от имени конкретного пользователя:​ https://​habr.com/​ru/​post/​135299/​ \\
  
 Вот ссылки описывают как создать проект,​ однако в них не работает имперсонализация пользователя. ​ Вот ссылки описывают как создать проект,​ однако в них не работает имперсонализация пользователя. ​
Line 215: Line 217:
 </​form>​ </​form>​
 </​code>​ </​code>​
 +====== Запуск процессов от имени имперсонированного пользователя ======
 +<​code>​using System;
 +using System.Runtime.InteropServices;​
 +using System.Security.Principal;​
 +using System.Text;​
 +using System.Diagnostics;​
 +using System.IO;
 +using System.ComponentModel;​
 +using System.Reflection;​
 +using Microsoft.Win32.SafeHandles;​
  
 +namespace vmware_net
 +{
 +
 +
 +    /// <​summary>​
 +    /// UserSpecificProcess extends the standard Process object to 
 +    /// create new processes under a different user than the calling parent process.
 +    /// Also, the standard output of the child process redirected back to the parent process.
 +    /// This is class is designed to operate inside an ASP.NET web application.
 +    /// The assumption is that the calling thread is operating with an impersonated security token.
 +    /// The this class will change the imperonated security token to a primary token, ​
 +    /// and call CreateProcessAsUser.
 +    /// A System.Diagnostics.Process object will be returned with appropriate properties set.
 +    /// To use this function, the following security priviliges need to be set for the ASPNET account ​
 +    /// using the local security policy MMC snap-in. CreateProcessAsUser requirement.
 +    /// "​Replace a process level token"/​SE_ASSIGNPRIMARYTOKEN_NAME/​SeAssignPrimaryTokenPrivilege
 +    /// "​Adjust memory quotas for a process"/​SE_INCREASE_QUOTA_NAME/​SeIncreaseQuotaPrivilege
 +    ///
 +    /// This class was designed for .NET 1.1 for operating systems W2k an higher.
 +    /// Any other features or platform support can be implemented by using the .NET reflector and 
 +    /// investigating the Process class.
 +    /// </​summary>​
 +    public class UserSpecificProcess : Process
 +    {
 +        [StructLayout(LayoutKind.Sequential)]
 +        public class CreateProcessStartupInfo
 +        {
 +            public int cb;
 +            public string lpReserved;
 +            public string lpDesktop;
 +            public string lpTitle;
 +            public int dwX;
 +            public int dwY;
 +            public int dwXSize;
 +            public int dwYSize;
 +            public int dwXCountChars;​
 +            public int dwYCountChars;​
 +            public int dwFillAttribute;​
 +            public int dwFlags;
 +            public short wShowWindow;​
 +            public short cbReserved2;​
 +            public IntPtr lpReserved2;​
 +            public IntPtr hStdInput;
 +            public IntPtr hStdOutput;
 +            public IntPtr hStdError;
 +            public CreateProcessStartupInfo()
 +            {
 +                this.cb = Marshal.SizeOf(typeof(CreateProcessStartupInfo));​
 +                this.lpReserved = null;
 +                this.lpDesktop = null;
 +                this.lpTitle = null;
 +                this.dwX = 0;
 +                this.dwY = 0;
 +                this.dwXSize = 0;
 +                this.dwYSize = 0;
 +                this.dwXCountChars = 0;
 +                this.dwYCountChars = 0;
 +                this.dwFillAttribute = 0;
 +                this.dwFlags = 0;
 +                this.wShowWindow = 0;
 +                this.cbReserved2 = 0;
 +                this.lpReserved2 = IntPtr.Zero;​
 +                this.hStdInput = IntPtr.Zero;​
 +                this.hStdOutput = IntPtr.Zero;​
 +                this.hStdError = IntPtr.Zero;​
 +            }
 +        }
 +
 +        [StructLayout(LayoutKind.Sequential)]
 +        public class CreateProcessProcessInformation
 +        {
 +            public IntPtr hProcess;
 +            public IntPtr hThread;
 +            public int dwProcessId;​
 +            public int dwThreadId;
 +            public CreateProcessProcessInformation()
 +            {
 +                this.hProcess = IntPtr.Zero;​
 +                this.hThread = IntPtr.Zero;​
 +                this.dwProcessId = 0;
 +                this.dwThreadId = 0;
 +            }
 +        }
 +
 +        [StructLayout(LayoutKind.Sequential)]
 +        public class SecurityAttributes
 +        {
 +            public int nLength;
 +            public IntPtr lpSecurityDescriptor;​
 +            public bool bInheritHandle;​
 +            public SecurityAttributes()
 +            {
 +                this.nLength = Marshal.SizeOf(typeof(SecurityAttributes));​
 +            }
 +        }
 +
 +        [DllImport("​kernel32.dll",​ CharSet = CharSet.Auto,​ SetLastError = true, ExactSpelling = true)]
 +        public static extern bool CloseHandle(HandleRef handle);
 +
 +        [DllImport("​kernel32.dll",​ CharSet = CharSet.Auto,​ SetLastError = true)]
 +        public static extern bool
 +        CreateProcess([MarshalAs(UnmanagedType.LPTStr)] string lpApplicationName,​ StringBuilder lpCommandLine,​ SecurityAttributes lpProcessAttributes,​ SecurityAttributes lpThreadAttributes,​ bool bInheritHandles,​ int dwCreationFlags,​ IntPtr lpEnvironment,​ [MarshalAs(UnmanagedType.LPTStr)] string lpCurrentDirectory,​ CreateProcessStartupInfo lpStartupInfo,​ CreateProcessProcessInformation lpProcessInformation);​
 +
 +        [DllImport("​advapi32.dll"​)]
 +        public static extern int LogonUserA(String lpszUserName,​ string lpszDomain, string lpszPassword,​ int dwLogonType,​ int dwLogonProvider,​ ref IntPtr phToken);
 +
 +        [DllImport("​advapi32.dll",​ CharSet = CharSet.Unicode,​ SetLastError = true)]
 +        public static extern bool CreateProcessAsUserW(IntPtr token, [MarshalAs(UnmanagedType.LPTStr)] string lpApplicationName,​ [MarshalAs(UnmanagedType.LPTStr)] string lpCommandLine,​ SecurityAttributes lpProcessAttributes,​ SecurityAttributes lpThreadAttributes,​ bool bInheritHandles,​ int dwCreationFlags,​ IntPtr lpEnvironment,​ [MarshalAs(UnmanagedType.LPTStr)] string lpCurrentDirectory,​ CreateProcessStartupInfo lpStartupInfo,​ CreateProcessProcessInformation lpProcessInformation);​
 +
 +        [DllImport("​advapi32.dll",​ CharSet = CharSet.Unicode,​ SetLastError = true)]
 +        public static extern bool CreateProcessAsUser(IntPtr token, [MarshalAs(UnmanagedType.LPTStr)] string lpApplicationName,​ [MarshalAs(UnmanagedType.LPTStr)] string lpCommandLine,​ SecurityAttributes lpProcessAttributes,​ SecurityAttributes lpThreadAttributes,​ bool bInheritHandles,​ int dwCreationFlags,​ IntPtr lpEnvironment,​ [MarshalAs(UnmanagedType.LPTStr)] string lpCurrentDirectory,​ CreateProcessStartupInfo lpStartupInfo,​ CreateProcessProcessInformation lpProcessInformation);​
 +
 +        [DllImport("​kernel32.dll",​ CharSet = CharSet.Ansi,​ SetLastError = true)]
 +        public static extern IntPtr GetStdHandle(int whichHandle);​
 +
 +        [DllImport("​kernel32.dll",​ CharSet = CharSet.Auto)]
 +        public static extern IntPtr CreateFile(string lpFileName, int dwDesiredAccess,​ int dwShareMode,​ SecurityAttributes lpSecurityAttributes,​ int dwCreationDisposition,​ int dwFlagsAndAttributes,​ HandleRef hTemplateFile);​
 +
 +        [DllImport("​kernel32.dll",​ CharSet = CharSet.Auto,​ SetLastError = true)]
 +        public static extern IntPtr CreateNamedPipe(string name, int openMode, int pipeMode, int maxInstances,​ int outBufSize, int inBufSize, int timeout, SecurityAttributes lpPipeAttributes);​
 +
 +        [DllImport("​kernel32.dll",​ CharSet = CharSet.Auto,​ SetLastError = true)]
 +        public static extern int GetConsoleOutputCP();​
 +
 +        [DllImport("​advapi32.dll",​ CharSet = CharSet.Auto,​ SetLastError = true)]
 +        public static extern bool DuplicateTokenEx(HandleRef hToken, int access, SecurityAttributes tokenAttributes,​ int impersonationLevel,​ int tokenType, ref IntPtr hNewToken);
 +
 +        // WinNT.h ACCESS TYPES
 +        const int GENERIC_ALL = 0x10000000;
 +
 +        // WinNT.h enum SECURITY_IMPERSONATION_LEVEL
 +        const int SECURITY_IMPERSONATION = 2;
 +
 +        // WinNT.h TOKEN TYPE
 +        const int TOKEN_PRIMARY = 1;
 +
 +        // WinBase.h
 +        const int STD_INPUT_HANDLE = -10;
 +        const int STD_ERROR_HANDLE = -12;
 +
 +        // WinBase.h STARTUPINFO
 +        const int STARTF_USESTDHANDLES = 0x100;
 +
 +        // Microsoft.Win23.NativeMethods
 +        static IntPtr INVALID_HANDLE_VALUE = (IntPtr)(-1);​
 +        public static HandleRef NullHandleRef = new HandleRef(null,​ IntPtr.Zero);​
 +
 +        public const int LOGON32_LOGON_INTERACTIVE = 2;
 +        public const int LOGON32_PROVIDER_DEFAULT = 0;
 +
 +        /// <​summary>​
 +        /// Starts the process with the security token of the calling thread.
 +        /// If the security token has a token type of TokenImpersonation,​
 +        /// the token will be duplicated to a primary token before calling
 +        /// CreateProcessAsUser.
 +        /// </​summary>​
 +        /// <param name="​process">​The process to start.</​param>​
 +        public void StartAsUser()
 +        {
 +            StartAsUser(WindowsIdentity.GetCurrent().Token);​
 +        }
 +
 +        /// <​summary>​
 +        /// Starts the process with the given security token.
 +        /// If the security token has a token type of TokenImpersonation,​
 +        /// the token will be duplicated to a primary token before calling
 +        /// CreateProcessAsUser.
 +        /// </​summary>​
 +        /// <param name="​process"></​param>​
 +        public void StartAsUser(IntPtr userToken)
 +        {
 +            if (StartInfo.UseShellExecute)
 +            {
 +                throw new InvalidOperationException("​can'​t call this with shell execute"​);​
 +            }
 +
 +            // just assume that the securityToken is of TokenImpersonation and create a primary.
 +            IntPtr primayUserToken = CreatePrimaryToken(userToken);​
 +
 +            CreateProcessStartupInfo startupInfo = new CreateProcessStartupInfo();​
 +            CreateProcessProcessInformation processInformation = new CreateProcessProcessInformation();​
 +
 +            IntPtr stdinHandle;​
 +            IntPtr stdoutReadHandle;​
 +            IntPtr stdoutWriteHandle = IntPtr.Zero;​
 +            IntPtr stderrHandle;​
 +            try
 +            {
 +                stdinHandle = GetStdHandle(STD_INPUT_HANDLE);​
 +                MyCreatePipe(out stdoutReadHandle,​ out stdoutWriteHandle,​ false);
 +                stderrHandle = GetStdHandle(STD_ERROR_HANDLE);​
 +
 +                //assign handles to startup info
 +                startupInfo.dwFlags = STARTF_USESTDHANDLES;​
 +                startupInfo.hStdInput = stdinHandle;​
 +                startupInfo.hStdOutput = stdoutWriteHandle;​
 +                startupInfo.hStdError = stderrHandle;​
 +
 +                string commandLine = GetCommandLine();​
 +                //Set Creation Flags value 0x8000000 to hide window. To show window set it to 0
 +                int creationFlags = 0x8000000;
 +                //int creationFlags = 0;
 +                IntPtr environment = IntPtr.Zero;​
 +                string workingDirectory = GetWorkingDirectory();​
 +
 +                // create the process or fail trying.
 +                //if (!CreateProcessAsUserW(
 +                if (!CreateProcessAsUser(
 +                primayUserToken,​
 +                null,
 +                commandLine,​
 +                null,
 +                null,
 +                true,
 +                creationFlags,​
 +                environment,​
 +                workingDirectory,​
 +                startupInfo,​
 +                processInformation))
 +                {
 +                    throw new Win32Exception();​
 +                }
 +            }
 +            finally
 +            {
 +                // close thread handle
 +                if (processInformation.hThread != INVALID_HANDLE_VALUE)
 +                {
 +                    CloseHandle(new HandleRef(this,​ processInformation.hThread));​
 +                }
 +
 +                // close client stdout handle
 +                CloseHandle(new HandleRef(this,​ stdoutWriteHandle));​
 +            }
 +            // }
 +            // get reader for standard output from the child
 +            Encoding encoding = Encoding.GetEncoding(GetConsoleOutputCP());​
 +            //
 +            //​StreamReader standardOutput = new StreamReader(new FileStream(stdoutReadHandle,​ FileAccess.Read,​ true, 0x1000, true), encoding);
 +            SafeFileHandle SafeStdOutReadHandle = new SafeFileHandle(stdoutReadHandle,​ true);
 +            StreamReader standardOutput = new StreamReader(new FileStream(SafeStdOutReadHandle,​ FileAccess.Read,​ 0x1000, true), encoding);
 +
 +            // set this on the object accordingly.
 +            typeof(Process).InvokeMember("​standardOutput",​
 +            BindingFlags.SetField | BindingFlags.NonPublic | BindingFlags.Instance,​
 +            null, this, new object[] { standardOutput });
 +
 +            // scream if a process wasn't started instead of returning false.
 +            if (processInformation.hProcess == IntPtr.Zero)
 +            {
 +                throw new Exception("​failed to create process"​);​
 +            }
 +
 +            // configure the properties of the Process object correctly
 +            //​typeof(Process).InvokeMember("​SetProcessHandle",​
 +            //​BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance,​
 +            //null, this, new object[] { processInformation.hProcess });
 +            typeof(Process).InvokeMember("​SetProcessId",​
 +            BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance,​
 +            null, this, new object[] { processInformation.dwProcessId });
 +        }
 +
 +        /// <​summary>​
 +        /// Starts the process as user with provided username, domain name and password.
 +        /// Method loggs user with provided cretential, get token and pass it to StartAsUser method.
 +        /// </​summary>​
 +        public void StartAsUserWithLogon(string UserName, string Domain, string Password)
 +        {
 +            IntPtr UserToken = IntPtr.Zero;​
 +            if (LogonUserA(UserName,​ Domain, Password, LOGON32_LOGON_INTERACTIVE,​ LOGON32_PROVIDER_DEFAULT,​ ref UserToken) !=0)
 +            {
 +                StartAsUser(CreatePrimaryToken(UserToken));​
 +            }
 +            else
 +            {
 +                throw new Win32Exception();​
 +            }
 +        }
 +
 +        /// <​summary>​
 +        /// Creates a primayToken out of an existing token.
 +        /// </​summary>​
 +        /// <param name="​userToken"></​param>​
 +        private IntPtr CreatePrimaryToken(IntPtr userToken)
 +        {
 +            SecurityAttributes securityAttributes = new SecurityAttributes();​
 +            IntPtr primaryUserToken = IntPtr.Zero;​
 +            if (!DuplicateTokenEx(new HandleRef(this,​ userToken), GENERIC_ALL,​ securityAttributes,​ SECURITY_IMPERSONATION,​ TOKEN_PRIMARY,​ ref
 +            primaryUserToken))
 +            {
 +                throw new Win32Exception();​
 +            }
 +            return primaryUserToken;​
 +        }
 +
 +        /// <​summary>​
 +        /// Gets the appropriate commandLine from the process.
 +        /// </​summary>​
 +        /// <param name="​process"></​param>​
 +        /// <​returns></​returns>​
 +        private string GetCommandLine()
 +        {
 +            StringBuilder builder1 = new StringBuilder();​
 +            string text1 = StartInfo.FileName.Trim();​
 +            string text2 = StartInfo.Arguments;​
 +            bool flag1 = text1.StartsWith("​\""​) && text1.EndsWith("​\""​);​
 +            if (!flag1)
 +            {
 +                builder1.Append("​\""​);​
 +            }
 +            builder1.Append(text1);​
 +            if (!flag1)
 +            {
 +                builder1.Append("​\""​);​
 +            }
 +            if ((text2 != null) && (text2.Length > 0))
 +            {
 +                builder1.Append("​ ");
 +                builder1.Append(text2);​
 +            }
 +            return builder1.ToString();​
 +        }
 +
 +        /// <​summary>​
 +        /// Gets the working directory or returns null if an empty string was found.
 +        /// </​summary>​
 +        /// <​returns></​returns>​
 +        private string GetWorkingDirectory()
 +        {
 +            return (StartInfo.WorkingDirectory != string.Empty) ?
 +            StartInfo.WorkingDirectory : null;
 +        }
 +
 +        /// <​summary>​
 +        /// A clone of Process.CreatePipe. This is only implemented because reflection with
 +        /// out parameters are a pain.
 +        /// Note: This is only finished for w2k and higher machines.
 +        /// </​summary>​
 +        /// <param name="​parentHandle"></​param>​
 +        /// <param name="​childHandle"></​param>​
 +        /// <param name="​parentInputs">​Specifies whether the parent will be performing the writes.</​param>​
 +        public static void MyCreatePipe(out IntPtr parentHandle,​ out IntPtr childHandle,​ bool parentInputs)
 +        {
 +            string pipename = @"​\\.\pipe\Global\"​ + Guid.NewGuid().ToString();​
 +
 +            SecurityAttributes attributes2 = new SecurityAttributes();​
 +            attributes2.bInheritHandle = false;
 +
 +            parentHandle = CreateNamedPipe(pipename,​ 0x40000003, 0, 0xff, 0x1000, 0x1000, 0, attributes2);​
 +            if (parentHandle == INVALID_HANDLE_VALUE)
 +            {
 +                throw new Win32Exception();​
 +            }
 +
 +            SecurityAttributes attributes3 = new SecurityAttributes();​
 +            attributes3.bInheritHandle = true;
 +            int num1 = 0x40000000;
 +            if (parentInputs)
 +            {
 +                num1 = -2147483648;​
 +            }
 +            childHandle = CreateFile(pipename,​ num1, 3, attributes3,​ 3, 0x40000080, NullHandleRef);​
 +            if (childHandle == INVALID_HANDLE_VALUE)
 +            {
 +                throw new Win32Exception();​
 +            }
 +        }
 +    }
 +}
 +</​code>​
 +===== Примеры использования =====
 +==== Запуск процесса (powershell.exe) от имени залогиненного имперсонированного пользователя ====
 +Для доступа к ресурсам за пределами сервера нужна аутентификация kerberos (обязательно),​ а также включенная делегация **kerberos** для учетки,​ от имени которой исполняется **Application Pool**. То есть, например **AppPool Identity** - **LocalSystem**,​ а для учетки компьютера в AD включена делегация **kerberos**.\\
 +На уровне всего приложения **<​identity impersonate="​false"/>​**. От имперсонированного пользователя запускается только кусок кода в using {...}.
 +<​code>​
 +//Get User Identity for Initializing Impersonation Context
 +var current = System.Security.Principal.WindowsIdentity.GetCurrent();​
 +WindowsIdentity clientId = (WindowsIdentity)User.Identity;​
 +string ScriptFilePath = @"";​
 +string Arguments = @"";​
 +
 +using (WindowsImpersonationContext wic = clientId.Impersonate())
 +{
 +    UserSpecificProcess myProcess = new UserSpecificProcess();​
 +    myProcess.StartInfo.FileName = "​powershell.exe";​
 +    myProcess.StartInfo.Arguments = @"​-NoLogo -NoProfile -executionpolicy bypass -File " + ScriptFilePath + " " + Arguments;
 +
 +    //​ResultBuilder.Append(ScriptFilePath + " " + Arguments + "​\r\n"​);​
 +
 +    myProcess.StartInfo.UseShellExecute = false;
 +    myProcess.StartInfo.CreateNoWindow = false;
 +    myProcess.StartInfo.WorkingDirectory = ScriptWorkingDirectory;​
 +    myProcess.StartAsUser();​
 +    StreamReader output = myProcess.StandardOutput;​
 +
 +    //wait then kill process
 +    if (!myProcess.WaitForExit(ScriptExecutionTimeout))
 +    {
 +        myProcess.Kill();​
 +    }
 +
 +    string line;
 +    while ((line = output.ReadLine()) != null)
 +    {
 +        // We use a string builder ton create our result text
 +        ResultBuilder.Append(line + "​\r\n"​);​
 +    }
 +}
 +</​code>​
 +
 +==== Запуск процесса (powershell.exe) от имени конкретного пользователя ====
 +<​code>​
 +protected void RunScript()
 +{
 +    string ScriptFilePath = @"​c:​\temp\script.ps1";​
 +    string Arguments = @"";​
 +    string ScriptWorkingDirectory = @"​c:​\temp\";​
 +    string UserName = "​Username";​
 +    string Domain = "​DomainName_or_MachineName";​
 +    string Password = "​superpassword";​
 +
 +    UserSpecificProcess proc = new UserSpecificProcess();​
 +
 +    proc.StartInfo.UseShellExecute = false;
 +    proc.StartInfo.CreateNoWindow = false;
 +    proc.StartInfo.RedirectStandardError = true;
 +    proc.StartInfo.RedirectStandardOutput = true;
 +    proc.StartInfo.FileName = "​powershell.exe";​
 +    proc.StartInfo.WorkingDirectory = ScriptWorkingDirectory;​
 +    proc.StartInfo.Arguments = @"​-NoLogo -NoProfile -executionpolicy bypass -command " + '"'​ + ScriptFilePath + " " + Arguments + '"';​
 +
 +    proc.StartAsUserWithLogon(UserName,​ Domain, Password);
  
 +    while (!proc.HasExited && proc.Responding)
 +    {
 +        System.Threading.Thread.Sleep(1000);​
 +    }
 +
 +</​code> ​        
  • ms_windows_ms_sql/web_gui_for_powershell_scripts.txt
  • Last modified: 2019/05/15 14:03
  • by admin