PowerShell 安全扩展任意字符串中的非打印字符

PowerShell safe expansion of non printing characters in arbitrary strings

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

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

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

双引号同样会“扩展”非打印字符和return 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')

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

最后,

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

return如预期的那样为假。这让我认为这是追求的正确方法,但考虑到其他地方的所有陷阱,我想 100% 确定没有办法将这种方法用作安全弱点。我是在正确的轨道上,还是我的 Google-Fu 还没有出现?

我最初是运行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\nactual 的非打印字符吗? IE

'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 "$invalidPath" -IsValid 这样的变量中的测试路径,以获得您要查找的内容。在 7.1.3(可能更早)中,PS 似乎足够聪明来解析那些转义序列等等——或者没有简单的方法来做你正在寻找的我能找到的东西。

类似于 Invoke-Expression, $ExecutionContext.InvokeCommand.ExpandString() is vulnerable to injection of unwanted commands, except that in the latter case such commands are only recognized if enclosed in $(...), the subexpression operator, as that is the only way to embed commands in 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 '(`*)$', '`$$'))
  }

注意:在输入字符串中逐字转义$即可。 $(或(/))(`u{24}(或`u{28}/`u{29})的 Unicode 转义序列表示,在 PowerShell (Core) v6+ only),不是问题,因为 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}'