powershellsshcmdsendkeysplink

Answering Plink hostkey and KEX prompts in PowerShell


I am successfully able to grab the result text I need from the plink.exe command-line below to $response. In order for the command to execute on the remote SSH device, I had to first precede it with emulated keystrokes Y {enter} Y {enter}

Code:

$Device = "SSHDeviceHostname"
$Command = "sho ver | include uptime is"
Add-Type -AssemblyName System.Windows.Forms

$wshell.SendKeys('y~y~'); $response = cmd /C "C:\Windows\plink.exe -ssh -l `"$($CredentialToken.GetNetworkCredential().username)`" -pw `"$($CredentialToken.GetNetworkCredential().password)`" $Device `"$Command`"" 2>$null

Results:

y
y


C:\Users\MKANET\Desktop> $response
SSHDeviceHostname uptime is 5 years, 25 weeks, 1 day, 3 minutes

C:\Users\MKANET\Desktop>

I would like to hide the fake keystrokes Y {enter} Y {enter} from being displayed; without adversely affecting the $response value. I tried to invoke-command'ing the whole scriptblock on the localhost without success. I probably could execute this code in a separate hidden PowerShell session. However, that would most likely slow things down way too much.

PS: I also tried using cmd (below) which unfortunately plink.exe completely ignores the second echo `y; aborting the connection immediately:

cmd /C "Echo Y && Echo Y | plink.exe -ssh -l `"$($Credential.GetNetworkCredential().username)`" -pw `"$($Credential.GetNetworkCredential().password)`" $DeviceName `"$Command`""

Solution

  • To answer your literal question, you can do the following in a batch file:

    powershell -command "Write-Host -NoNewLine y ; sleep 2 ; Write-Host -NoNewLine y" | plink ...
    

    Note that the code really works in batch file (in cmd) only. It does not work in Windows PowerShell. The | has a different semantics in Windows PowerShell than in cmd. In Windows PowerShell, it would wait for powershell to complete before starting the plink, so the only effect of sleep is that it will delay starting the plink. While in cmd, powershell and plink run in parallel, as you want.

    As commented by @mklement0, this has been improved since PowerShell (Core) 6. So there you can do:

    & { 'y'; sleep 2; 'y' } | plink ...
    

    But you should not blindly accept the host key. You lose a protection against man-in-the-middle attacks. You should verify the host key using the -hostkey switch.
    See Prepend a command in Start-Process to provide "y" input to Plink

    Similarly for the KEX verification. There's no switch for the KEX in Plink. But you can create an ad-hoc store session in registry with the diffie-hellman-group1-sha1 allowed. And invoke the stored session in Plink.

    $hostname = "example.com"
    $username = "username"
    $password = "password"
    $hostkey = "c4:26:18:cf:a0:15:9a:5f:f3:bf:96:d8:3b:19:ef:7b"
    
    $session = "Dummy"
    $key = "HKCU:\SOFTWARE\SimonTatham\PuTTY\Sessions\$session"
    
    New-Item -Path $key -Force | Out-Null
    New-ItemProperty -Path $key -Name HostName -Value $hostname | Out-Null
    New-ItemProperty -Path $key -Name UserName -Value $username | Out-Null
    New-ItemProperty -Path $key -Name KEX `
        -Value "ecdh,dh-gex-sha1,dh-group14-sha1,rsa,dh-group1-sha1,WARN" | Out-Null
    
    & plink $session -pw $password -hostkey $hostkey