【问题标题】:PowerShell safe expansion of non printing characters in arbitrary stringsPowerShell 安全扩展任意字符串中的非打印字符
【发布时间】:2021-07-24 08:20:46
【问题描述】:

我在 XML 文件中有数据,最终将用作注册表路径,其中可能包含非打印字符(例如,将路径从网站复制到 XML 时)。如果发现非打印字符,我想验证数据并抛出特定错误。

在 Powershell 中,如果我在单引号中定义了一个带有非打印字符的变量,然后 test-Path 它测试为有效路径,因为非打印字符被作为文字处理。

Test-Path 'HKEY_LOCAL_MACHINE\SOFTWARE\Test\`n@microsoft.com/GENUINE\@microsoft.com/GENUINE' -isValid

同样的东西用双引号将“扩展”非打印字符并返回false,这是我需要的。

Test-Path "HKEY_LOCAL_MACHINE\SOFTWARE\Test\`n@microsoft.com/GENUINE\@microsoft.com/GENUINE" -isValid

我找到了对 [string]::Format(() 的引用,用于扩展非打印字符,但是

$invalidPath = 'HKEY_LOCAL_MACHINE\SOFTWARE\Test\`n@microsoft.com/GENUINE\@microsoft.com/GENUINE'
[string]::Format("{0}",$invalidPath)

未按预期扩展非打印字符。 我也看到过使用 Invoke-Expression 的参考,但这并不安全,也不是一种选择。

终于找到了$ExecutionContext.InvokeCommand.ExpandString(),好像可行,

$ExecutionContext.InvokeCommand.ExpandString('HKEY_LOCAL_MACHINE\SOFTWARE\Test\`n@microsoft.com/GENUINE\@microsoft.com/GENUINE')

返回一个多行字符串到控制台,而 $ExecutionContext.InvokeCommand.ExpandString('Write-Host "Screwed"') 将实际字符串返回到控制台,而不是实际执行 Write-Host 并且仅将 Screwed 返回到控制台。

最后,

$invalidPath = 'HKEY_LOCAL_MACHINE\SOFTWARE\Test\`n@microsoft.com/GENUINE\@microsoft.com/GENUINE'
Test-Path ($ExecutionContext.InvokeCommand.ExpandString($invalidPath)) -isValid

按预期返回 false。这让我认为这是追求的正确方法,但考虑到其他地方的所有问题,我想 100% 确定这种方法不会被用作安全弱点。我是在正确的轨道上,还是我的 Google-Fu 还没有出现的陷阱?

【问题讨论】:

    标签: powershell invoke-command


    【解决方案1】:

    Invoke-Expression 一样,$ExecutionContext.InvokeCommand.ExpandString() 容易受到不需要的命令的注入,但在后一种情况下,此类命令仅在包含在$(...)subexpression operator 中时才会被识别,因为这是嵌入的唯一方法expandable strings 中的命令(方法的参数被解释为)。

    例如:

    $ExecutionContext.InvokeCommand.ExpandString('a $(Write-Host -Fore Red Injected!) b')
    

    防止这种情况的一种简单方法是分类处理所有嵌入的 $ 字符。 逐字逐句,通过` 转义它们:

    'a $(Write-Host -Fore Red Injected!) b',
    'There''s no place like $HOME',
    'Too `$(Get-Date) clever by half' |
      ForEach-Object {
        $ExecutionContext.InvokeCommand.ExpandString(($_ -replace '(`*)\$', '$1$1`$$'))
      }
    

    注意:在输入字符串中转义 逐字 $ 就足够了。 $(或 ( / ))(`u{24}(或 `u{28} / `u{29})的 Unicode 转义序列表示,仅在 PowerShell (Core) v6+ 中支持),不是问题,因为 PowerShell 会处理生成的字符逐字


    当然,如果存在命令(或变量值)注入的风险,您可以选择报告错误,这可以很简单:

    $path = 'There''s no place like $HOME'
    
    if ($path -match '\$') { Throw 'Unsupported characters in path.' }
    

    但是,这也会阻止在路径中合法使用逐字的 $


    退后一步:

    您声明这些路径可能是从网站复制粘贴的。 这种粘贴的字符串可能确实包含(也许是隐藏的)控制字符,但它们会被包含在 逐字 而不是 PowerShell_escape 序列

    因此,测试/悄悄地从字符串的文字内容中删除控制字符可能就足够了(在调用Test-Path -IsValid之前):

    # Test for control characters.
    if ($path -match '\p{C}') { throw 'Path contains control characters.' }
    
    # Quietly remove them.
    $sanitizedPath = $path -replace '\p{C}'
    

    【讨论】:

    • 我担心我错过了一些技巧。它有InvokeCommand 作为一种方法,这一事实让我很担心。我什至可以使用正则表达式来查看是否有任何匹配 $(...) 并在那里抛出特定错误。
    • @Gordon 这是个好主意,也是增加一层额外安全性的好方法。
    • @gordon - 如果您打算使用正则表达式来解析 $() 的输入,请注意 Unicode 转义序列 - 例如$x = "xxx"; $y = "aaa$`u{28}`$x)bbb"; $ExecutionContext.InvokeCommand.ExpandString($y)。您可能想先使用 powershell 的内置解析器来预处理输入字符串 - 请参阅 docs.microsoft.com/en-us/dotnet/api/…
    • @mclayton,Unicode转义(v6+)扩展为总是使用verbatim,所以转义verbatim$,如答案所示, 就足够了。
    • @mklement0 - 你是对的,当然。这将教会我尝试使用我的手机在 TryItOnline (tio.run) 上编写/运行测试:-)。我没有测试我认为我正在测试的东西......
    【解决方案2】:

    我最初运行的是 PS 版本 7.1.3,其中所有这些方法都产生了相同的结果。

    我跑的时候:

    $invalidPath = 'HKEY_LOCAL_MACHINE\SOFTWARE\Test\`n@microsoft.com/GENUINE\@microsoft.com/GENUINE'
    Test-Path ($ExecutionContext.InvokeCommand.ExpandString($invalidPath)) -isValid
    

    True 被返回

    同:

    Test-Path ([regex]::Escape($invalidPath)) -IsValid
    

    以及你提到的其他方法。

    在 5.1 的测试中,我看到了和你一样的结果。

    在您的 XML 中,您会看到类似 `n\n实际 非打印字符的内容吗?浏览器

    'HKEY_LOCAL_MACHINE\SOFTWARE\Test\`n@microsoft.com/GENUINE\@microsoft.com/GENUINE'
    

    'HKEY_LOCAL_MACHINE\SOFTWARE\Test\
    @microsoft.com/GENUINE\@microsoft.com/GENUINE'
    

    如果后者为真,您应该能够将字符串传递给 test-path 中的变量,如 Test-Path "$invalidPath" -IsValid 以获得您要查找的内容。在 7.1.3(可能更早)中,似乎 PS 足够聪明,可以解析这些转义序列等——或者没有简单的方法可以做你正在寻找的事情。

    【讨论】:

    • 有趣。我只是尝试使用此处的字符串,但确实失败了。但是最好提供一个错误代码来解释它失败的原因,这样用户就可以知道要纠正什么。对我来说,一直是 Test-Path 之类的弱点。
    猜你喜欢
    • 2021-10-07
    • 1970-01-01
    • 1970-01-01
    • 2011-03-18
    • 1970-01-01
    • 2020-04-20
    • 2021-03-22
    • 1970-01-01
    相关资源
    最近更新 更多