首页
论坛
课程
招聘
[翻译]针对日本的复杂Powershell恶意脚本分析
2019-6-9 17:10 5526

[翻译]针对日本的复杂Powershell恶意脚本分析

2019-6-9 17:10
5526

原文: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)

我们可以再次使用我们的python脚本来对字符串格式化技术进行去混淆(注意文件是ASCII格式,所以python脚本的编码要从utf_16改为utf_8)。我们可以进一步对代码进行一些美化,以方便理解它的功能。


图11.第七轮去混淆(阶段8)

在第44行,根据当前Windows的版本,调用v6B或v10A函数。3到27行与1到21行相似,只增加了一点内容。如果Windows版本为6,调用v6B。该函数首先尝试从两个URL(如果第一个无效则尝试第二个)下载一个图片。如果图片的尺寸是55555,然后在35到36行从图片中提取出一个嵌入的字符串。

图12.包含PowerShell代码的图片(信息隐藏技术)

JaromirHorejsi指出的,这个样本使用的信息隐藏技术与ursnif很相似(https://twitter.com/DissectMalware/status/1057518886709612546)。

图13.使用的信息隐藏技术与ursnif的相似

提取出的代码随后被Nice函数解密、解压缩(39行)。得到的base64编码的字符串经过解码后随即被执行(40行)。

https://i.postimg.cc/kn50Ph3h/6A.png?dl=1

https://i.imgur.com/wRli0qz.png

如果Windows版本不等于6,则调用函数v10A。首先,一个被base64编码的字符串被解码,产生的结果被[Reflection.Assembly]::Load函数加载。根据这个函数,

我们可以知道数据是一个.NET dll二进制。然后调用静态指定的函数Stefan.gavbo.pf()。


图14.嵌入的.NET dll

Pf函数下载另一个图片,见图13。

图15.pf函数下载的图片。包含PowerShell代码

从v6B下载的图片中提取出的代码如下图。

图16.从下载的图片中提取出的PowerShell代码

去混淆后为


图17.第一轮去混淆后,提取出的PowerShell脚本

再进行一轮去混淆后


图18.第二轮去混淆后,提取出的PowerShell脚本

再进行一轮去混淆后,我们得到:


图19.第三轮去混淆后,提取出的PowerShell脚本

代码将从下述URL下载另一个图片并提取、解压并执行其中嵌入的PowerShell代码。

https://images2.imgbox.com/25/39/dMnX3Y3Q_o.png

https://i.imgur.com/vwN9O7y.png

图20.脚本下载的包含PowerShell代码(信息隐藏技术)的图片

嵌入的代码为:


图21.从图片中提取出的代码


图22.展开嵌入的PE二进制文件

注意,如果你使用python脚本进行去混淆,确保注释掉了37行。

在图22的2415行,我们看到

$NiSs=$Ni."LC`Id"; $aCc=@(($niss %4),2,($niss %6),4,($niss %7),($niss %9),($niss %11),($niss %12),10,($niss %100),(($niss %50)+10),(($niss %50)-10),(($niss %800)+9));[byte[]]$MjT=$null;$sPKk=""+$NiSs;.("{0}{1}"-f 'E','du') ${glOBaL:`M`G`GG} ([system.text.encoding]::"As`cIi"."gEtBy`TES"($sPKk)) ([ref]$MjT) $aCc;${glOBaL:`M`G`GG}=([System.Text.Encoding]::"Ut`F8"."g`e`TsTrinG"($MjT))

$Ni在15行的结尾赋值($Ni=Get-Culture)。当前culture的语言id赋值给$NiSs ($Ni.LCId)。Ja-JP的语言id为1041。要使代码正常运行,我们要将其替换为这个值。

$NiSs=1041; $aCc=@(($niss %4),2,($niss %6),4,($niss %7),($niss %9),($niss %11),($niss %12),10,($niss %100),(($niss %50)+10),(($niss %50)-10),(($niss %800)+9));[byte[]]$MjT=$null;$sPKk=""+$NiSs;.("{0}{1}"-f 'E','du') ${glOBaL:`M`G`GG} ([system.text.encoding]::"As`cIi"."gEtBy`TES"($sPKk)) ([ref]$MjT) $aCc;${glOBaL:`M`G`GG}=([System.Text.Encoding]::"Ut`F8"."g`e`TsTrinG"($MjT))

现在我们可以调试代码并提取出嵌入的PE文件了。

引用

· Invoke-Obfuscation: PowerShell obFUsk8tion Techniques & How To (Try To) D""e`Tec`T'Th'+'em'(Invoke-Obfuscation:PowerShell混淆技术和如何去混淆)

· -f Format operator(-f格式化操作)

· DissectMalware/deobfuscate_string_format.py(解刨恶意软件、去混淆字符串格式的py文件)

· Language Code identifier(语言代码识别)

· Rfc2898DeriveBytes

· https://twitter.com/JaromirHorejsi/status/1103682604702806017

· https://twitter.com/DissectMalware/status/1057518886709612546



【公告】【iPhone 13、ipad、iWatch】11月15日中午12:00,看雪·众安 2021 KCTF秋季赛 正式开赛【攻击篇】!!!文末有惊喜~

收藏
点赞1
打赏
分享
最新回复 (1)
雪    币: 1744
活跃值: 活跃值 (538)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
chinasmu 活跃值 2019-6-10 01:30
2
0
技术含量很高的感觉
游客
登录 | 注册 方可回帖
返回