jborean93 / pypsrp

PowerShell Remoting Protocol for Python
MIT License
328 stars 49 forks source link

New authentication event on each invoke call on the same ps object #166

Open abhispra opened 1 year ago

abhispra commented 1 year ago

Hi Jordan,

I'm observing a logon event for each invoke call on the same PS object. Sample code

import logging
import psrp

logging.basicConfig(filename='out.txt', level=logging.DEBUG)
conn = psrp.WSManInfo("WIN-MLEMJVSIP4P.corp.xyz.com", auth="ntlm",
                             username='administrator', password='********')
with psrp.SyncRunspacePool(conn) as rp:
    ps = psrp.SyncPowerShell(rp)
    ps.add_command("Get-Item").add_parameter("-Path", "HKLM:\\Software\\Mozilla")
    object = ps.invoke()
    ps.add_statement()
    ps.add_command("Get-Item").add_parameter("-Path", "HKLM:\\Software\\Mozilla\\Firefox")
    object = ps.invoke()
    val = ps.invoke()
    print(object)
    print(val)

The above code generates a large number of logon events. Notably, each invocation of the invoke function call is performing a logon.

Considering that I'm using a SyncRunSpacePool and SyncPowerShell instance on the same ps object, shouldn't there be one logon and then reuse the session rather than a logon? Am I missing something here, or have you noticed similar things? Thank you for all your help.

Edited the domain and password.

jborean93 commented 1 year ago

This is expected and how the native Windows PowerShell client works. Each Runspace Pool and Pipeline spawn a background listener thread. These threads must be run on a new connection which means they must reauthenticate themselves so in this script example you will see:

The primary reason why this is done is the runspace pool and/or pipeline can send back events that are handled as they come in rather than through something that blocks the main thread. This enables the following

abhispra commented 1 year ago

Thank you for the explanation.

The thing that I don't fully comprehend - aren't the above invoke running on the same pipeline? I wrote the below C# program using dot net core to test my understanding.

namespace Samples
{
    using System;
    using System.Security;
    using System.Collections.ObjectModel;
    using System.Management.Automation;            // PowerShell namespace.
    using System.Management.Automation.Runspaces;  // PowerShell namespace.

    internal class RemoteRunspace02
    {
        private static void Main(string[] args)
        {
            Uri RemoteComputerUri = new Uri("https://WIN-MLEMJVSIP4P.corp.xyz.com:5986/WSMAN");
            WSManConnectionInfo connectionInfo = new WSManConnectionInfo(RemoteComputerUri);
            connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Default;
            SecureString securePwd = new SecureString();
            string pwd = "*********";
            foreach (char ch in pwd)
            {
                securePwd.AppendChar(ch);
            }
            connectionInfo.Credential = new PSCredential("wstest", securePwd);
            connectionInfo.OperationTimeout = 4 * 60 * 1000; // 4 minutes.
            connectionInfo.OpenTimeout = 1 * 60 * 1000; // 1 minute.
            connectionInfo.NoMachineProfile = true;
            connectionInfo.SkipCACheck = true;
            connectionInfo.SkipRevocationCheck = true;

            using (Runspace remoteRunspace = RunspaceFactory.CreateRunspace(connectionInfo))
            {
                remoteRunspace.Open();
                using (PowerShell powershell = PowerShell.Create())
                {
                    powershell.Runspace = remoteRunspace;

                    powershell.AddCommand("Get-Item");
                    powershell.AddParameter("-Path", "HKLM:\\Software\\Mozilla");
                    Collection<PSObject> results = powershell.Invoke();
                    Console.WriteLine(results);
                    powershell.AddStatement();
                    powershell.AddCommand("Get-Item");
                    powershell.AddParameter("-Path", "HKLM:\\Software\\Mozilla\\Firefox");
                    results = powershell.Invoke();
                    Console.WriteLine(results);
                    powershell.AddStatement();
                    powershell.AddCommand("Get-Item");
                    powershell.AddParameter("-Path", "HKLM:\\Software\\Mozilla\\Mozilla Firefox");
                    results = powershell.Invoke();
                    Console.WriteLine(results);
                    powershell.AddCommand("Get-Item");
                    powershell.AddParameter("-Path", "HKLM:\\Software\\Mozilla\\Mozilla Firefox");
                    results = powershell.Invoke();
                    Console.WriteLine(results);
                    powershell.AddCommand("Get-Item");
                    powershell.AddParameter("-Path", "HKLM:\\Software\\Mozilla\\Mozilla Firefox");
                    results = powershell.Invoke();
                    Console.WriteLine(results);
                }
                remoteRunspace.Close();
            }
        }
    }
}

Here I see three logon events generated for the wstest user irrespective of the number of invoke calls. Shouldn't the behavior be similar for both programs, or is there something basic I'm missing? Thanks again for all the help!

jborean93 commented 1 year ago

Hmm, I'll have to look into that and maybe rework it. I always assumed that each receive operation (which starts per invocation) was on the separate thread and thus separate connection.