Search code examples
powershellcmdescapingquoting

How to escape unknown string and pass it to native command


Consider the following example (disregard security implications):

$pass = (Read-Host)
&net user testuser $pass /add

Now if someone enter something like:

a1'"\'"\'"\

this won't work. I get error message saying that such user does not exist.

I tried using [Regex]::Escape(), but it does not work. I tried putting $pass in double quotes, but it does not work. I tried using Start-Process like this:

Start-Process -Wait net -ArgumentList @("user", "testuser", $pass, "/add")

but it does not work. I tried running net command without leading ampersand, but it does not work.

I read ca. 30 questions here on StackOverflow that show after asking for "powershell escape command argument" (of 197 that are shown), but nothing comes even close to what I need. Maybe I missed something, but 197 questions is a lot.

EDIT:

OK, so I found this:

https://github.com/PowerShell/PowerShell/issues/1995

It seems that it is nearly impossible to do it right without writing custom function and knowing if what is called is native binary or cmdlet or something else. There has never been more appropriate moment for saying "I don't even".

EDIT 2: Regarding this very case (user creation and proper quoting of password) Ansgar Wiechers provided link that shows yet another way to create local user account, much appreciated. This doesn't mean that quoting problem goes away, it just means that there is one less case when such quoting is required.


Solution

  • Note: Given the challenges around quoting detailed below, consider solving this problem differently, which may ultimately offer more flexibility and better security.
    However, it's important to understand PowerShell's quoting challenges in general, not least because there may not always be an alternative to calling an external program.


    While it shouldn't be necessary, the unfortunate reality is that PowerShell requires manual escaping of embedded " chars. as \" when passing arguments to external programs, which is a longstanding problem summarized in this GitHub issue.

    # Sample password
    $pass = @'
    a1'"\'"\'"\
    '@
    
    #"# Escape all embedded " chars. as \"
    net user testuser ($pass -replace '"', '\"') /add
    

    As for what you tried:

    I tried using [Regex]::Escape()

    You're trying to pass a password stored in a variable verbatim, so regular expressions do not come into play here.

    I tried putting $pass in double quotes

    That has no effect, because $pass is a string variable to begin with (a string is what Read-Host returns).

    I tried using Start-Process

    Start-Process is never the right tool for invoking console applications synchronously - see https://stackoverflow.com/a/51334633/45375.

    Additionally, Start-Process has its own quoting challenges - see this GitHub issue.