深入分析已出现在APT攻击样本中的一个有趣的macOS后门
2020-05-27 10:40:00 Author: www.4hou.com(查看原文) 阅读量:421 收藏

早在2018年,我就针对Mac遇到的各种APT攻击进行了一次总结。其中,我专门提到了攻击者正在使用的后门,它是Tinyshell的修改版。 Tiny Shell是一款轻量级的标准远程Shell工具,可以提供远程执行命令(包括:Rlogin,Telnet,Ssh等)和文件传输功能(上传、下载),支持单字节,完全支持pseudo-Terminal Pairs(pty/tty)等伪终端,其运行方式类似于SSH的一个未知版本。由于该恶意软件已经被恶意行为者修改,因此称其为Tinyshell似乎并不准确。因此,我将这个特定的修改版本称为TinyTim,本文中使用的样本可以在VirusTotal 上找到。

SHA256:8029e7b12742d67fe13fcd53953e6b03ca4fa09b1d5755f8f8289eac08366efc

在VirusTotal页面上,你会注意到许多杀毒软件都将其标记为Keydnap。2016年,有安全公司发现了一例针对Mac 用户的恶意木马程序 OSX/Keydnap,该恶意程序窃取苹果系统密码信息并驻留于Mac OS系统中成为后门木马。其实,我自己也想知道这种联系是如何产生的,但我还没有发现什么线索。

发现过程

初步观察表明,该恶意软件是由开发人员签名的。不过我还不知道这是一个合法的偷来的签名证书,还是由攻击者创建和拥有的。这很有趣,因为恶意软件通常仅是为了攻击Gatekeeper而进行签名的。正如我在开头的文件中提到的那样,这个恶意软件是通过SSH使用被攻击的凭据在受害系统上释放的。也许攻击者是故意使用带符号的二进制代码,以便在发现二进制文件时将其混入。

1.png

Tinyshell源代码的主要变化之一是添加了一个名为“MyDecode()”的函数。这个函数允许攻击者将一些暴露的字符串编码到二进制文件中。如果我们想更好地了解此时发生的情况,就必须在诸如Hopper这样的反汇编程序中打开它。

2.png

在主函数内部,第一次检查显示TinyTim添加了一些基本的反调试功能。在开始时,我们看到ptrace与ptrace_deny_attach参数一起使用,如果在连接调试器时执行该程序,该参数将立即关闭程序,我们将必须牢记这一点。

在检查调试器是否存在之后,它将继续运行getuid(),然后返回执行程序的用户的用户ID。在本例中,恶意软件正在检查属于根用户的UID是否为0。在这两种情况下,MyDecode函数最终都在一个看起来像是乱码的字符串上运行。如果我们在左边的标签中选择_MyDecode并按下' x ',则可以很好地看到所有引用此函数的地方。

3.png

可以看到来自main的11个调用和来自tshd_runshell的2个调用,显然,此恶意软件经常依赖此功能。如果将Hopper视图切换为伪代码,我们就会发现这个函数实际上是非常基本的。

4.png

现在要关注的关键项是r14 ^ r15,这是恶意软件常见的两个字节的简单异或运算。在上图中,我们可以看到r14和r15的值是传递给该函数的第二个和第三个参数的值。这里传递的第一个参数是攻击者想要解密的字符串。如果我们回到主代码,则可以看看攻击者在调用MyDecode时传递了什么值。

5.png

在前几个调用中,我们看到MyDecode使用XOR方案0x4 ^ 0x2对每个字符串进行解码。我们有一些选项可以将这些字符串转换回可读的文本。我们可以调试程序,或者编写一个简单的脚本来解码字符串,或者,我们可以两者都做!现在,让我们开始调试。

准备调试

在开始之前,我们必须通过执行以下步骤来准备此可执行文件,这样它才能运行……

1. 使用chmod + x赋予TinyTim可执行文件权限;

2. 使用codesign --remove-signature删除已撤销的签名;

3. 使用xattr -d com.apple.quarantine删除隔离位(假设这个恶意软件已经被下载)。

如果删除前面讨论的ptrace()调用,这将是一种反调试技术,如果检测到调试器的存在,它将关闭这个程序。我们可以通过在ptrace调用上放置一个断点,然后跳过它来做到这一点,或者我们可以简单地NOP它,这样我们就不必在每次运行它时都担心一个额外的断点。

6.png

调试

现在,我们已经完成了所有设置,可以在调试器中打开TinyTim并开始使用MyDecode()函数。让我们在函数末尾的返回值上放置一个断点,然后启动调试器。

7.png

当在调试器中命中断点时,这意味着MyDecode函数刚刚完成运行。如果我们使用x/s $ RDX命令打印RDX寄存器,我们可以看到被解码的字符串。

8.png

在本例中,我们看到已解码的字符串是“/Users/%@/Library/Fonts/.cache”。请记住,我们是以基本用户的身份运行的,从我们在main中看到的情况来看,如果它以root用户的身份运行,则会使用不同的路径(参见第一个屏幕快照中的if/else语句)。我们可以继续“跳转到下一个断点”并打印每个字符串。所以,结果并不令人惊讶。

0x10000c260: “/Users/%@/Library/Fonts/.cache”
0x7ffeefbffa40: “PROG_INFO”
0x7ffeefbffa50: “name_masq”
0x7ffeefbffa60: “CONN_INFO”
0x7ffeefbffb28: “domain”
0x7ffeefbffa70: “”
0x7ffeefbffa70: “next_time”

大多数安全分析人员将上述字符串视为后门配置选项,这些选项可能是从“/Users/%@/Library/Fonts/.cache”文件中读取的。但是,这些配置都没有成功读取,因为我们没有在指定的位置创建配置文件。还要注意,解码的字符串中有一个是空的。这有点奇怪,但我们稍后会再讨论。让我们一起来破解一些快速的python代码,这些代码也可以解密这些字符串,因为这样做不会带来什么副作用。这里没有什么复杂的知识,我们只需要遍历提供的字符串中的每个字符,并在其上运行XOR方案来获得解码的字符。

10.png

现在我们无需使用调试器即可容易地解码:

11.png

现在,我们可以获取存储在可执行文件中的各种字符串,并以纯文本格式查看它们。继续,我们可以尝试创建一个配置文件来查看会发生什么,但是我们不知道该配置文件的格式。下面,让我们看看是否可以解决这个问题。

发现配置格式的关键实际上是在getProfileString()函数中,这个函数只引用fopen()、fgets()、fseek()和fclose()函数,这些函数通常用于打开、关闭和移动文件的不同内容。

12.png

我们可以看到fopen打开指定为arg0的文件,在本例中,arg0是恶意软件的配置文件。然后,开始解析它。在文件的底部,我们看到sscanf()与一些特定的格式一起使用。

13.png

你可以使用sscanf函数来尝试能在网上找出发生了什么,或者你可以执行我们应该已经执行的操作,只需使用GetProfileString()函数在网上进行搜索,它可以准确地揭示我们正在寻找的内容。

14.png

上图中有一个函数,它是来自Windows的某种类型的端口,已允许用户读取ini格式的配置文件。如果你回顾一下我们前面看到的myDecode()函数产生的值,则这很有意义。这意味着所有大写的项都是lpAppName值,小写的项是lpKeyName值。当然,这在Mac上感觉有点不自然,因为这是Windows .ini格式的一部分。但实际上它只是一个文本文件,这真的是一种格式吗?这意味着我们的配置文件应该类似于以下内容:

15.png

这里使用的值当然是为我自己的测试而设置的,但是这种格式应该可以达到目的。一种简单的确认方法是在getProfileString()的底部附近的strcpy()函数上放置一个断点,因为该函数可能用于保存从配置文件中取出的字符串。一旦遇到断点,我们就可以使用“x/s $RDI”来打印$RDI寄存器(当调用函数时,RDI应该总是保持arg0)来显示传递给strcpy函数的第一个参数,然后继续执行下一个断点并重复。

16.png

使用正确的配置文件格式之后,我们将更接近于可操作的恶意软件。然而,仍有一些悬而未决的问题。让我们重新查看放置在myDecode函数上的断点,并再次打印出每个解码值。如果你还记得,我们试图打印的第六个字符串是空字符串。现在,看看这里有没有变化。

17.png

解码后的字符串现在显示为“749060607”,请注意,该字符串是在“域”字符串解码之后立即解码的。只需看一下,我们就可以知道它与我们提供的本地主机IP地址——127.0.0.1的长度相同。

使用我们编写的myDecode.py脚本并在127.0.0.1上运行它,看看是否有可能得到“749060607”?

18.png

结果果然与预料的一样,因此,我们在配置文件中使用的IP地址必须使用XOR方案进行编码。这对攻击者来说是明智的,它确保即使配置文件被找到,命令和控制IP或域也不能在纯文本中被发现。它还可以确保如果他们使用的是已知的恶意IP地址,则不会通过简单的YARA规则提取他们使用的C2。具体信息,请点此获取更多详细信息。因此,如果我们希望看到与该恶意软件的成功连接,则必须确保首先对存储在配置文件中的IP或域进行相应的编码。由于XOR是可逆的,而且我们已经知道了正在使用的方案,所以它最终会非常简单。我们可以通过在python myDecode脚本中翻转一个操作符来实现,从ascii = (ord(x) + 0x4) ^ 0x2翻转到ascii = (ord(x) - 0x4) ^ 0x2。

这样就为我们提供了所需的屏蔽格式的127.0.0.1,可以在更新配置文件后正确对其进行解码。

19.png

现在你可能认为我们已经准备好连接到C2服务器了,然而,TinyTim还有另一个反调试技巧。如果我们在伪代码中再次查看main()函数,我们会注意到调用connect()函数依赖于一个非匹配的字符串比较。

20.png

让我们在这个strcmp函数上添加一个断点,然后通过打印寄存器RDI和RSI(传递给strcmp的第一个和第二个参数)来查看正在比较的内容。

21.png

当然,在连接到指定的C2之前,要进行一次检查,以确保这不是试图连接到正在运行恶意软件的同一台计算机。而这,恰恰也是恶意软件开发者非常聪明的地方。不过有很多方法可以解决此问题,为简单起见,我将在VM内启动Tinyshell服务器,获取该VM的本地IP地址,使用XOR方案重新询问IP,然后将其添加到配置文件中。这样,问题就解决了。现在,运行的TinyTim现在将在我的VM上创建一个到我的Tinyshell服务器的连接。

22.png

不过,TinyTim需要密码,这是预料之中的,因为Tinyshell以其开源形式要求用户输入密码。但是,因为我们没有在配置文件中看到指定的密码选项,所以我们知道它必须存储在可执行文件的某个地方。开源的Tinyshell将密码称为“secret”。在Hopper中,我们可以简单的搜索“secret”。

23.png

现在,我们看到一个XREF指向一个有趣的字符串“`lcc ,./3”,我们可以试着用这个作为密码,但是与可执行文件中的其他所有字符串一样,实际进行编码的可能性很大。因此,我们将首先使用python脚本对其进行解码。

24.png

密码似乎是free&2015:

25.png

现在我们就可以开始进行攻击测试了,一直以来,我们的目标都是让恶意软件连接到我们的C2服务器,以查看是否可以通过任何方式对其进行进一步修改。事实证明,在经过以上的处理后,这种恶意软件的行为就像开源的Tinyshell一样。因此,主要增加的是编码字符串、用于快速更改的配置文件和一些反调试技术。与其继续使用我们的反编译器,不如仅查看Tinyshell客户端源代码更有意义。

本文翻译自:https://themittenmac.com/tinyshell-under-the-microscope/如若转载,请注明原文地址:


文章来源: https://www.4hou.com/posts/g6J6
如有侵权请联系:admin#unsafe.sh