原文:https://inquest.net/blog/2019/03/09/Analyzing-Sophisticated-PowerShell-Targeting-Japan
翻译:玉林小学生
校对:StrokMitream
本文,我们详细分析一个针对日本用户的多阶段恶意Powershell脚本。我们3 月 7 日在HybridAnalysis上发现该例攻击。这是一个独特的样本,因为它使用了多层混淆、加密、信息隐藏来让payload躲避检测。直到写这篇文章的时候,VirusTotal上没有一款反病毒软件能检测到这个攻击。
这个将要分析的样本最初在3 月 7 日出现在我们的RADAR。最初样本及相关报告为:
博客发布时,VirusTotal上没有一款反病毒软件检测到该样本(0/57),在深入分析过程中我们会发现为什么会这样。
前言
本节,我们回顾一下该恶意软件频繁用到的混淆技术。该混淆大多基于Powershell中的字符串格式化操作(-f)、转义字符(`)来混淆最终的payload。这些技术Daniel Bohannon已经在Invoke-Obfuscation: PowerShell obFUsk8tion Techniques & How To (Try To) D""e`Tec`T'Th'+'em'中介绍过,并且已经在他的Invoke-Obfuscation框架中实现了。
Powershell中基本的字符串格式化操作通过-f操作实现。下面几行简单解释下资料中描述的该操作的语法:
跟随着占位符-f的以,分隔的一个字符串列表
占位符以{索引,偏移:格式}的格式出现,索引是-f操作后面的字符串值里的索引。偏移和格式在进行混淆是通常不用,我们在这里忽略它们。
样例:
看下从主样本中摘出的如下PowerShell语句:
"{2}{1}{0}" -f ']','te[','By'
执行-f操作后,我们得到’Byte[]’(By位于索引2,te[位于索引1,]位于索引0)。
`用于将在(校对:对)PowerShell中有特殊意义的字符进行转义。例如,如果你想要在字符串place " first中使用”,你需要在它前面放置`。所以你要写成place `”first。
在普通字符前出现的转义字符不会改变普通字符的意义。也就是说,转义字符被忽略。
样例,分析从样本中提取出的下面片段:
"lOAD`WiThPart`iAlN`AmE"
其中`W/`i/`A为W/i/A,所以字符串为lOADWiThPartiAlNAmE。
去混淆、去加密
本节中,我们根据之前讨论的混淆技术一层一层除去混淆层。你也可以使用后面提供的我们编写的Python脚本来基于这些技术自动对编码后的PowerShell脚本进行去混淆。
图1. 最初的PowerShell脚本
图1是我们从HybridAnalysis得到的最初PowerShell脚本。第一行的灰色字符用于混淆lOADWiThPartiAlNAmE。后面的字符串格式化混淆技术用于混淆System.Security和Out-Null字符串。
要进行去混淆,可以使用我们开发的可以处理这两类混淆技术的python脚本。
python bash-deobfuscator.py -f obfuscated.powershell.script.ps1
图2是对原始脚本进行第一次去混淆后的结果。
图2. 第一轮去混淆后的代码(阶段一)
在第二行,Set-Alias (sa)命令用于创建一个新的别名,DF用于代替new-object。
代码包含两个函数(第3行到第18行,第19行到第21行)。在21行,我们看到:
${ZAE} = (&("get-culture"))."pAreNT"."nAmE"[0];
Get-Culture命令获取当前系统的culture 配置。Name域源于RFC 4646。你可以在这个Github仓库看到完整的语言代码。Name域的第一个字符分配给ZAE变量。
22行调用 pLank函数,传递的 2 个参数为 miss 和 Colss 。mlss为一个长加密字符串,colSs为ZAE。之后在第10和11行,colSs被作为第一个参数传递给Rfc2898DeriveBytes类的构造函数;产生的类用于创建两个秘钥,DcZ和DeFs。这些秘钥之后传递给在第4行创建的RijndaelManaged 对象的CreateDecryptor(DcZ作为key,DeFs作为IV)方法。
简单说,pLank函数使用第二个参数构造的秘钥解密第一个参数。第二个参数是一个英文小写字母。通过挨个测试这些字母,我们发现字母j会解密出有意义的内容。Ja-JP是唯一以j开头的语言码。因此,该恶意软件针对当前culture设置为ja-JP的系统。可以合理地假设这些系统都位于日本。所以我们相信该恶意软件的目标是日本用户和系统。
最后,解密后的代码在第25行被执行。
为了获取解密后的脚本,我们使用Windows 10下默认安装的PowerShell ISE调试器。我们再修改22行为${ZAE} ='j',修改25行阻止执行恶意软件并在25行设一个断点。下面的gif文件展示如何进行调试操作。
图3. 调试PowerShell代码来解密payload
图4.展示从25行获得的经过解密和解压缩的脚本。
图4. 第一轮解密后的payload(阶段二)
在红线部分,GV是Get-Variable的别名。Get-Variable '*mDR*'与MaximumDriveCount变量匹配。"MaximumDriveCount"[3,11,2]是['i','e','x']并应有-join操作,最终得到字符串iex。
图5.Get-Variable '*mDR*'语句的输出
执行剩余代码将得到一个字符串。长字符串首先被frOMbaSe64stRING函数解码,然后使用DEFlATeSTREam进行解压缩。最终,结果被转换为一个ASCII字符串。我们可以通过在PowerShell或PowerShell ISE运行这部分代码来解除这一层。我们可以通过删除图4中标红部分来进行,最终获得图6所示代码。
图6. 第二轮去混淆(阶段3)
这阶段不再依赖变量,而变成依赖环境变量来构建iex字符串。在其余命令中,使用数字异或0x0c然后转换成字符并拼接成字符串的方式(这部分很慢,很耗时)。接着产生的字符串通过iex被执行。
图7.第三轮去混淆(阶段4)
这阶段中,构造一个字符串并通过管道命令“|”传给iex(标红部分)。$PSHome是一个预定义的变量,指向PowerShell主目录。
要构建字符串,每一个数字(二进制形式)要先转换为字符串,然后转换为int16,然后转换为一个字符。最终,所有产生的字符拼接在一起组成最后的字符串。
图8.第四轮去混淆(阶段5)
重申一下,标红部分用于构建iex字符串。这个脚本的其余部分中,使用格式化操作来构建一个字符串。然后使用替换函数(标蓝部分)将一些字符和子串替换为另一些字符。最终字符串见图9。
图9.第五轮去混淆(阶段6)
这个脚本采用相同的技术,这一阶段去混淆后,我们得到:
图10.第六轮去混淆(阶段7)
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)