无法通过插值法计算PowerShell密码

rryofs0p  于 2022-11-10  发布在  Shell
关注(0)|答案(1)|浏览(119)

如果未提供密码,我将根据another answer使用PowerShell向用户请求密码。然后,我将密码(没有双关语)传递给某个程序do-something.exe。我没有使用中间变量,而是尝试将密码转换为普通字符串“inline”:

[CmdletBinding()]
Param(
  [Parameter(Mandatory, HelpMessage="password?")] [SecureString]$password
)
do-something password=${[Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($password))}

这不管用。我只能使用一个临时的中间变量使其工作:

[CmdletBinding()]
Param(
  [Parameter(Mandatory, HelpMessage="password?")] [SecureString]$password
)
$pwd=[Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($password))
do-something.exe password=$pwd

在调用do-something.exe时尝试内联计算密码是否出错?如何做到这一点呢?

jjjwad0x

jjjwad0x1#

${...}是一个变量引用,不管...是什么,都会原封不动地**作为变量名*。

  • 将变量名称包含在{...}中通常是*不必要的,但在两种情况下是必需的:(A)如果变量名称包含特殊字符和/或(B)在可扩展字符串("...")的上下文中,以消除变量名称与后续字符的歧义-请参见this answer

为了嵌入表达式命令作为参数部分,使用$(...)子表达式运算符,并且最好将整个参数括在"..."**中--这样,整个参数明确地作为单个参数传递,而以$(...)子表达式开头的未加引号的*令牌将作为(至少)两个参数传递(参见this answer)。

  • 如果表达式或命令本身形成参数(...),则分组运算符就足够了,通常更可取-请参见this answer

因此:

[CmdletBinding()]
param(
  [Parameter(Mandatory, HelpMessage="password?")]
  [SecureString] $password
)

# Note the use of $(...) and the enclosure of the whole argument in "..."

do-something "password=$([Runtime.InteropServices.Marshal]::PtrToStringBSTR([Runtime.InteropServices.Marshal]::SecureStringToBSTR($password)))"

另请注意:

  • 在Windows上没有区别(在Unix上[securestring]示例几乎不提供保护,应该完全避免),但它应该是[Runtime.InteropServices.Marshal]::PtrToStringBSTR(),而不是[Runtime.InteropServices.Marshal]::PtrToStringAuto()
  • 正如Santiago Squarzon指出的那样,有一种更简单的方法可以将SecureString示例转换为其纯文本等效项(但是,通常应该避免[1],更根本的是,不鼓励在新项目中使用[securestring][2]):
[pscredential]::new('unused', $password).GetNetworkCredential().Password

[1]存储在.NET字符串中的密码的纯文本表示形式在内存中停留了一段您无法控制的未指定时间。更具体地说,如果它是进程命令行的一部分,就像您的例子一样,它可以通过这种方式被发现。当然,如果您的目标CLI除了使用纯文本密码之外无法提供其他身份验证方式,则您别无选择。
[2]参见this answer,它链接到this .NET platform-compatibility recommendation

相关问题