2015年5月,奇虎360的研究人員公布了一份關于OceanLotus木馬的研究報告。在報告中,他們詳細的分析了這個攻擊了中國組織機構的木馬。報告中還介紹了一個針對OS X系統的木馬,這個木馬樣本在幾個月前被上傳到了VirusTotal上。有意思的是,截至2016年2月8日,VirusTotal上的55種殺毒解決方案仍然無法檢測出這個惡意樣本。因此,我們決定調查一番這個OS X版本的OceanLotus木馬。
OS X版本的OceanLotus是一個偽裝成Adobe Flash更新的應用程序包(Application Bundle)。在這個應用程序包中有很多不同的文件,下面是我們感興趣的幾個:
如下,EmptyApplication是一個通用二進制(universal binary),既可以在i386架構,也可以在x86_64架構下運行。這是一個非常簡單的程序,首先,這個程序會使用ROL3算法解碼出兩個“文件”: .en_icon
和DS_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算法的加密秘鑰,混淆了二進制中的字符串。下面是一個簡單的解密函數。
在64位版本中,8字節長度以內的字符串會保存成整數值(integer value)。超過8字節長度的字符串會加密儲存在相鄰變量中,解密函數在讀取變量時會以8個字節為界。如下所示,&v34被傳遞給了解密函數,但是,函數實際上解密了v34和v35組合。
在解碼了.en_icon
之后,EmptyApplication會將其寫到一個名稱是“pboard”的臨時目錄(可能是為了偽裝成OS X系統中的粘貼板守護進程),并執行二進制文件。然后,EmptyApplication會刪除自身,解碼.DS_Stores
,并把解碼得到的二進制寫為“EmptyApplication”-替換掉原來的EmptyApplication可執行文件。最后,通過調用NSTask.launch()
就可以啟動新的EmptyApplication。解密后的.DS_Stores
二進制在功能上與原有的EmptyApplication并沒有太多區別,只是新的EmptyApplication不會查找.DS_Stores
。
解碼后的.en_icon
文件就是主木馬。這個木馬具備反調試功能,能夠處理CC連接。后面我們會談到,這個木馬利用了幾個OS X命令和API調用,所以說,這個木馬很明顯是專門針對OS X制作的,而不是從其他系統上移植的。
還有一點,二進制中的大部分字符串都使用了XOR算法進行加密,但是,這個二進制使用了多個不同的秘鑰,并且這些秘鑰本身也經過了XOR加密。事實上,這個木馬做的第一件事就是解密幾個XOR秘鑰。有趣的是,用于設置解密秘鑰的代碼會通過使用C++靜態構造器(static constructor)在“main”入口點之前執行。這個代碼引用在mach-o二進制文件的__mod_init_func
部分。
從上圖中可以看出,整個可執行文件主要使用的解密秘鑰是“Variable”。但是,這里出現了幾個不同的"Variable" 字符串,這樣能夠方便木馬作者使用不同的解密秘鑰來更新代碼。雖然,XOR解密不難,但是,這種方案能夠增加逆向工程的繁瑣程度。下面這個解密函數與EmptyApplication使用的函數很類似,只不過,下面這個版本采用了一個變量解密秘鑰:
為了避免連接到調試程序,木馬使用了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服務器(C2)來獲取命令和其他有效載荷。木馬首先會使用HTTP連接端口80上的第一個C2:kiifd[.]pozon7[.]net。下面的例子就是一個check-in請求:
在這里,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。然后,木馬會收集下面的系統信息和當前用戶信息:
除了系統和用戶信息,木馬會根據www.microsoft.com獲取當前時間。為了獲取時間信息,木馬會發送一個HTTP請求到www.microsoft.com,并解析響應中的Data標頭。實際上,在請求中存在一個錯誤-發送到www.microsoft.com的請求是這樣的:
你會發現,在請求中沒有任何路徑,并且服務器響應了一個400。因為木馬只關心響應中的Data標頭,所以,這個有問題的請求也是可以用的。解析后的數據會轉換成epoch時間,并儲存在~/Library/Hash/.Hashtag/.hash (或/Library/Hash/.Hashtag/.hash)。在這里,代碼中還存在另一個錯誤,導致木馬會從~/Library/Hash/.hash中讀取時間信息,而真正的目錄中應該有.HashTag。除了時間戳,值“th”和1也儲存在這個文件中,所有的內容都使用了XOR加密,秘鑰是“camon”。
木馬會把系統信息和用戶信息發送到C2,并最后創建一個線程來處理C2發來的命令。下面的轉儲就是加密的C2通訊:
使用秘鑰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)。
除了幾個命令之外,這些命令的作用都是一目了然的。
LSSharedFileListCreate(0, kLSSharedFileListRecentDocumentItems, 0)
實現;CGWindowListCopyWindowInfo()
實現/usr/sbin/screencapture -x <PATH>
"(-x是為了避免截圖聲音)實現ps awx | awk '$1 == [PID] {print $5}
"實現,其中PID指的是當前進程ID除了上面的這些功能,還有一些命令代碼能夠允許C2執行下面的操作(有些命令和前面的有重合):
這個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
kiifd[.]pozon7[.]net
shop[.]ownpro[.]net
pad[.]werzo[.]net
/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)
#!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"
}
}
}