標籤:android seandroid selinux mac dac
與iOS相比,Android最被人詬病的是其流暢性和安全性。然而,從4.0開始,Android不遺餘力地改善其流暢性。特別是在即將發布的L版本中,用ART替換了Dalvik,相信會越來越流暢。至於安全性,Android也沒有遺忘。從4.3開始,Android引入了一套基於SELinux的安全機制,稱為SEAndroid,來加強系統安全性。接下來我們就對SEAndroid進行簡要介紹和制定學習計劃。
老羅的新浪微博:http://weibo.com/shengyangluo,歡迎關注!
在介紹SEAndroid安全機制之前,我們要先瞭解一下Android當前所採用的安全機制是什麼。實際上,在前面從NDK在非Root手機上的調試原理探討Android的安全機制一文中,我們已經介紹過Android系統的安全機制了。因此,這裡主要是進行一下總結。
在引進SEAndroid安全機制之前,Android系統的安全機制分為應用程式和核心兩個層級。應用程式層級的安全機制就是我們通常說的Permission機制。一個應用如果需要訪問一些系統敏感或者特權資源,那麼就必須要在AndroidManifest.xml設定檔中進行申請,並且在安裝的時候由使用者決定是否賦予相應的許可權。應用安裝過後,一般是通過系統服務來間接使用系統敏感或者特權資源的。這樣系統服務在代表應用使用這些資源之前,就會先檢查應用之前是否已經申請過相應的許可權。如果已經申請過,那麼就直接允許存取,否則的話,就拒絕執行。
核心層級的安全機制就是傳統的Linux UID/GID機制。在Linux中,每一個使用者都擁有一個使用者ID,並且也有一個使用者組ID,分別簡稱為UID和GID。此外,Linux系統的進程和檔案也有UID和GID的概念。Linux就是通過使用者、進程、檔案的UID/GID屬性來進行許可權管理的。
我們知道,當Linux核心啟動完成之後,啟動的第一個進程稱為init進程。Init進程接著會啟動一個login進程,等待使用者輸入使用者名稱和密碼登入。一旦登入成功,就會啟動一個shell進程。之後這個shell進程就負責執行使用者的命令。注意,在上述啟動過程中,init進程是以root使用者身份啟動的,也就是init進程的UID是0,即root。在預設情況下,由init進程啟動的所有進程,也就是fork出來的所有子進程,也同樣是以root身份啟動並執行。因此。負責登入的login進程的UID也是0。但是,當使用者輸入使用者名稱和密碼,並且登入成功後,所啟動的shell進程就不再是root了,而是成功登入系統的使用者。這是如何做到的呢?原來,具有root許可權的進程可以通過系統介面setuid來改變自己身份。也就是說,由login進程啟動的使用者shell進程在開始的時候的身份其實也是root的,不過在它可以執行使用者的命令之前,它已經通過setuid將自己的UID修改為登入使用者對應的UID了。這樣就可以限制每一個成功登入系統的使用者的許可權。
使用者之後在shell進程中執行的各種命令,要麼是在本進程中執行,要麼在啟動的子進程中執行。注意,根據我們上面的分析,由使用者shell進程啟動的子進程同樣是以登入使用者的身份啟動並執行,並且這些進程在啟動並執行過程中所建立的檔案的預設UID和GID也是和登入使用者的UID和GID一致的,並且這些檔案只可以被使用者自己訪問。如果一個使用者想將一些自己建立的檔案交給另外一個使用者訪問,那麼應該怎麼辦呢?Linux將檔案的許可權劃分為讀、寫和執行三種,分別用字母r、w和x表示。每一個檔案有三組讀、寫和執行許可權,分別是針對檔案的所有者、檔案所有者所屬的組以及除了所有者以及在所有者所屬組的使用者之外所有其它使用者。這樣,如果一個使用者想要將一個自己建立的檔案交給另外一個使用者訪問,那麼只需要相應地設定一下這個檔案的其它使用者權限位就可以了。
我們知道,Android是一個基於Linux核心的系統,但是它不像傳統的Linux系統,需要使用者登入之後才能使用。然而,Android系統又像傳統的Linux系統一樣有使用者的概念。只不過這些使用者不需要登入,也可以使用Android系統。具體來說,就是Android系統將每一個安裝在系統的APK都映射為一個不同的Linux使用者。也就是說,每一個APK都有一個對應的UID和GID。這些UID和GID是在APK安裝的時候由系統安裝服務PackageManagerService分配的。
我們知道,APK所運行在的進程是由另外一個系統服務ActivityManagerService負責啟動的。ActivityManagerService在啟動APK進程之前,會先向PackageManagerService查詢APK安裝時分配到的UID和GID。有了APK的UID和GID後,ActivityManagerService就向另外一個以root身份啟動並執行zygote進程發出建立APK進程的請求。Zygote進程收到請求之後,就會fork出一個子進程來作為請求建立的APK進程。APK進程的建立過程的詳細分析可以參考Android應用程式進程啟動過程的原始碼分析一文。
注意,我們上面提到,zygote進程是以root身份啟動並執行。因此,它fork出來的子進程,也就是APK進程,在一開始的時候也是以root身份啟動並執行。不過,APK進程在可以執行APK代碼之前,會通過系統介面setuid將自己的UID設定為APK安裝時分配到的UID。這個過程與傳統的Linux系統通過login進程啟動使用者shell進程的過程非常類似。通過這種方式,就可以保證每一個APK進程都以不同的身份來運行,從而保證了相互之間不會受到幹擾。這就是所謂的沙箱了,這完全是建立在Linux的UID和GID基礎上的。
以上分析的UID/GID機制可以通過圖1來描述:
圖1 Android系統基於UID/GID的安全機制
這種基於Linux UID/GID的安全機制存在什麼樣的問題呢?注意我們前面提到的,當一個使用者想將授予另外一個使用者訪問自己建立的檔案的時候,它只需要修改一下該檔案的存取權限位就行了。也就是說,在Linux系統中,檔案的許可權控制在所有者的手中。因此,這種許可權控制方式就稱為自主式的,正式的英文名稱為Discretionary Access Control,簡稱為DAC。
在理想情況下,DAC機制是沒有問題的。然而,在現實中,會產生嚴重的安全問題。例如,一個使用者可能會不小心將自己建立的檔案的許可權位錯誤地修改為允許其它使用者訪問。如果這個使用者是一個特權使用者,並且它錯誤操作的檔案是一個敏感的檔案,那麼就會產生嚴重的安全問題。這種誤操作的產生方式有三種:
1. 使用者執行了錯誤的命令
2. 負責執行使用者命令的程式有BUG
3. 負責執行使用者命令的程式受到攻擊
由此可見,DAC機制只能在理想情況下沒有問題,但是在現實中是防不勝防!例如,GingerBreak漏洞就是通過攻擊以root身份運行Android磁碟管理守護進程vold來獲得root許可權,從而實現對裝置進行root的。
注意,上面我們說的DAC問題雖然是針對核心層級的Linux UID/GID機制,然而同樣適用於應用層級的Permission機制。這個問題通過MasterKey漏洞表現得淋漓盡致。我們知道,MasterKey漏洞可以在不改變簽名的情況下對APK進行篡改。這會導致什麼後果呢?假如被篡改的APK申請有特殊的Permission,那麼就意味著嵌入的惡意代碼可以任意地使用這些特殊的Permission。
更為嚴重的是被篡改的APK是一個系統APK。Android系統的Permission分為兩種,一種是所有APK都可以申請的,另一種是系統APK才可以申請的。只有系統APK才可以申請的Permission更為敏感,例如用來安裝APK的Permission--android.permission.INSTALL_PACKAGES。這意味著一個具有android.permission.INSTALL_PACKAGES的系統APK被篡改後,惡意代碼就可以在裝置上安裝任意的惡意APK了。
事實上,應用層級的Permission機制也是建立在Linux UID/GID基礎上的。當我們在APK的AndroidManfest.xml設定檔中申請某一個Permission的時候,Android系統的安裝服務PackageManagerService除了會記錄它申請有相應的Permission之外(以便APK調用需要許可權的API介面時進行驗證),還會將APK加入到相應的某個Linux使用者組去。這是因為在Android系統中,並不是所有的特權操作都是間接地通過系統服務來執行的,例如網路訪問。一旦一個APK申請網路訪問的Permission,那麼它就會加入到Linux的網路使用者組去,這時候APK就可以通過建立socket來訪問網路了。
這樣,在Android系統中,無論是應用層級的Permission機制,還是核心層級的Linux UID/GID機制,都同樣會受到DAC問題的困擾。這時候我們就需要一種更為強有力的安全機制來保證系統的安全。
在存取控制模型中,與DAC機制相對的是MAC機制。MAC的全稱是Mandatory Access Control,翻譯為強制存取控制。在MAC機制中,使用者、進程或者檔案的許可權是由管理原則決定的,而不是由它們自主決定的。例如,我們可以設定這樣的一個管理原則,不允許使用者A將它建立的檔案F授予使用者B訪問。這樣無論使用者A如何修改檔案F的許可權位,使用者B都是無法訪問檔案F的。這種安全訪問模型可以強有力地保護系統的安全。我們在這個系列的檔案中要介紹的SEAndroid就是一種MAC機制。
在SEAndroid中,每一個進程和檔案都會關聯有一個安全上下文。這個安全上下文由使用者、角色、類型、安全層級四個部分組成,每一部分通過一個冒號來分隔。例如,u:r:t:s0描述的就是一個SEAndroid安全上下文。當每一個進程和檔案都關聯上一個安全上下文之後,系統管理員就可以基於這些安全上下文制定一個安全存取原則,用來規定什麼樣的進程可以訪問什麼樣的檔案。
上面描述的SEAndroid安全機制2所示:
圖2 SEAndroid安全機制
在圖2中,紅色標註的即為SEAndroid安全上下文。其中,u:r:unstructed_app:s0描述的是使用者安裝的APK所運行在的進程的安全上下文,而u:object_r:app_data_file:s0描述的是使用者安裝的APK在運行過程中產生的資料檔案的安全上下文。
從圖2還可以看到,SEAndroid安全機制與傳統的Linux UID/GID安全機制是並存關係的,也就是說,它們同時用來約束進程的許可權。當一個進程訪問一個檔案的時候,首先要通過基於UID/GID的DAC安全檢查,接著才有資格進入到基於SEAndroid的MAC安全檢查。只要其中的一個檢查不通過,那麼進程訪問檔案的請求就會被拒絕。上述的安全檢查過程3所示:
圖3 基於Linux UID/GID和SEAndroid的安全訪問流程
我們通過一個例子來說明上述的安全訪問流程。當我們想從手機上下載一個檔案到電腦上時,我們使用adb pull命令來實現。當我們執行adb pull命令的時候,實際上是由手機上的守護進程adbd來讀出指定的檔案,並且將讀出來的內容發送給在電腦上啟動並執行adb進程的。接下來,我們就按照以下步驟嘗試從啟用了SEAndroid的三星Note II上下載檔案/system/bin/gpsd到電腦上來。
1. 執行ls -l命令檢查手機上存在/system/bin/gpsd檔案,以及它基於傳統的Linux UID/GID的許可權位:
$ ./adb shell ls -l /system/bin/gpsd-rwxr-xr-x root shell 2822268 2014-02-11 03:27 gpsd
從命令的輸出可以看到,如果只考慮傳統的Linux UID/GID安全機制,手機上的/system/bin/gpsd檔案是所有使用者均可以讀取的。
2. 執行adb pull命令下載手機上的/system/bin/gpsd檔案:
$ ./adb pull /system/bin/gpsd ./gpsdfailed to copy ‘/system/bin/gpsd‘ to ‘./gpsd‘: Permission denied
從命令的輸出可以看到,我們沒有按照預期那樣將手機上的/system/bin/gpsd檔案下載電腦上來,原因是“Permission denied”,也就是許可權不夠。
3. 分別通過ls -Z和ps -Z命令檢查檔案/system/bin/gpsd和進程adbd的安全上下文:
./adb shell ls -Z /system/bin/gpsd-rwxr-xr-x root shell u:object_r:gpsd_exec:s0 gpsd$ ./adb shell ps -Z | grep ‘adbd‘u:r:adbd:s0 shell 1978 1 /sbin/adbd
從命令的輸出可以看到,檔案/system/bin/gpsd的安全上下文為u:object_r:gpsd_exec:s0,而進程adbd的安全上下文為u:r;adbd:s0,因此我們可以斷定,在三星Note II啟動並執行系統上,一定存在一個存取原則不允許安全上下文為u:r;adbd:s0的進程訪問安全上下文為u:object_r:gpsd_exec:s0的檔案。
從上面這個例子就可以看出,在啟用SEAndroid之前,原本可以訪問的檔案,到啟用SEAndroid之後,就變得不可以訪問了!如果我們確實是需要訪問這些檔案,例如我們需要將這些檔案打包在我們自己製作ROM裡面,那麼有沒有其它辦法訪問呢?當讀完SEAndroid安全機制這個系列的文章之後,你就會發現答案是肯定的,並且是在遵循SEAndroid安全性原則的前提下實現的!
關於SEAndroid安全機制,還有一個關鍵點是值得提及的。那就是SEAndroid安全機制的目的不是為了完全杜絕別人攻擊我們的裝置,而是為了保證我們的裝置受到攻擊時,受到的損害減少到最少的程度。例如,SEAndroid安全機制並不能完全阻止我們的裝置被root,但是它能保證我們的裝置被root之後,一些敏感的檔案仍然是不可訪問,這樣就可以最大程度地保護我們的裝置。這是因為只要程式是由人類來編寫的,就或多或少地存在BUG,或者說漏洞,特別是複雜的程式,進而就會被駭客利用,並且成功地侵入到我們的系統中來。這是防不勝防的。當然,我們並不是說SEAndroid對阻止裝置被侵入毫無用處,它在一定程度上還是能加大侵入的技術難度的。
由於SEAndroid的內容很多,足夠寫一本很厚很厚的書來描述,但是在接下來的文章中,老羅並不打算逐一逐一地介紹,而是主要抓住關鍵區段進行詳細的分析,因此希望同學們在繼續學習接下的文章之前,可以讀讀以下的一本書以及一篇論文:
1. SELinux by Example - Using Security Enhanced Linux
2. Security Enhanced (SE) Android: Bringing Flexible MAC to Android
事實上,接下來介紹SEAndroid的文章都是基於上面的論文來Security Enhanced (SE) Android: Bringing Flexible MAC to Android展開的,目的是從Android系統源碼分析的角度來闡述該論文的內容。
好了,廢話不說了。接下來我們就按照以下的情景來深入學習SEAndroid安全機制:
1. SEAndroid安全機制的基本概念分析
2. SEAndroid安全機制的檔案安全上下文分析
3. SEAndroid安全機制的進程安全上下文分析
4. SEAndroid安全機制的安全性原則管理分析
5. SEAndroid安全機制的Binde IPC保護支援分析
6. SEAndroid安全機制的Property保護支援分析
希望通過這六個情景的分析,使得我們對Android系統的安全機制有一個深刻的認識,以協助我們更好地保護手機上的隱私和資料,敬請關注!更多資訊可以關注老羅的新浪微博:http://weibo.com/shengyangluo。