powershellsshescapingopensshpowershell-5.1

Escaping rules for strings containing scripts through SSH


I'm trying to send a PowerShell script as a string through SSH in PowerShell 5.1. Its objective is to modify the content of a configuration file in a remote computer. I managed to fix the problems relating to escaping the special character sequences thanks to this answer in my previous question. However, I assumed that ssh $Username@$ComputerIP $stringScript would execute similarly to cmd.exe /c $stringScript but from my testing it doesn't, they are processed differently at a level I don't understand, as the code with ssh gives an error.

Here's my intended code:

$forceOnlyKeysSSH = '`nMatch all`n`tPasswordAuthentication no'
$rmtPSAuthOnlyKeys = "powershell Add-Content -Force -Verbose -Path c:\ProgramData\ssh\sshd_config " +
    "-Value \`"$forceOnlyKeysSSH\`""
ssh -o ConnectTimeout=10 $Username@$ComputerIP $rmtPSAuthOnlyKeys

Ideally it would modify the sshd_config file with

Match all
    PasswordAuthentication no

But I'm getting this error, that to me suggest is dividing the arguments around the spaces.

Add-Content : A positional parameter cannot be found that accepts argument 'all
        PasswordAuthentication'.
At line:1 char:1
+ Add-Content -Force -Verbose -Path c:\ProgramData\ssh\sshd_config -Val ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [Add-Content], ParameterBindingException
    + FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.AddContentCommand

Am I wrongly escaping the string? Why does this error not show up when calling cmd.exe /c $rmtPSAuthOnlyKeys?


Solution


  • Workaround:

    Specifically, in Windows PowerShell and PowerShell 7.2- you must manually \-escape " chars. (double quotes) that are embedded in arguments.

    Here's a cross-edition solution that applies the manual escaping only when needed:

    $forceOnlyKeysSSH = '`nMatch all`n`tPasswordAuthentication no'
    $rmtPSAuthOnlyKeys = "powershell -NoProfile Add-Content -Force -Verbose -Path c:\ProgramData\ssh\sshd_config " +
        "-Value \`"$forceOnlyKeysSSH\`""
    if ($PSVersionTable.PSVersion -lt '7.3') { # Workaround needed
      # Manually add another round of \-escaping to the embedded, escaped \" chars.
      $rmtPSAuthOnlyKeys = $rmtPSAuthOnlyKeys.Replace('\"', '\\\"')
    }
    ssh -o ConnectTimeout=10 $Username@$ComputerIP $rmtPSAuthOnlyKeys
    

    Note: