恶意PowerShell脚本的检测与解码技术解析

本文深入探讨了如何通过系统事件日志检测恶意PowerShell脚本,详细解析了base64编码、Gzip压缩和多层混淆技术的解码方法,并提供了实用的Python解码脚本和取证分析技巧。

恶意PowerShell脚本的检测与解码

PowerShell无处不在。我开始遇到越来越多的恶意PowerShell脚本。

攻击者为何钟爱PowerShell?因为它原生支持多种Windows版本,提供对WMI和.Net Framework的完全访问权限,并且可以在内存中执行恶意代码从而规避杀毒软件。哦,对了——我是否还提到了缺乏日志记录?

在分析这类案例的过程中,我发现了攻击者使用PowerShell的多个迹象。这些包括已安装的服务、注册表项和磁盘上的PowerShell脚本。如果启用了日志记录,也能提供一些有用的痕迹。本文将从不太熟悉PowerShell的分析师视角出发,讨论在分析过程中如何定位恶意PowerShell痕迹,以及一些用于解码混淆PowerShell脚本的方法。这将是未来几周内编写的三部分系列文章的第一部分。

第一部分:作为服务安装的PowerShell脚本

首先是我最喜欢的方法——在系统事件日志中作为已安装服务发现的PowerShell脚本。要找到这些,我首先做的事情之一是查找事件ID 7045。该事件在系统上安装服务时发生。下面显示了一个作为服务安装的PowerShell脚本示例:

需要注意的红色标志包括:

  1. 随机服务名称
  2. 服务文件名包含"%COMSPEC%"(cmd.exe的环境变量)
  3. 对powershell可执行文件的引用
  4. Base64编码的数据

这样的条目是如何进入事件日志的?虽然有多种方法可以实现,但一种方法是使用内置的Windows服务控制管理器创建服务:

1
2
sc.exe create MyService binPath=%COMSPEC% powershell.exe -nop -w hidden -encodedcommand <insertbase64>
sc start MyService

上述命令创建名为"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文本才能解码它):

1
2
3
import base64
code="JABjACAAPQAgAEAAIgAKAFsARABsAGwASQBtAHAA...."
base64.b64decode(code).decode('UTF16')

解码后的PowerShell命令如下所示。快速扫描代码揭示了一些明显的迹象——引用使用TCP协议创建网络套接字和IP地址:

这类似于Meterpreter用于设置反向shell的代码类型。上面的PowerShell代码相当容易解码,但通常情况更为复杂。

接下来是另一个示例——这次只是"常规"base64。再次注意%COMSPEC%变量和对powershell.exe的引用:

同样,可以使用Python解码base64编码的PowerShell:

这次,解码后的输出不太有用。如果我们回头更仔细地查看系统事件日志条目,可以看到对"Gzip"和"Decompress"的引用:

啊…所以反过来思考,这些数据可能已使用Gzip压缩,然后使用base64编码。使用Python,我将把解码的base64写入文件,以便尝试解压缩:

1
2
3
4
5
6
import base64
code="H4sICCSPh1kCADEAT..."
decoded=base64.b64decode(code)
f=open("decoded.gzip",'wb')
f.write(decoded)
f.close

使用7zip我成功解压了gzip文件!由于没有收到任何错误,我可能走对了路:

现在,如果我使用文本编辑器打开解压后的文件,希望能看到一些PowerShell代码:

啊…什么???好吧——是时候用十六进制编辑器查看一下了:

也没什么帮助。我认为这可能是shellcode。作为下一步,我将通过PDF Stream Dumper的shellcode分析工具scdbg.exe运行它:

嗒哒!scdbg.exe能够从shellcode中为我提取一些IOC。

总结一下,我解码此PowerShell条目采取的步骤:

  1. 解码base64 PowerShell字符串
  2. 将解码的base64写入zip文件
  3. 使用7zip解压缩Gzip文件
  4. 通过scdbg.exe运行二进制输出

如上所示,在找到宝藏之前可能需要经过多个层。

最后一个示例:

这看起来很熟悉。第一步,解码Unicode base64得到以下结果——其中包含更多base64代码内的base64代码!

混淆,然后使用压缩再次混淆。这在我见过的案例中非常典型。这次因为压缩文本中没有"gzip"引用,我将把第二轮base64保存到常规zip文件,并尝试用7zip打开:

1
2
3
4
decoded2="nVPvT9swEP2ev+IUR...."
f=open("decoded2.zip,"wb")
f.write(base64.b64decode(decoded2))
f.close()

尝试用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文件:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#import required .Net libraries 
from System.IO import BinaryReader, StreamReader, MemoryStream
from System.IO.Compression import CompressionMode, DeflateStream
from System import Array, Byte
from System.IO import FileStream, FileMode
from System.Text import Encoding
from System.IO import File

#functions to decompress the data 
def decompress(data):
    io_zip = DeflateStream(MemoryStream(data), CompressionMode.Decompress)
    str = StreamReader(io_zip).ReadToEnd()
    io_zip.Close()
    return str

print "Decompressing stream..."
compressedBytes = File.ReadAllBytes("decoded2.zip")
decompressedString = decompress(compressedBytes)

f = open("decompressed.txt", "wb")
f.write(decompressedString)
f.close()

使用IronPython运行脚本很容易:ipy.exe io_decompress.py

我能够打开脚本创建的decompressed.txt文件,并获得了以下纯文本PowerShell脚本。再次注意IP地址:

总结此事件日志条目采取的步骤:

  1. 解码Unicode base64
  2. 解码嵌入的base64代码
  3. 解压缩结果解码的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日志记录和从内存中提取信息。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计