문제

나는 특정 사용자의 맥락에서 CreateProcessAsUser Windows API의 기능이지만 다소 불쾌한 보안 문제에 실행되는 것 같습니다 ...

더 이상 설명하기 전에, 여기에 새로운 프로세스를 시작하는 데 현재 사용하고있는 코드입니다 (콘솔 프로세스 - 파워 쉘은 구체적이지 않지만 중요하지 않음).

    private void StartProcess()
    {
        bool retValue;

        // Create startup info for new console process.
        var startupInfo = new STARTUPINFO();
        startupInfo.cb = Marshal.SizeOf(startupInfo);
        startupInfo.dwFlags = StartFlags.STARTF_USESHOWWINDOW;
        startupInfo.wShowWindow = _consoleVisible ? WindowShowStyle.Show : WindowShowStyle.Hide;
        startupInfo.lpTitle = this.ConsoleTitle ?? "Console";

        var procAttrs = new SECURITY_ATTRIBUTES();
        var threadAttrs = new SECURITY_ATTRIBUTES();
        procAttrs.nLength = Marshal.SizeOf(procAttrs);
        threadAttrs.nLength = Marshal.SizeOf(threadAttrs);

        // Log on user temporarily in order to start console process in its security context.
        var hUserToken = IntPtr.Zero;
        var hUserTokenDuplicate = IntPtr.Zero;
        var pEnvironmentBlock = IntPtr.Zero;
        var pNewEnvironmentBlock = IntPtr.Zero;

        if (!WinApi.LogonUser("UserName", null, "Password",
            LogonType.Interactive, LogonProvider.Default, out hUserToken))
            throw new Win32Exception(Marshal.GetLastWin32Error(), "Error logging on user.");

        var duplicateTokenAttrs = new SECURITY_ATTRIBUTES();
        duplicateTokenAttrs.nLength = Marshal.SizeOf(duplicateTokenAttrs);
        if (!WinApi.DuplicateTokenEx(hUserToken, 0, ref duplicateTokenAttrs,
            SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, TOKEN_TYPE.TokenPrimary,
            out hUserTokenDuplicate))
            throw new Win32Exception(Marshal.GetLastWin32Error(), "Error duplicating user token.");

        try
        {
            // Get block of environment vars for logged on user.
            if (!WinApi.CreateEnvironmentBlock(out pEnvironmentBlock, hUserToken, false))
                throw new Win32Exception(Marshal.GetLastWin32Error(),
                    "Error getting block of environment variables for user.");

            // Read block as array of strings, one per variable.
            var envVars = ReadEnvironmentVariables(pEnvironmentBlock);

            // Append custom environment variables to list.
            foreach (var var in this.EnvironmentVariables)
                envVars.Add(var.Key + "=" + var.Value);

            // Recreate environment block from array of variables.
            var newEnvironmentBlock = string.Join("\0", envVars.ToArray()) + "\0";
            pNewEnvironmentBlock = Marshal.StringToHGlobalUni(newEnvironmentBlock);

            // Start new console process.
            retValue = WinApi.CreateProcessAsUser(hUserTokenDuplicate, null, this.CommandLine,
                ref procAttrs, ref threadAttrs, false, CreationFlags.CREATE_NEW_CONSOLE |
                CreationFlags.CREATE_SUSPENDED | CreationFlags.CREATE_UNICODE_ENVIRONMENT,
                pNewEnvironmentBlock, null, ref startupInfo, out _processInfo);
            if (!retValue) throw new Win32Exception(Marshal.GetLastWin32Error(),
                "Unable to create new console process.");
        }
        catch
        {
            // Catch any exception thrown here so as to prevent any malicious program operating
            // within the security context of the logged in user.

            // Clean up.
            if (hUserToken != IntPtr.Zero)
            {
                WinApi.CloseHandle(hUserToken);
                hUserToken = IntPtr.Zero;
            }

            if (hUserTokenDuplicate != IntPtr.Zero)
            {
                WinApi.CloseHandle(hUserTokenDuplicate);
                hUserTokenDuplicate = IntPtr.Zero;
            }

            if (pEnvironmentBlock != IntPtr.Zero)
            {
                WinApi.DestroyEnvironmentBlock(pEnvironmentBlock);
                pEnvironmentBlock = IntPtr.Zero;
            }

            if (pNewEnvironmentBlock != IntPtr.Zero)
            {
                Marshal.FreeHGlobal(pNewEnvironmentBlock);
                pNewEnvironmentBlock = IntPtr.Zero;
            }

            throw;
        }
        finally
        {
            // Clean up.
            if (hUserToken != IntPtr.Zero)
                WinApi.CloseHandle(hUserToken);

            if (hUserTokenDuplicate != IntPtr.Zero)
                WinApi.CloseHandle(hUserTokenDuplicate);

            if (pEnvironmentBlock != IntPtr.Zero)
                WinApi.DestroyEnvironmentBlock(pEnvironmentBlock);

            if (pNewEnvironmentBlock != IntPtr.Zero)
                Marshal.FreeHGlobal(pNewEnvironmentBlock);
        }

        _process = Process.GetProcessById(_processInfo.dwProcessId);
    }

여기에서 문제를 해결하기 위해 환경 변수를 다루는 코드를 무시합니다 (해당 섹션을 독립적으로 테스트했으며 작동하는 것 같습니다.)

이제 내가 얻는 오류는 다음과 같습니다 (전화 후 줄에 던져졌습니다. CreateProcessAsUSer):

"필수 특권은 클라이언트가 보유하지 않습니다"(오류 코드 1314)

(오류 메시지는 Win32Exception 생성자에서 메시지 매개 변수를 제거하여 발견되었습니다. 여기서 내 오류 처리 코드가 가장 좋지는 않지만 다소 관련이없는 문제입니다. 그러나 원하는 경우 의견을 환영합니다. )이 상황 에서이 모호한 오류의 원인에 대해 정말 혼란스러워합니다. MSDN 문서와 다양한 포럼 스레드는 나에게 많은 조언을 주었고, 특히 그러한 오류의 원인이 널리 변하는 것처럼 보이면 어떤 코드 섹션을 수정 해야하는지 모르겠습니다. 아마도 그것은 단순히 변경해야 할 단일 매개 변수 일지 모르지만, 내가 아는 모든 것을 위해 Winapi가 충분하지 않은 것을 만들 수 있습니다. 저를 크게 혼란스럽게하는 것은 평원을 사용하는 이전 버전의 코드입니다. CreateProcess 기능 (사용자 토큰 매개 변수를 제외한 동등한)은 완벽하게 잘 작동했습니다. 내가 이해했듯이 로그온 사용자 기능을 호출하여 적절한 토큰 핸들을 수신 한 다음 통과 할 수 있도록 복제하면됩니다. CreateProcessAsUser.

코드 수정에 대한 제안과 설명은 매우 환영합니다.

메모

나는 주로 MSDN 문서를 언급하고 있습니다 (뿐만 아니라 pinvoke.net c# 함수/strut/enum 선언). 특히 다음 페이지에는 비고 섹션에 많은 정보가있는 것으로 보이며, 그 중 일부는 중요하고 저를 피할 수 있습니다.

편집하다

방금 Mitch의 제안을 시도했지만 불행히도 오래된 오류는 새로운 오류로 대체되었습니다. "시스템은 지정된 파일을 찾을 수 없습니다." (오류 코드 2)

이전 호출 CreateProcessAsUser 다음으로 대체되었습니다.

retValue = WinApi.CreateProcessWithTokenW(hUserToken, LogonFlags.WithProfile, null,
    this.CommandLine, CreationFlags.CREATE_NEW_CONSOLE |
    CreationFlags.CREATE_SUSPENDED | CreationFlags.CREATE_UNICODE_ENVIRONMENT,
    pNewEnvironmentBlock, null, ref startupInfo, out _processInfo);

이 코드는 더 이상 중복 토큰이 아니라 MSDN 문서가 제안한 것처럼 원본을 사용합니다.

그리고 여기에 또 다른 시도가 있습니다 CreateProcessWithLogonW. 이번에 오류는 "로그온 실패 : 알 수없는 사용자 이름 또는 나쁜 비밀번호"입니다 (오류 코드 1326)

retValue = WinApi.CreateProcessWithLogonW("Alex", null, "password",
    LogonFlags.WithProfile, null, this.CommandLine,
    CreationFlags.CREATE_NEW_CONSOLE | CreationFlags.CREATE_SUSPENDED |
    CreationFlags.CREATE_UNICODE_ENVIRONMENT, pNewEnvironmentBlock,
    null, ref startupInfo, out _processInfo);

또한 UPN 형식 ( "Alex@Alex-PC")의 사용자 이름을 지정하고 도메인을 두 번째 인수로 독립적으로 전달하는 시도를 시도했습니다 (동일한 오류).

도움이 되었습니까?

해결책

Ahh ... Winapi Interop 프로그래밍에서 가장 큰 gotchas 중 하나에 갇힌 것처럼 보인다. 또한 내 기능 선언에 대한 코드를 게시하는 것은이 경우 현명한 아이디어 일 것입니다.

어쨌든, 내가해야 할 일은 지정하는 함수의 dllimport 속성에 인수를 추가하는 것입니다. CharSet = CharSet.Unicode. 이것은 두 가지 모두에 대한 트릭을 수행했습니다 CreateProcessWithLogonW 그리고 CreateProcessWithTokenW 기능. 마침내 함수 이름의 w 접미어가 유니 코드에 언급되었으며 C#에 이것을 명시 적으로 지정해야한다고 생각합니다! 여기에 있습니다 옳은 누구나 관심이있는 경우 기능 선언 :

[DllImport("advapi32", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool CreateProcessWithLogonW(string principal, string authority,
    string password, LogonFlags logonFlags, string appName, string cmdLine,
    CreationFlags creationFlags, IntPtr environmentBlock, string currentDirectory,
    ref STARTUPINFO startupInfo, out PROCESS_INFORMATION processInfo);

[DllImport("advapi32", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool CreateProcessWithTokenW(IntPtr hToken, LogonFlags dwLogonFlags,
    string lpApplicationName, string lpCommandLine, CreationFlags dwCreationFlags,
    IntPtr lpEnvironment, string lpCurrentDirectory, [In] ref STARTUPINFO lpStartupInfo,
    out PROCESS_INFORMATION lpProcessInformation);

다른 팁

에서 여기:

일반적으로 CreateProcessAsuser 기능을 호출하는 프로세스에는 SE_ASSIGNPRIMARYTOKEN_NAME 및 SE_INCREASE_QUOTA_NAME 권한이 있어야합니다. 이 함수가 error_privilege_not_held (1314)로 실패하면 대신 CreateProcesswithLogonw 함수를 사용하십시오. CreateProcesswithLogonw에는 특별한 권한이 필요하지 않지만 지정된 사용자 계정은 대화식으로 로그온해야합니다. 일반적으로 CreateProcesswithLogonw를 사용하여 대체 자격 증명으로 프로세스를 작성하는 것이 가장 좋습니다.

이 블로그 게시물을 참조하십시오 .NET에서 CreateProcesswithLogonw 및 CreateProcessAsuser를 호출하는 방법

Jonathan Peppers는 내 문제를 해결하는이 위대한 코드를 제공했습니다.

http://social.msdn.microsoft.com/forums/en-us/csharpgeneral/thread/0c0c087-5e7b-4046-93cb-c7b3e48dfb?ppud=4

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top