恶意PowerShell脚本的检测与解码
PowerShell无处不在。我开始遇到越来越多的恶意PowerShell脚本。
攻击者为何钟爱PowerShell?因为它原生支持多种Windows版本,提供对WMI和.Net Framework的完全访问权限,并且可以在内存中执行恶意代码从而规避杀毒软件。哦,对了——我是否还提到了缺乏日志记录?
在分析这类案例的过程中,我发现了攻击者使用PowerShell的多个迹象。这些包括已安装的服务、注册表项和磁盘上的PowerShell脚本。如果启用了日志记录,也能提供一些有用的痕迹。本文将从不太熟悉PowerShell的分析师视角出发,讨论在分析过程中如何定位恶意PowerShell痕迹,以及一些用于解码混淆PowerShell脚本的方法。这将是未来几周内编写的三部分系列文章的第一部分。
第一部分:作为服务安装的PowerShell脚本
首先是我最喜欢的方法——在系统事件日志中作为已安装服务发现的PowerShell脚本。要找到这些,我首先做的事情之一是查找事件ID 7045。该事件在系统上安装服务时发生。下面显示了一个作为服务安装的PowerShell脚本示例:
需要注意的红色标志包括:
- 随机服务名称
- 服务文件名包含"%COMSPEC%"(cmd.exe的环境变量)
- 对powershell可执行文件的引用
- Base64编码的数据
这样的条目是如何进入事件日志的?虽然有多种方法可以实现,但一种方法是使用内置的Windows服务控制管理器创建服务:
|
|
上述命令创建名为"MyService"的服务,并使用binPath选项启动cmd.exe,后者又执行PowerShell代码。
有趣的是——以这种方式创建服务后,可能会记录一些失败错误。这些错误并不意味着操作不成功。Windows只是期望安装"真正的"服务二进制文件,并等待"服务"报告回传时"超时"。我怎么知道这一点?在测试中,我能够使用上述方法建立成功的反向shell,这在Windows机器上生成了失败的服务错误。左侧是我在攻击虚拟机上启动的Metasploit会话。右侧是Windows 7主机虚拟机。尽管Windows 7机器显示"服务未及时响应启动或控制请求",但反向shell仍在Metasploit会话中打开:
以下是系统事件日志中记录的两个相应事件日志条目7000和7009。尽管7009消息显示"FakeDriver服务启动失败…",但这并不意味着binPath变量内的命令未成功执行。因此请注意,将这些解释为PowerShell未执行的指示可能是错误的:
7045系统事件日志中的PowerShell命令使用base64编码,可以使用Python解码。有趣的是——这个base64代码是Unicode格式,因此在解码时需要指定额外参数。(出于显示原因,我截断了base64文本——您需要包含完整的base64文本才能解码它):
|
|
解码后的PowerShell命令如下所示。快速扫描代码揭示了一些明显的迹象——引用使用TCP协议创建网络套接字和IP地址:
这类似于Meterpreter用于设置反向shell的代码类型。上面的PowerShell代码相当容易解码,但通常情况更为复杂。
接下来是另一个示例——这次只是"常规"base64。再次注意%COMSPEC%变量和对powershell.exe的引用:
同样,可以使用Python解码base64编码的PowerShell:
这次,解码后的输出不太有用。如果我们回头更仔细地查看系统事件日志条目,可以看到对"Gzip"和"Decompress"的引用:
啊…所以反过来思考,这些数据可能已使用Gzip压缩,然后使用base64编码。使用Python,我将把解码的base64写入文件,以便尝试解压缩:
|
|
使用7zip我成功解压了gzip文件!由于没有收到任何错误,我可能走对了路:
现在,如果我使用文本编辑器打开解压后的文件,希望能看到一些PowerShell代码:
啊…什么???好吧——是时候用十六进制编辑器查看一下了:
也没什么帮助。我认为这可能是shellcode。作为下一步,我将通过PDF Stream Dumper的shellcode分析工具scdbg.exe运行它:
嗒哒!scdbg.exe能够从shellcode中为我提取一些IOC。
总结一下,我解码此PowerShell条目采取的步骤:
- 解码base64 PowerShell字符串
- 将解码的base64写入zip文件
- 使用7zip解压缩Gzip文件
- 通过scdbg.exe运行二进制输出
如上所示,在找到宝藏之前可能需要经过多个层。
最后一个示例:
这看起来很熟悉。第一步,解码Unicode base64得到以下结果——其中包含更多base64代码内的base64代码!
混淆,然后使用压缩再次混淆。这在我见过的案例中非常典型。这次因为压缩文本中没有"gzip"引用,我将把第二轮base64保存到常规zip文件,并尝试用7zip打开:
|
|
尝试用7Zip打开压缩文件时出现错误:
使用内置的Windows实用程序也是如此:
我还尝试了各种Python库来解压缩文件。经过一些研究,我发现使用的压缩与某些.Net库相关。现在,由于我是Python爱好者,我想弄清楚如何使用Python解压缩,以便轻松将其实现到我的脚本中。由于Python与Linux、Windows和Mac跨平台兼容,.Net不是其核心原生支持。因此,我使用Iron Python来完成我的任务。(当然,您绝对可以使用PowerShell解码,但我能说什么——我想用Python做)
根据Iron Python网站,“IronPython是Python编程语言的开源实现,与.NET Framework紧密集成。IronPython可以使用.NET Framework和Python库,其他.NET语言也可以同样轻松地使用Python代码。“很整洁。在Windows上安装很简单——只是一个MSI。安装后,您只需运行调用ipy.exe的脚本(我稍后会展示一个示例)。
有了这个,我能够编写一些Python代码(io_decompress.py),使用python IO压缩库解压缩zip文件:
|
|
使用IronPython运行脚本很容易:ipy.exe io_decompress.py:
我能够打开脚本创建的decompressed.txt文件,并获得了以下纯文本PowerShell脚本。再次注意IP地址:
总结此事件日志条目采取的步骤:
- 解码Unicode base64
- 解码嵌入的base64代码
- 解压缩结果解码的base64代码
从上面的三个示例中我们看到,攻击者可能使用各种技术来混淆其PowerShell条目。这些可能以各种组合使用,其中一些我已在上文演示。每个案例采取的步骤各不相同,每个案例本身也是如此。我通常在每个案例中看到2-3种变体,在几个月内推送到数百个系统。有时步骤可能是:base64、base64、解压缩、shellcode。也可能是:base64、解压缩、base64、代码、base64、shellcode。看到这多么快变得像俄罗斯套娃了吗?当我结束这个系列时,我将讨论自动化该过程的方法。如果您使用像Harlan Carvy的时间线脚本这样的工具来获取文本输出,这会变得相当容易。
那么如何在您的检查中找到并解码这些?
- 查找事件日志ID 7045,包含”%COMSPEC%、powershell.exe、-encodedcommand、-w hidden、“From Base64String"等。
- 查找"Gzipstream"或”[IO.Compression.CompressionMode]::Decompress"以获取使用了什么类型压缩的提示
- 尝试通过sdbg.exe、shellcode2exe或其他恶意软件分析工具运行生成的二进制文件
第二部分将讨论注册表中的PowerShell,随后是第三部分关于PowerShell日志记录和从内存中提取信息。