https://www.alienvault.com/open-threat-exchange/blog/oceanlotus-for-os-x-an-application-bundle-pretending-to-be-an-adobe-flash-update

2015年5月,奇虎360的研究人员公布了一份关于OceanLotus木马的研究报告。在报告中,他们详细的分析了这个攻击了中国组织机构的木马。报告中还介绍了一个针对OS X系统的木马,这个木马样本在几个月前被上传到了VirusTotal上。有意思的是,截至2016年2月8日,VirusTotal上的55种杀毒解决方案仍然无法检测出这个恶意样本。因此,我们决定调查一番这个OS X版本的OceanLotus木马。

0x00 分析


OS X版本的OceanLotus是一个伪装成Adobe Flash更新的应用程序包(Application Bundle)。在这个应用程序包中有很多不同的文件,下面是我们感兴趣的几个:

  • FlashUpdate.app/Contents/MacOS/EmptyApplication
  • FlashUpdate.app/Contents/Resources/en.lproj/.en_icon
  • FlashUpdate.app/Contents/Resources/en.lproj/.DS_Stores

0x01 载入程序(Loader)


如下,EmptyApplication是一个通用二进制(universal binary),既可以在i386架构,也可以在x86_64架构下运行。这是一个非常简单的程序,首先,这个程序会使用ROL3算法解码出两个“文件”: .en_iconDS_Stores;然后再执行这些文件。

#!bash
$file EmptyApplication
EmptyApplication: Mach-O universal binary with 2 architectures
EmptyApplication (for architecture x86_64): Mach-O 64-bit executable x86_64
EmptyApplication (for architecture i386): Mach-O executable i386

在混淆算法上,EmptyApplication使用了“xc”作为XOR算法的加密秘钥,混淆了二进制中的字符串。下面是一个简单的解密函数。

p1

在64位版本中,8字节长度以内的字符串会保存成整数值(integer value)。超过8字节长度的字符串会加密储存在相邻变量中,解密函数在读取变量时会以8个字节为界。如下所示,&v34被传递给了解密函数,但是,函数实际上解密了v34和v35组合。

p2

在解码了.en_icon之后,EmptyApplication会将其写到一个名称是“pboard”的临时目录(可能是为了伪装成OS X系统中的粘贴板守护进程),并执行二进制文件。然后,EmptyApplication会删除自身,解码.DS_Stores,并把解码得到的二进制写为“EmptyApplication”-替换掉原来的EmptyApplication可执行文件。最后,通过调用NSTask.launch()就可以启动新的EmptyApplication。解密后的.DS_Stores二进制在功能上与原有的EmptyApplication并没有太多区别,只是新的EmptyApplication不会查找.DS_Stores

0x02 木马


加密字符串

解码后的.en_icon文件就是主木马。这个木马具备反调试功能,能够处理CC连接。后面我们会谈到,这个木马利用了几个OS X命令和API调用,所以说,这个木马很明显是专门针对OS X制作的,而不是从其他系统上移植的。

还有一点,二进制中的大部分字符串都使用了XOR算法进行加密,但是,这个二进制使用了多个不同的秘钥,并且这些秘钥本身也经过了XOR加密。事实上,这个木马做的第一件事就是解密几个XOR秘钥。有趣的是,用于设置解密秘钥的代码会通过使用C++静态构造器(static constructor)在“main”入口点之前执行。这个代码引用在mach-o二进制文件的__mod_init_func部分。

p3

从上图中可以看出,整个可执行文件主要使用的解密秘钥是“Variable”。但是,这里出现了几个不同的”Variable” 字符串,这样能够方便木马作者使用不同的解密秘钥来更新代码。虽然,XOR解密不难,但是,这种方案能够增加逆向工程的繁琐程度。下面这个解密函数与EmptyApplication使用的函数很类似,只不过,下面这个版本采用了一个变量解密秘钥:

p4

反调试

为了避免连接到调试程序,木马使用了PT_DENY_ATTACH参数来调用ptrace()。此外,木马会创建一个signal handler来捕捉SIGTRAPs,调用“int 3”来投放一个SIGTRAPs,在SIGTRAP处理器中设置flag并在继续运行之前检查flag值。就反调试而言,这种方法非常有效。

接下来,在执行代码之前,木马会通过查看二进制文件的后27位字节,从而执行签名检查。在这27个字节中,前11字节必须匹配二进制的一个硬编码值,后16个字节必须是二进制的MD5哈希值减去这27个字节所得到的值。

木马维持

木马的第一个功能就是设置一个Launch Agent(启动代理)来维持木马-每次用户登录时,这个Launch Agent就会运行。木马会把自己复制到~/Library/Logs/.Logs/corevideosd(如果木马有root权限,则会复制到/Library/Logs/.Logs/corevideosd),并在~/Library/LaunchAgents/com.google.plugins.plist(或/Library/LaunchAgents/com.google.plugins.plist)中创建一个Launch Agent plist来引用corevideosd可执行文件。

除了使用“隐藏”目录,木马还会调用corevideosd文件和com.google.plugins.plist文件的chflags(文件名,UF_HIDDEN)。为了降低自己暴露的可能,木马最后还会调用’xattr -d -r com.apple.quarantine "PATH to corevideosd"‘ 来移除corevideosd文件上的审查扩展属性(quarantine extended attribute)。如果Launch Agent已经运行,在重启corevideosd之前,Launch Agent会使用命令“/bin/launchctl unload “/Library/LaunchAgents/com.google.plugins.plist”来卸载自己。

CC通讯

木马会尝试联系多个CC服务器(C2)来获取命令和其他有效载荷。木马首先会使用HTTP连接端口80上的第一个C2:kiifd[.]pozon7[.]net。下面的例子就是一个check-in请求:

p5

在这里,1AD6A35F4C2D73593912F9F9E1A55097是IOPlatformUUID的MD5哈希。IOPlatformUUID是通过执行下面的OS X命令获取到的:

#!bash
/usr/sbin/ioreg -rd1 -c IOPlatformExpertDevice | grep 'IOPlatformUUID'

这个UUID还会写到本地的~/Library/Preferences/.fDTYuRs。在写入磁盘之前,UUID还要经过XOR加密,使用的秘钥是“pth”。

目前,kiifd[.]pozon7[.]net已经下线了,但是如果C2能够联系到木马,木马就可以变更代码,从而下载和执行其他的有效载荷。木马克制运行一个可执行文件或打开一个压缩的应用程序包(.app应用)。

在联系了第一个C2后,木马会检查一个本地文件~/Library/Parallels/.cfg(或/Library/Parallels/.cfg),获取需要运行的可执行文件或应用列表。从本质上来说,~/Library/Parallels/.cfg是一个”启动项目(Startup Items)”文件,在这个文件中包含有木马首次启动时会运行的程序列表。虽然中方在报告中称,OceanLotus MAC木马能够检测出Parallels虚拟机,但是我们持不同意见。OceanLotus MAC只是简单地把隐藏的配置文件储存在了/Library/Parallels/目录下。

接下来,木马会请求连接一个“加密的”C2。首先,木马会尝试连接到shop[.]ownpro[.]net,但是,如果主机已经下线,木马就会再连接pad[.]werzo[.]net。木马的网络通讯是通过端口443实现的,但是没有使用SSL。相反,数据使用了一个单字节的XOR秘钥-0x1B。在初始请求阶段,受害者不会发送任何关于受害主机的信息。

在确定成功联系到C2时,木马会为处理C2发来的命令做准备。首先,木马会创建一个“保持活动”(keep-alive)线程,每分钟“ping”一次C2。然后,木马会收集下面的系统信息和当前用户信息:

  • 产品名称和版本(读取自/System/Library/CoreServices/SystemVersion.plist)
  • 机器名称
  • 是否是root用户
  • 用户的名称(User’s name,读取自pw_gecos)
  • 用户名(username)
  • IOPlatformUUID的MD5哈希(如果没有找到IOPlatformUUID,则使用用户名和机器名作为受害者的身份标识ID)

除了系统和用户信息,木马会根据www.microsoft.com获取当前时间。为了获取时间信息,木马会发送一个HTTP请求到www.microsoft.com,并解析响应中的Data标头。实际上,在请求中存在一个错误-发送到www.microsoft.com的请求是这样的:

p6

你会发现,在请求中没有任何路径,并且服务器响应了一个400。因为木马只关心响应中的Data标头,所以,这个有问题的请求也是可以用的。解析后的数据会转换成epoch时间,并储存在~/Library/Hash/.Hashtag/.hash (或/Library/Hash/.Hashtag/.hash)。在这里,代码中还存在另一个错误,导致木马会从~/Library/Hash/.hash中读取时间信息,而真正的目录中应该有.HashTag。除了时间戳,值“th”和1也储存在这个文件中,所有的内容都使用了XOR加密,秘钥是“camon”。

木马会把系统信息和用户信息发送到C2,并最后创建一个线程来处理C2发来的命令。下面的转储就是加密的C2通讯:

p7

使用秘钥0x1B解码了系统信息块后,我们得到了下面的数据-加粗的部分是产品名称,OS版本,用户名,机器名和IOPlatformUUID的MD5哈希。

\x02\x10\x00\x00\x00Mac OS X 10.10.5\x00\x02\x00\x00\x00av\t\x00\x00\x00lab _osx_1 \x00\x00\x001AD6A35F4C2D73593912F9F9E1A55097\xcb\xf2\x81V\x00\x00\x00\x00@\x00\x00\x00\x02\x00\x00\x00th\x00\x00\x00\x00

在向C2发送了系统信息和用户信息后,这个线程每秒都会尝试读取C2信息,但是,C2似乎每隔5秒才会发送一次数据。如果C2响应的数据中包含有命令指令,木马就会执行某条命令。下面的这些字符串是从二进制文件中解密获得的,很可能属于C2端的一个交互命令控制台(console)。

p8

除了几个命令之外,这些命令的作用都是一目了然的。

  • “exec”通过调用系统(“open ”)打开一个应用程序包(.app directory)
  • “info”返回关于文件或路径信息
  • “recent”返回近期打开的文档列表,通过调用LSSharedFileListCreate(0, kLSSharedFileListRecentDocumentItems, 0)实现;
  • “windows”返回系统上当前打开窗口的信息(比如,某个窗口的进程),通过调用CGWindowListCopyWindowInfo()实现
  • “capture”保存当前桌面截图到指定路径,通过命令 “/usr/sbin/screencapture -x <PATH>“(-x是为了避免截图声音)实现
  • “where”没有用法介绍,但是大家都认为这是一个命令,用于返回运行木马的完整路径,通过执行”ps awx | awk '$1 == [PID] {print $5}“实现,其中PID指的是当前进程ID

除了上面的这些功能,还有一些命令代码能够允许C2执行下面的操作(有些命令和前面的有重合):

  • 更新/Library/Hash/.Hashtag/.hash文件
  • 更新或读取 /Library/Parallels/.cfg文件
  • 自动从某个URL中下载文件
  • 解压或打开压缩的应用程序,运行某个可执行文件,或从某个动态库中执行代码
  • 杀死某个进程
  • 删除某个文件或路径
  • 断开C2连接

0x03 总结


这个OS X版本的OceanLotus木马很明显是一个专门针对OS X制作的成熟木马。对OS X命令和API的使用证明了木马作者非常精通OS X系统,并且木马作者用了相当多的时间来定制这个木马,以便让木马适应OS X环境。与其他先进的恶意软件类似,二进制混淆的使用表明木马作者想要保护自己的成果,增加逆向工程的难度并降低木马被检测到的概率。VirusTotal上的0检测率事实也说明木马作者做的很成功。

我们还发现了一个相对简单的OceanLotus木马版本。这版木马使用的C2仍然硬编码在二进制中,并通过端口80 连接kiifd[.]pozon7[.]net,但是,没有连接到加密C2上。这个版本也不能启动多线程来处理其他任务,所以我们认为这个木马可能是一个早期变种。所以,我们没有深入分析这个早期版本,不过,如果你想研究木马的发展历程的话,这个早期变种还是不错的研究对象。

App bundle

83cd03d4190ad7dd122de96d2cc1e29642ffc34c2a836dbc0e1b03e3b3b55cff

Another older variant that only communicates with the unencrypted C2

a3b568fe2154305b3caa1d9a3c42360eacfc13335aee10ac50ef4598e33eea07

C2s:

kiifd[.]pozon7[.]net

shop[.]ownpro[.]net

pad[.]werzo[.]net

Dropped Files:

/Library/.SystemPreferences/.prev/.ver.txt or ~/Library/.SystemPreferences/.prev/.ver.txt

/Library/Logs/.Logs/corevideosd or ~/Library/Logs/.Logs/corevideosd

/Library/LaunchAgents/com.google.plugins.plist or ~/Library/LaunchAgents/com.google.plugins.plist

/Library/Parallels/.cfg or /~Library/Parallels/.cfg

/tmp/crunzip.temp.XXXXXX (passed to mktemp(), so the actual file will vary)

~/Library/Preferences/.fDTYuRs

/Library/Hash/.Hashtag/.hash (or ~/Library/Hash/.Hashtag/.hash)

Detection

#!bash
Yara Rules

rule oceanlotus_xor_decode
{
        meta:
               author = "AlienVault Labs"
               type = "malware"
               description = "OceanLotus XOR decode function"
    strings:
        $xor_decode = { 89 D2 41 8A ?? ?? [0-1] 32 0? 88 ?? FF C2 [0-1] 39 ?A [0-1] 0F 43 D? 4? FF C? 48 FF C? [0-1] FF C? 75 E3 }
    condition:
        $xor_decode
}

rule oceanlotus_constants
{
        meta:
               author = "AlienVault Labs"
               type = "malware"
               description = "OceanLotus constants"
    strings:
        $c1 = { 3A 52 16 25 11 19 07 14 3D 08 0F }
        $c2 = { 0F 08 3D 14 07 19 11 25 16 52 3A }
    condition:
        any of them
}

#!bash
Osquery OceanLotus pack:
{
  "platform": "darwin",
  "version": "1.4.5",
  "queries": {
    "OceanLotus_launchagent": {
      "query" : "select * from launchd where name = 'com.google.plugins.plist';",
      "interval" : "10",
      "description" : "OceanLotus Launch Agent",
      "value" : "Artifact used by this malware"
    },
    "OceanLotus_dropped_file_1": {
      "query" : "select * from file where pattern = '/Users/%/Library/Logs/.Logs/corevideosd';",
      "interval" : "10",
      "description" : "OceanLotus dropped file",
      "value" : "Artifact used by this malware"
    },
    "OceanLotus_dropped_file_2": {
      "query" : "select * from file where path = '/Library/Logs/.Logs/corevideosd';",
      "interval" : "10",
      "description" : "OceanLotus dropped file",
      "value" : "Artifact used by this malware"
    },
    "OceanLotus_dropped_file_3": {
      "query" : "select * from file where pattern = '/Users/%/Library/.SystemPreferences/.prev/.ver.txt';",
      "interval" : "10",
      "description" : "OceanLotus dropped file",
      "value" : "Artifact used by this malware"
    },
    "OceanLotus_dropped_file_4": {
      "query" : "select * from file where path = '/Library/.SystemPreferences/.prev/.ver.txt';",
      "interval" : "10",
      "description" : "OceanLotus dropped file",
      "value" : "Artifact used by this malware"
    },
    "OceanLotus_dropped_file_5": {
      "query" : "select * from file where pattern = '/Users/%/Library/Parallels/.cfg';",
      "interval" : "10",
      "description" : "OceanLotus dropped file",
      "value" : "Artifact used by this malware"
    },
    "OceanLotus_dropped_file_6": {
      "query" : "select * from file where path = '/Library/Parallels/.cfg';",
      "interval" : "10",
      "description" : "OceanLotus dropped file",
      "value" : "Artifact used by this malware"

    },
    "OceanLotus_dropped_file_7": {
      "query" : "select * from file where pattern = '/Users/%/Library/Preferences/.fDTYuRs';",
      "interval" : "10",
      "description" : "OceanLotus dropped file",
      "value" : "Artifact used by this malware"
    },
    "OceanLotus_dropped_file_8": {
      "query" : "select * from file where pattern = '/Users/%/Library/Hash/.Hashtag/.hash';",
      "interval" : "10",
      "description" : "OceanLotus dropped file",
      "value" : "Artifact used by this malware"
    },
    "OceanLotus_dropped_file_9": {
      "query" : "select * from file where path = '/Library/Hash/.Hashtag/.hash';",
      "interval" : "10",
      "description" : "OceanLotus dropped file",
      "value" : "Artifact used by this malware"
    },
    "OceanLotus_dropped_file_10": {
      "query" : "select * from file where pattern = '/Users/%/Library/Hash/.hash';",
      "interval" : "10",
      "description" : "OceanLotus dropped file",
      "value" : "Artifact used by this malware"
    },
    "OceanLotus_dropped_file_11": {
      "query" : "select * from file where path = '/Library/Hash/.hash';",
      "interval" : "10",
      "description" : "OceanLotus dropped file",
      "value" : "Artifact used by this malware"
    },
    "OceanLotus_dropped_file_12": {
      "query" : "select * from file where path = '/tmp/crunzip.temp.%';",
      "interval" : "10",
      "description" : "OceanLotus dropped file",
      "value" : "Artifact used by this malware"
    }
  }
}