5

When my PowerShell script runs, it prompts the user for a password parameter. That password can contain any number of special characters like *\~;(%?.:@/ That password is then used as a parameter for a .exe command, but it is often incorrect due to some special characters not being escaped properly.

An example past password was $(?-.?-(. The only characters I needed to escape was '(', which I replaced with '`(' to make it work. However, that password is now expired. The new password is something like *\~;~(%?.:@/ *NOTE: these passwords have random numbers and letters mixed into them as well, but have been redacted.

The only characters in the new password NOT in the first are *\~;%:@/ Is there an easy way to escape all characters and just take any user input as it is? If not, would someone mind helping me escape these special characters?


param (
    [Parameter(Mandatory=$true)][string]$password
)

The above code prefaces the script, causing the console to prompt for user input.

Invoke-Expression -Command "<path_to_exe> -install $user $password"

^this is the command that uses that password parameter


I have tried many other suggestions on Stack Overflow, Reddit, and other various coding forums/blogs and none have worked. Any help is much appreciated!

5
  • Do you have a full list of characters you need to escape? Commented Sep 16, 2019 at 23:32
  • Please show the code handling/setting that password. We cannot tell you how (or if) you need to escape anything without seeing that. Commented Sep 16, 2019 at 23:33
  • @Drew *\~;~(%?.:@/ is the full list of characters I need to escape. Commented Sep 16, 2019 at 23:35
  • @AnsgarWiechers param ( [Parameter(Mandatory=$true)][string]$password ) The above code prefaces the script, causing the console to prompt for user input. Invoke-Expression -Command "<path_to_exe> -install $user $password" ^this is the command that uses that password parameter Commented Sep 16, 2019 at 23:39
  • 1
    Don't use Invoke-Expression. & 'C:\path\to\your.exe' -install $user $password will do what you want. Commented Sep 17, 2019 at 7:55

3 Answers 3

11

You're using Invoke-Expression to call an external program:

  • There's no reason to do that, and Invoke-Expression should generally be avoided: it causes quoting headaches (as in your case), but, more importantly, it can be a security risk and there are typically better solutions.

    • As an aside: Unfortunately, even with direct invocation there can be quoting challenges around empty-string arguments and arguments with embedded " chars. - see footnote [1] and this answer.
  • If you instead invoke the external program directly - as any shell, including PowerShell is designed to do - your problem will likely go away:[1]

& <path_to_exe> -install $user $password

Note:

  • &, PowerShell's call operator, is only needed if your executable's path is quoted (e.g, "C:\Program Files\foo.exe") and/or is specified via a variable reference (e.g., $HOME\foo.exe); otherwise, you can invoke the executable as-is (e.g., to invoke cmd.exe, use something like
    cmd /c 'echo hi').

  • The above shows uses of individual variables as arguments; however, it is also possible to store multiple or all arguments in an array - see this answer.


Separately, if you do ever find yourself needing to escape any of the characters in a set of characters, use -replace with a character class, [...]:

Note: This is not necessary for passing arguments, neither to external programs, as shown above, nor to PowerShell commands; however, due to PowerShell's broken handling of " characters embedded in argument values passed to external programs, you may have to escape " characters (only), as \"[1].

PS> 'a*b\c~d;e(f%g?h.i:j@k/l' -replace '[*\\~;(%?.:@/]', '`$&'
a`*b`\c`~d`;e`(f`%g`?h`.i`:j`@k`/l  # all chars. inside [...] were `-escaped

Note: Since \ has special meaning even inside a character class, it had to be escaped as \\ - all other chars. are used as-is.

For more information about the -replace operator, see this answer.


[1] There is one character that still causes problems: embedded ". For historical reasons, PowerShell does not properly pass embedded " correctly to external programs, and annoyingly requires manual \-escaping in Windows PowerShell and PowerShell (Core) versions up to v7.2.x - see this answer for details. Applied to your solution:& <path_to_exe> -install $user ($password -replace '"', '\"')

Sign up to request clarification or add additional context in comments.

Comments

7

Using the below will escape the characters using the escape prefix you mentioned. The normal escape prefix is \ as shown below. I have set it this way so it is easier for you to add additional characters to escape or change the escape prefix.

function Set-EscapeCharacters {
    Param(
        [parameter(Mandatory = $true, Position = 0)]
        [String]
        $string
    )
    $string = $string -replace '\*', '`*'
    $string = $string -replace '\\', '`\'
    $string = $string -replace '\~', '`~'
    $string = $string -replace '\;', '`;'
    $string = $string -replace '\(', '`('
    $string = $string -replace '\%', '`%'
    $string = $string -replace '\?', '`?'
    $string = $string -replace '\.', '`.'
    $string = $string -replace '\:', '`:'
    $string = $string -replace '\@', '`@'
    $string = $string -replace '\/', '`/'
    $string
}

$Password = Set-EscapeCharacters $Password

1 Comment

I suspect Enoch's escaping problem will go away if the target program is invoked directly rather than - needlessly - via Invoke-Expression. Aside from that, it's simpler and more efficient to use a single -replace operation with a character set ([...]). Quibble: [Regex]::Escape() escapes regex metacharacters, which has nothing to do with PowerShell.
0

Not exactly answering the question for Power-Shell scripting by OP, but googling powershell string escape brings here, so below could be useful for people looking for escaping in interactive shell.


If you need variable expansion in strings with special characters (e.g. JSON) used as input to commands (e.g. curl.exe) escape both " and \ with `:

curl.exe -X POST -d "{`\`"username`\`":`\`"$user`\`",`\`"password`\`":`\`"$pw`\`"}" --trace-ascii curl.log "https://exampled.com/api"

So the actual string sent to curl.exe will be:

{\"username\":\"test\",\"password\":\"foo\"}

In the curl trace log you can see the actual data sent is valid JSON:

=> Send data, 36 bytes (0x24)
0000: {"username":"test","password":"foo"}

See this answer for more information.

1 Comment

Note that \ never needs escaping in PowerShell, so curl.exe -X POST -d "{\`"username\`" ...}" is sufficient. In PowerShell (Core) 7 v7.3+, there is fortunately no longer a need to \-escape embedded " chars.: curl.exe -X POST -d "{`"username`" ...}"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.