本文將分析的樣本是OSX.TinyShell的變種:TinyTim。早在2018年,我就發現有攻擊者正在使用Tiny SHell的改良版。這個後門(Tiny SHell,是開放源碼的)的運行方式類似於SSH的可疑版本。雖然我已經有一段時間沒有遇到新的示例了,但是我完全相信攻擊者仍然在使用它。
目前,尚未有專門的文章討論討論有關該惡意軟件的技術細節。這可能是因為實際上,它相對於其開源形式幾乎沒有什麼變化,但是這些修改的確使我們能夠通過多種方式檢測其獨特性。由於該惡意軟件已被惡意行為者修改,因此稱其為Tiny SHell似乎並不準確。因此,我將這個特定的修改版本稱為“TinyTim”(因為我開始嘗試在假期前後撰寫此博客文章,並將發佈拖到現在為止)。
本文中使用的樣本可以在VirusTotal上找到(SHA256:8029e7b12742d67fe13fcd53953e6b03ca4fa09b1d5755f8f8289eac08366efc)。
在VirusTotal頁面上,你會注意到許多反病毒掃描程序將其標記為OSX.Keydnap,但我不知道這種聯繫是如何產生的,因為我看不到它們之間的共同點。
發現過程
第一次觀察表明,這個惡意軟件是由開發人員簽名的。我不知道這是一個合法的被盜的簽名證書,還是它是由攻擊者創建和擁有的。這很有趣,因為惡意軟件通常僅用於攻擊Gatekeeper的簽名。正如我之前發現的那樣,這個惡意軟件是通過SSH使用受攻擊的憑證在受害系統上釋放的。也許攻擊者是有意使用帶符號的二進制代碼來進行混合(因為macOS上的大多數二進制代碼都是帶符號的)。使用macOS的代碼簽名實用程序,我們可以轉儲惡意軟件的代碼簽名信息:
如上所述,我們今天看到的這種變體是直接基於開源Tiny SHell後門的。Tiny SHell源代碼進行的主要更改之一是添加了一個稱為MyDecode的函數,攻擊者使用此函數對二進制文件內的某些“敏感”字符串進行編碼。如果我們想對這裏發生的事情有個更好的瞭解,就必須在Hopper等反彙編程序中打開它。
在main函數內部,第一個檢查顯示TinyTim添加了一些基本的反調試函數。在開始時,我們看到ptrace與ptrace_deny_attach參數一起使用,如果程序在附加到調試器時執行,ptrace_deny_attach參數將立即關閉程序,我們必須記住這一點。
在檢查調試器是否存在之後,它調用getuid,它返回執行程序的用户的用户ID。在這種情況下,惡意軟件會檢查根用户的UID是否為0。在這兩種情況下,MyDecode函數最終運行在一個看起來像是亂碼的字符串上。如果我們在左邊的標籤中選擇_MyDecode並按下x,則可以很好地看到引用此函數的所有位置:
main調用11個,tshd_runshell調用2個,顯然,這個惡意軟件經常依賴於這個函數。如果我們把Hopper視圖切換到偽代碼,我們會看到此函數實際上是非常基本的:
這裏要重點關注的關鍵項是r14 ^ r15,這是惡意軟件常見的兩個字節的簡單XOR運算。我們看到r14和r15的值是傳遞給該函數的第二個和第三個參數的值。此處傳遞的第一個參數是攻擊者想要取消屏蔽的字符串。如果回到主代碼,我們可以看一下攻擊者調用MyDecode時傳遞的值:
在前幾個調用中,我們看到MyDecode使用XOR模式0x4 ^ 0x2對每個字符串進行解碼。我們有一些選項可以將這些字符串轉換回可讀的文本。我們可以調試程序,也可以編寫簡單的腳本為我們解碼字符串。或者,我們可以同時做!讓我們從調試開始。
準備調試
在開始之前,我們必須採取以下步驟來準備這個可執行文件,以便它能夠運行。
1. 通過chmod x賦予TinyTim可執行文件權限;
2. 使用以下代碼刪除已撤銷的簽名:codesign --remove-signature;
3. 使用xattr -d com.apple.quarantine刪除隔離位(假設已下載此惡意軟件);
4. 刪除前面討論的ptrace調用,這是一種防調試技術,如果檢測到調試器,它將關閉該程序。我們可以通過在ptrace調用上放置一個斷點,然後跳過它來完成此操作,或者我們可以簡單地NOP它,因此我們不必每次運行時都擔心一個額外的斷點。
攻擊過程
現在,我們已經完成了所有的設置,我們可以在調試器中打開TinyTim並開始使用MyDecode函數。讓我們在函數末尾的返回處放一個斷點,然後啓動調試器:
當在調試器中命中斷點時,這意味着MyDecode函數剛剛完成運行。如果使用x / s $ rdx命令打印RDX寄存器,我們可以看到已解碼的字符串:
在本例中,我們看到已解碼的字符串是“/Users/%@/Library/Fonts/.cache”。請記住,我們是以基本用户的身份運行的,從我們在main中看到的情況來看,如果它作為根用户運行,將使用不同的路徑(請參見第一個屏幕快照中的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方案來獲得已解碼的字符。
現在我們無需使用調試器就可以輕鬆解碼:
太棒了!我們現在可以獲取存儲在可執行文件中的各種字符串,並以純文本的形式查看它們。繼續,我們可以嘗試創建一個配置文件來查看發生了什麼,但是我們不知道配置文件的格式,接下來看看是否可以解決這個問題。
發現配置格式的關鍵實際上是在getProfileString函數中,該函數僅引用fopen,fgets,fseek和fclose函數。這些函數通常用於打開、關閉和移動文件的不同內容。
我們可以看到fopen打開了指定為arg0的文件,在本例中,arg0是惡意軟件的配置文件。然後開始解析它。在文件的底部,我們看到sscanf正在使用某些特定的格式:
你可以使用sscanf函數在網上找一下,或者你可以使用我們應該已經使用過的方法,即使用GetProfileString函數,它可以準確地揭示我們所要查找的內容。
這裏我們有一個函數,它是來自Windows的某種類型的端口,允許用户讀取ini格式的配置文件。如果考慮一下我們前面看到的MyDecode函數的值,這是有意義的。這意味着所有大寫的項都是lpAppName值,小寫的項是lpKeyName值。當然,這在Mac上感覺有點不自然,因為這是Windows ini格式的一部分,但實際上它只是一個文本文件,這真的是一種格式嗎?這意味着我們的配置文件應該如下:
這裏使用的值當然是為我自己的測試而設置的,但是這種格式應該可以達到目的。一種簡單的確認方法是在getProfileString底部附近的strcpy函數上放置一個斷點,因為這個函數可能用於保存從配置文件中取出的字符串。一旦斷點被命中,我們就可以使用x/s $RDI打印RDI寄存器(當函數被調用時,RDI應該總是保持arg0)來顯示傳遞給strcpy函數的第一個參數,然後繼續到下一個斷點並重復。
使用正確的配置文件格式之後,我們將更接近於可操作的惡意軟件。然而,仍有一些懸而未決的問題。讓我們重新檢查放置在myDecode函數上的斷點,然後再次打印出每個解碼後的值。你還記得嗎,我們嘗試打印的第六個字符串是空字符串,讓我們看看是否有任何變化?
現在,解碼後的字符串顯示為749060607。請注意,該字符串在域字符串解碼後立即解碼。只需看一下就可以看出它與我們提供的localhost IP地址的長度相同-127.0.0.1。
如果我們使用我們編寫的myDecode.py腳本並在127.0.0.1上運行它,那麼我們是否可能得到749060607?
事實證明,我們在配置文件中使用的IP地址必須使用XOR方案進行編碼。這對於攻擊者而言是明智的。這樣可以確保即使找到配置文件,也無法以純文本方式找到命令和控制IP或域。如果他們使用的是已知的惡意IP地址,這還可以確保通過簡單的YARA規則無法提取他們使用的C2。因此,如果我們希望看到與該惡意軟件的成功連接,則必須確保首先對存儲在配置文件中的IP或域進行了相應的編碼。由於XOR是可逆的,並且我們已經知道所使用的方案,因此最終變得非常簡單。我們可以通過在python myDecode腳本中翻轉單個運算符來實現:
它以所需的掩碼格式提供了127.0.0.1,可以在我們更新配置文件後正確解碼:
現在你可能認為我們已經準備好連接回C2服務器了,然而,TinyTim還有另一個反調試技巧。如果我們再看一下偽代碼中的main函數,我們會注意到connect函數的調用依賴於一個非匹配的字符串比較。
讓我們在此strcmp函數上添加一個斷點,並通過打印寄存器RDI和RSI(傳遞給strcmp的第一個和第二個參數)來查看正在比較的內容:在連接到指定的C2之前,要進行一次檢查,以確保這不是試圖連接到運行惡意軟件的同一台計算機,這是惡意軟件開發者的另一個聰明舉動。有很多方法可以繞過這個問題。為了簡單起見,我將在VM中啓動微型Tiny SHell服務器,獲取該VM的本地IP地址,使用XOR模式重新詢問IP,並將其添加到配置文件中,這樣問題就解決了。運行TinyTim現在將創建到我的VM上的Tiny SHelll服務器的連接,這樣最後一個問題就解決了。
TinyTim想要一個密碼,這是意料之中的,因為在其開源形式Tiny SHell中,用户需要輸入密碼。但是,因為我們沒有在配置文件中看到指定的密碼選項,所以我們知道它必須存儲在可執行文件的某個地方。開源的Tiny SHell將該密碼稱為機密,在Hopper中,我們可以對秘密進行簡單的搜索:
現在,我們會看到一個XREF指向一個有趣的字符串' lcc,./3,我們可以嘗試使用此密碼作為密碼,但是與可執行文件中的其他所有字符串一樣,實際上已經對其進行了編碼,這很有可能會有所幫助。因此,我們將首先使用python腳本對其進行解碼:
一直以來,我們的目標都是讓惡意軟件連接到我們的C2服務器,以查看是否可以通過任何方式對其進行進一步修改。事實證明,從此以後,這種惡意軟件的行為就像開源的Tiny SHell。因此,主要的增加是編碼字符串,增加了用於快速更改的配置文件以及少量的反調試技術。與其繼續使用我們的反編譯器,不如僅僅看一下Tiny SHell客户端源代碼就更有意義了。