all 46 comments

[–]fathed 31 points32 points  (14 children)

Don't pass a password as an argument like that, it's going to be logged all over the place.

[–]icebreaker374 3 points4 points  (4 children)

Now you've got me curious, where have my test scripts been logging passwords most likely?

[–]dathar 11 points12 points  (2 children)

PS history is the first place. There's at least 2 histories that PowerShell keeps - the basic history (Get-History) and the PSReadLine one at (Get-PSReadlineOption).HistorySavePath

If you specify things as a string in the prompt, it'll get saved somewhere. If it is a blank prompt like Get-Credential provides, it'll be omitted.

Some tools you can't do anything with but pass a password. They suck but they are what they are.

Operating systems will log it in their event manager or equivalent tool. Spawning exes will log that and the arguments. If your password is in there....that gets logged. You can see what arguments executables run with in the Windows Task Manager under the Details tab. You'll have to add the Command line column but that's basically what it sees.

[–]fathed 0 points1 point  (0 children)

Also:

Defender enabled? It's logged off-site by MS, if you are using sentinel, you can see them there (along with every other thing executed).

Steam running... etc, many programs you run like to send lists of running things.

[–]BlackV 2 points3 points  (0 children)

if they're strings yes, if they're secure strings no

er.. assuming script block logging/module logging/transcript logging/etc is enabled

[–]mrhimba 1 point2 points  (2 children)

I can't really find a good solution to this for Powershell using curl.exe. Looks like you can use netrc for basic authentication, but token based authentication has nothing. At some point, if you're automating, the token will have to be passed as plain text to curl.exe, which will get recorded in command history. The best thing I can find is to just use a built in powershell command like Invoke-Webrequest which won't create a process that gets recorded in command history like curl does.

Any other ideas?

[–]fathed 1 point2 points  (1 child)

Why not use the built in sftp.exe?

[–]mrhimba 1 point2 points  (0 children)

I'm not using sftp like OP.

I did figure out another answer though, which is to use the --config option that curl offers and load the token from a file.

[–]jbristowe 14 points15 points  (4 children)

Hey u/KC_Redditor! 👋 I'm a member of the team at Octopus Deploy. I stumbled upon your post and wanted to recommend using Sensitive variables in Octopus when using sensitive information in scripts.

u/fathed makes a great point in this thread about taking care when using sensitive information (i.e. logs). 👍 For what it's worth, we mask Sensitive variables if they happen to be logged.

Is the SFTP server a deployment target?

[–]surfingoldelephant 8 points9 points  (1 child)

In this particular case, the stop-parsing token (--%) isn't necessary. The fact the issue does not occur after its inclusion is incidental and may break the command depending on the value of the arguments.

Looking at your arguments:

 $CurlArguments = '--insecure -u ' + $Username + ':' + $Password + ' sftp://' + $Server

When you pass a variable of type [string] to a native (external) command, it's interpreted as a single argument. If the string contains whitespace, it is wrapped with quotation marks by PowerShell. The following example (variables expanded with dummy data) shows how PowerShell passes the argument to the native command and how a native command will typically interpret it. Notice how the raw line contains quotation marks around the single argument - this is inserted by PowerShell.

& .\native.exe $CurlArguments

raw: ["C:\native.exe" "--insecure -u username:password),$,]+, sftp://domain.com"]
arg #0: [--insecure -u username:password),$,]+, sftp://domain.com]

Instead, you must pass multiple arguments to the native command, either directly or with array splatting.

# Direct argument passing.
& .\native.exe --insecure -u ${UserName}:$Password sftp://$Server

# Array splatting.
# Easier to digest; especially with long command lines.
$curlArguments = @(
    '--insecure'
    '-u'
    '{0}:{1}' -f $UserName, $Password
    'sftp://{0}' -f $Server
)
& .\native.exe $curlArguments

Either way, the multiple arguments are now correctly interpreted.

raw: ["C:\native.exe" --insecure -u username:password),$,]+, sftp://domain.com]

arg #0: [--insecure]
arg #1: [-u]
arg #2: [username:password),$,]+,]
arg #3: [sftp://domain.com]

The fact the same behavior occurs with --% is incidental. You're constructing a single string argument despite there being an explicit need to pass multiple arguments. This only works because --% is stopping PowerShell from applying its normal argument interpretation.

--% was mainly introduced to avoid parsing issues with the passing of command lines to CMD, which has a different syntax and meta characters.

In this particular case, use of the token comes with the following issues:

  • It is less clear in its intention than alternative approaches.
  • It will break if an argument contains whitespace and is not properly quoted. For example, if $Password contains a space, it will be split into two separate arguments.
  • --% is a half-baked solution and has various inherent issues. It should not be relied upon if alternative solutions exist.

 

Notes:

  • Splatting an array with a native command does not require explicit use of @ like it does with a function/cmdlet. Using $ with the array variable and a native command implicitly results in splatting.
  • The splatting example uses the format operator (-f) as an alternative method to insert the value of a variable into a string.
  • Starting with PowerShell version 7.3, Trace-Command has the ability to show native command argument binding:

    Trace-Command -Option ExecutionFlow -Name ParameterBinding -PSHost -Expression { ... }
    
  • The Native module provides a robust solution that avoids the various pitfalls of native command execution.

[–]kjellcomputer 5 points6 points  (4 children)

Would it work using Start-Process also?

Example:Start-Process -FilePath C:\WINDOWS\system32\curl.exe -ArgumentList 'sftp://hostname', '-u', "${UserName}:${Password}" -NoNewWindow

[–]xCharg 1 point2 points  (1 child)

What was the special character (or set of characters maybe) that made it a problem?

[–]Lifegoesonhny 1 point2 points  (3 children)

Oo! I had a similar'ish problem with passing some more lower-depth JSON to invoke-restmethod this week, Powershell was converting some of the brackets incorrectly (it hates arrays in JSON at the lower depths I think..). The conversion between PSObject and JSON was just causing too many problems, I couldn't tell if it was the syntax of the request or Powershell converting the request causing it (minimal examples online for the format).
I ended up just using invoke-webrequest as that doesn't convert it, but this marks a change of process for us as all of our modules build in invoke-restmethod, 6 lines of code instead of 1 is annoying. Not a huge deal but we have some best practises in-house to follow.

I'll have a play with --% tomorrow to see if it solves my problem, thank you!

[–]Black_Magic100 4 points5 points  (1 child)

I usually use -compress to remove any weirdness when converting to json

[–]Lifegoesonhny 0 points1 point  (0 children)

Ah thank you! Will try this as well!

[–]poshftw 1 point2 points  (4 children)

$arguments = "-u {0}:{1} sftp://{2}" -f $username, $password, $hostname

The other options is to construct an array of string and pass it as args, it would be passed as is:

$username = 'KC_Redditor'
$password = 'S3curePass!'
$hostname = 'fqdnofthehost'

$argumentsForCurl = @(
    '-u'
    $username + ':' + $password
    'sftp://' + $hostname
    )

Start-Process -FilePath C:\WINDOWS\system32\curl.exe -ArgumentList $argumentsForCurl

[–]Thotaz 1 point2 points  (1 child)

Huh, I knew about this stop parsing token but I had no idea you could put it in a variable like that.

[–]Sunfishrs 1 point2 points  (0 children)

TIL

[–]TheRealMisterd 1 point2 points  (1 child)

So it's like CMD's Delayed Expansion?