12

Windows APIの関数を使用して、特定のユーザーのコンテキストで新しいプロセスを作成しようとしましたがCreateProcessAsUser、かなり厄介なセキュリティの問題が発生しているようです...

詳しく説明する前に、新しいプロセスを開始するために現在使用しているコードを次に示します (コンソール プロセス - 具体的には PowerShell ですが、問題にはなりません)。

    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 のドキュメントやさまざまなフォーラム スレッドからは多くのアドバイスしか得られませんでした。特に、このようなエラーの原因はさまざまであるため、コードのどのセクションを変更する必要があるのか​​わかりません。おそらく、変更する必要があるのは単に 1 つのパラメーターにすぎませんが、私が知っているすべてに対して、間違った/不十分な WinAPI 呼び出しを行っている可能性があります。私を大いに混乱させるのは、プレーンを使用するコードの以前のバージョンがCreateProcess関数(ユーザートークンパラメーターを除いて同等)は完全に正常に機能しました。私が理解しているように、Logon ユーザー関数を呼び出して適切なトークン ハンドルを受け取り、それを複製して に渡せるようにするだけで済みますCreateProcessAsUser

コードの変更や説明の提案は大歓迎です。

ノート

私は主に MSDN のドキュメントを参照してきました ( C# の関数/ストラット/列挙型の宣言についてはPInvoke.netも同様です)。特に次のページでは、備考セクションに多くの情報が含まれているように見えますが、そのうちのいくつかは重要であり、私にはわからない可能性があります。

編集

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") で指定し、2 番目の引数としてドメインを個別に渡そうとしましたが、すべて役に立ちませんでした (同一のエラー)。

4

3 に答える 3

6

ここから:

通常、CreateProcessAsUser 関数を呼び出すプロセスには、SE_ASSIGNPRIMARYTOKEN_NAME および SE_INCREASE_QUOTA_NAME 権限が必要です。この関数が ERROR_PRIVILEGE_NOT_HELD (1314) で失敗した場合は、代わりに CreateProcessWithLogonW 関数を使用してください。CreateProcessWithLogonW には特別な権限は必要ありませんが、指定されたユーザー アカウントは対話的にログオンできる必要があります。一般に、CreateProcessWithLogonW を使用して、別の資格情報でプロセスを作成することをお勧めします。

このブログ投稿を参照してください。.NET で CreateProcessWithLogonW と CreateProcessAsUser を呼び出す方法

于 2009-03-20T23:52:42.327 に答える
5

Jonathan Peppersは、私の問題を修正するこの素晴らしいコードを提供してくれました

http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/0c0ca087-5e7b-4046-93cb-c7b3e48d0dfb?ppud=4

于 2009-12-03T16:54:28.503 に答える