2

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.

mklement0
  • 382,024
  • 64
  • 607
  • 775
Jędrzej Dudkiewicz
  • 1,053
  • 8
  • 21
  • 3
    This is a classic case of [X-Y problem](http://mywiki.wooledge.org/XyProblem). Instead of trying to escape arbitrary passwords for use with `net user` you should look into how to properly create local user accounts in PowerShell. `net user` is one way to do that, but there are others that are better suited for handling arbitrary password strings. [Related](https://stackoverflow.com/q/15167069/1630171). – Ansgar Wiechers Jul 17 '19 at 22:36
  • @AnsgarWiechers: While it's always beneficial to learn PowerShell-native ways of doing things - because they usually offer descriptive, standardized syntax and more powerful features - there is no excuse for PowerShell's broken quoting of arguments for external programs: invoking external executables - such as `net.exe` in this case - is one of the core mandates of a shell, which is especially important now that PowerShell [Core] is cross-platform, given that Unix-like platforms (unlike Windows) have a wealth of powerful - and performant - utilities (external executables). – mklement0 Jul 18 '19 at 02:55
  • @AnsgarWiechers Quote: "Consider the following example (disregard security implications):". Besides I couldn't find any other way to create user without installing features and/or capabilities. For example "New-LocalUser" does not work on Windows Server 2014 and I have to support it. So there. – Jędrzej Dudkiewicz Jul 18 '19 at 06:15
  • @AnsgarWiechers Question that you linked to has `net user` as most voted answer. `New-LocalUser` is PS 5.1, I'm on 4.0 at best, 3.5 as baseline. Mentioned module `Carbon` has to be installed separately and I can't do that. So it seems that it isn't classic case of `X-Y problem`, I'm trying to do X, but there is no way except for doing Y. – Jędrzej Dudkiewicz Jul 18 '19 at 07:10
  • @mklement0 My intention was not to make excuses for the quoting hell. However, I've always been of the mind "use what works". If there are other (working) ways to accomplish a particular goal: don't waste time on working around the limitations of one particular approach. – Ansgar Wiechers Jul 18 '19 at 07:10
  • @AnsgarWiechers Well, I don't understand what "don't get fresh with me, buster" means, as I don't speak English that well. But that aside I read *again* linked question and tried code from the question itself *again*. I don't know what I've been doing wrong yesterday, but lo and behold, it works like a charm. Thanks for your help. – Jędrzej Dudkiewicz Jul 18 '19 at 07:28
  • Not sure if we're miscommunicating here, but I meant the code from the question itself (the ADSI approach), not code from one of the answers. – Ansgar Wiechers Jul 18 '19 at 07:31
  • @AnsgarWiechers Yes, sorry, I edited my comment. I meant question itself but since solutions are usually in answers and I were somewhat excited that it worked... Anyway thanks again for your help. – Jędrzej Dudkiewicz Jul 18 '19 at 07:36
  • @AnsgarWiechers: That's a sensible approach, but very different from saying that the problem at hand is an X-Y problem. A proper framing would be: Yes, PowerShell's quoting is - regrettably - broken, but there may be a way to avoid it altogether, and that way may even offer more flexibility. – mklement0 Jul 19 '19 at 01:11
  • @mklement0 I stand by that it is an X-Y problem, b/c the OP was focusing on working around broken quoting rather than the actual objective: creating a new local user account with a password. However, I believe this discussion is getting off-topic. – Ansgar Wiechers Jul 19 '19 at 07:08
  • @AnsgarWiechers:,Agreed that it technically fits the X-Y pattern, but my point was that it is important to acknowledge that what Jędrzej was trying _should_ work and that PowerShell is the problem here. With that important clarification out of the way, it makes perfect sense to point to alternatives that offer more flexibility and better security, so I've added a link to the post you suggested to the answer. Knowing PowerShell's quoting challenges is important, not least because sometimes you may find yourself with _no_ alternative to calling an external program. Happy to leave it at that. – mklement0 Jul 19 '19 at 09:31

1 Answers1

2

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.

mklement0
  • 382,024
  • 64
  • 607
  • 775
  • 1
    What I tried is what I tried out of desperation. In reality I only had any hopes for `Start-Process`, and thought that maybe due to Powershell's idiosyncrasies something else will work. As for issue that you linked, this is similar to one that I added, and it basically means that due to how Windows' arguments are parsed and developer's need to keep backward compatibility, there is no way to do it *right*. I don't know why they didn't provide any built-in (or any other it seems) module to start external tools - executing external programs is basically what shell is for if you ask me. – Jędrzej Dudkiewicz Jul 18 '19 at 06:20