预估稿酬:200RMB
投稿形式:发送电邮至linwei#,或登入网页版在线投稿
一、前言
PowerShell的身影无所不在,我近来也碰到过越来越多的恶意PowerShell脚本。为何功击者热衷于使用PowserShell?由于许多Windows版本中还会自带这个工具,但是该工具可以访问WMI以及.NetFramework的全部功能,也能在显存中执行恶意代码以逃避反病毒软件的查杀,对了,与PowerShell相关的日志记录也没有这么完备。
在对这类案例的剖析过程中,我发觉了多条线索可以证明某位功击者早已借助PowerShell施行过功击。这种线索包括已安装的服务、注册表条目以及c盘上的PowerShell文件。假如启用了日志记录功能,我也能找到许多有用的信息。本文的目的是为这些对PowerShell不是非常熟悉的安全剖析人员提供一份参考。在本文中linux 防恶意代码软件,我会向你们介绍怎样定位恶意PowerShell程序的一些经验linux学习论坛,同时也会介绍解码经过混淆的PowerShell脚本的一些技巧。这一系列文章共有三篇,本文是第一篇,未来几周我会相继推出后续文章。
二、Part1:以服务方式安装的PowerShell脚本
首先我想介绍我最喜欢的一个反例:怎么通过System风波日志发觉功击者将PowerShell脚本安装成本机服务。为了定位这类程序,我所做的第一件事情就是查找ID为7045的这些风波(EventID7045)。当系统中安装某个服务时,日志中都会出现这类风波。以服务方式安装的某个PowerShell脚本如下所示:
上图中我用红框标出了一些值得注意的信息:
1、服务名(ServiceName)为随机的字符串。
2、服务文件名(ServiceFileName)为%COMSPEC%,这个字符串为cmd.exe所对应的环境变量。
3、引用了powershell可执行文件。
4、包含经过Base64编码的数据。
这么,这类风波为何会出现在日志中?有多种方式可以做到这一点,其中一种方式就是使用外置的Windows服务控制管理器(ServiceControlManager)来创建服务,命令如下所示:
sc.execreateMyServicebinPath=%COMSPEC%powershell.exe-nop-whidden-encodedcommand
scstartMyService
上述命令会创建名为“MyService”的一个服务,“binPath=”选项拿来启动cmd.exe,前者会执行PowerShell代码。
有个有趣的信息须要导致我们的注意:以这些方法创建服务后,我们可能会得到一些错误信息。但是,这种错误信息并不意味着服务安装失败,缘由在于Windows希望安装“真实”的二补码程序,因而在等待这个“服务”时会出现“超时”,最终呈现错误信息。我也是经过测试才晓得这一点。在测试过程中,我使用该方式成功安装了一个回调shell,操作过程中Windows主机上会形成服务错误信息。如右图中,左图为我在功击虚拟机上启动的一个Metasploit会话,下图为安装了Windows7系统的虚拟机。其实Windows7主机会提示“服务没有及时响应启动或控制恳求(Theservicedidnotrespondtothestartorcontrolrequestinatimelyfashion)”,但并不影响Metasploit会话中成功打开大跌shell:
如下两图分别对应System风波日志中的7000以及7009风波。其实7009风波提示说“FakeDriver服务启动失败(TheFakeDriverservicefailedtostart)”linux cp,但这并不代表binPath变量中包含的命令执行失败。所以,假若按照这种信息判定PowerShell没有执行,我们可能会得到错误推论:
我们可以使用python来解码System日志中7045风波所包含的经过base64编码的PowerShell命令。有趣的一点是,这是一段Unicode编码的base64代码,因而在解压时我们须要指定额外的参数。(为了易于展示,我没有全部呈现所有base64文本,你须要在解码程序中包含所有的base64文本):
import
base64code="JABjACAAPQAgAEAAIgAKAFsARABsAGwASQBtAHAA...."
base64.b64decode(code).decode('UTF16')
解码后的PowerShell命令如下所示。快速查看代码后,我们可以找到一些有趣的线索,例如,我们可以在其中找到与NetSocket有关的TCP合同以及IP地址信息:
这段代码在功能上与使用Meterpreter拿来创建大跌shell类似。上述PowerShell代码解码上去十分容易,但是,许多场景下,我们面临的情况并没有这么简单。
接出来是另一个反例,这一次我们面对的是“普通”的base64代码。其中我们须要再度注意%COMSPEC%变量以及其中包含的powershell.exe字符串:
我们还是使用Python来解码这段base64编码的PowerShell代码:
这一次,解码后的结果没有这么直观。假如我们回过头好好观察一下System中的风波记录,我们可以发觉其中包含有关“Gzip”以及“Decompress”相关信息:
按照这种信息,我们可以逆向思索,这段数据可能先经过Gzip的压缩,之后再使用base64进行编码。为此,我决定使用python将base64的解码结果写入到文件中,便于进一步解压缩处理:
importbase64
code="H4sICCSPh1kCADEAT..."
decoded=base64.b64decode(code)
f=open("decoded.gzip",'wb')
f.write(decoded)
f.close
通过7zip,我成功解压了这个gzip文件!操作过程中没有出现任何错误信息,为此我使用的可能是正确的方式:
之后,我通过文本编辑器打开解压后的文件,希望能看见一些PowerShell代码:
哪些情况?好吧,是时侯祭出十六补码编辑器了:
仍然没有太大帮助。我猜想这应当是一段shellcode代码。下一步,我想通过PDFStreamDumper的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再度打开这个文件:
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因为核心机制缘由难以做到这一点。为此,我选择使用IronPython来解决这个问题(其实,你可以使用PowerShell来解压这段数据,但是我上面说过,我还是想用Python来完成这个任务)。
按照IronPython官网的说法,“IronPython是Python编程语言的开源实现方案,与.NETFramework密切相关。IronPython可以使用.NETFramework和Python库,但是其他.NET代码想要使用Python代码也十分简单”。在Windows上安装IronPython十分简单,只须要一个MSI文件即可。安装完毕后,你就可以使用ipy.exe来运行脚本(稍后我会给开具体事例)。
有了这个工具后,我就可以开发python代码(io_decompress.py),使用pythonIO压缩库来解压zip文件:
importrequired.Netlibraries
fromSystem.IOimportBinaryReader,StreamReader,MemoryStreamfromSystem.IO.CompressionimportCompressionMode,DeflateStreamfromSystemimportArray,BytefromSystem.IOimportFileStream,FileModefromSystem.TextimportEncodingfromSystem.IOimportFile
functionstodecompressthedata
defdecompress(data):iozip=DeflateStream(MemoryStream(data),CompressionMode.Decompress)str=StreamReader(iozip).ReadToEnd()io_zip.Close()returnstr
print"Decompressingstream..."compressedBytes=File.ReadAllBytes("decoded2.zip")decompressedString=decompress(compressedBytes)
f=open("decompressed.txt","wb")f.write(decompressedString)f.close()
通过IronPython来运行这段脚本也十分简单,命令为ipy.exeio_decompress.py,如下所示:
这段脚本生成了decompressed.txt文件,其中包含明文版的PowerShell脚本,如右图所示。我们须要注意其中包含的IP地址信息:
总结一下,为了从风波日志中得到正确结果,我经过了如下步骤:
1、解码Unicodebase64代码。
2、解码嵌套的base64代码。
3、解压解码后的base64代码。
三、总结
从前面这三个事例中,我们可以见到,功击者可能会使用各类技术来混淆她们的PowerShell功击代码。这种技术可以组合使用,其中一些用法我早已在里面事例中介绍过。对于每种情况,所采取的步骤常常也各不相同。我常常会听到每种案例会对应2到3种变化,但是会在几个月的时间内漫延到数百个系统中。个别情况下,我们所须要使用的步骤可能为:base64、base64、解压、shellcode,也有可能是base64、解压、base64、明文代码、base64、shellcode。这些情况是不是与日本套娃十分类似?在完成这一系列文章后,我会介绍怎么手动化处理这个过程。假如你使用过类似HarlanCarvy的时间线脚本来获取文本输出结果,这么整个过程处理上去会特别简单。
为此,我们怎么在自己的环境中定位并解码这种脚本?可以使用如下步骤:
1、查找带有%COMSPEC%、powershell.exe、-encodedcommand、-whidden、"FromBase64String"之类特点的7045风波。
2、查找例如Gzipstream或则[IO.Compression.CompressionMode]::Decompress之类的特点字符串,可以帮助我们了解代码所使用的压缩方式。
3、尝试使用sdbg.exe、shellcode2exe或其他恶意软件剖析工具来剖析得到的二补码文件。
在Part2中,我会介绍注册表中的PowerShell代码,在Part3中linux 防恶意代码软件,我会介绍怎么记录PowerShell日志,以及怎样从显存中提取相关信息。