最近都在忙著研究Android的安全攻防技術(shù),好長一段時間沒有寫博客了,,準備回歸老本行中--Read the funcking android source code,。這兩天在看NDK文檔的時候,,看到一句話“Native debugging ... does not require root or privileged access, aslong as your application is debuggable”,。咦,,NDK調(diào)試不就是通過ptrace來實現(xiàn)調(diào)試的么,?在非Root的手機上是怎么進行ptrace的呢,?借這兩個問題正好可以介紹一下Android的安全機制,。 老羅的新浪微博:http://weibo.com/shengyangluo,歡迎關(guān)注,! Android是一個基于Linux內(nèi)核的移動操作系統(tǒng),。Linux是一個支持多用戶的系統(tǒng),系統(tǒng)中的文件的訪問權(quán)限是通過用戶ID(UID)和用戶組ID(GID)來控制的,。換句話說,,就是Linux的安全機制是基于UID和GID來實現(xiàn)的。Android在Linux內(nèi)核提供的基于UID和GID的安全機制的基礎(chǔ)上,,又實現(xiàn)了一套稱為Permission的安全機制,,如圖1所示: 圖1 Linux的UID/GID安全機制與Android的Permission安全機制 那么,這兩個安全機制是如何對應起來的呢,? 我們首先看一下Linux基于UID和GID的安全機制,,它包含三個基本角色:用戶、進程和文件,,如圖2所示: 圖2 Linux基于UID/GID的安全機制的三個角色 Linux中的每一個用戶都分配有一個UID,,然后所有的用戶又按組來進劃分,每一個用戶組都分配有一個GID,。注意,,一個用戶可以屬于多個用戶組,也就是說,,一個UID可以對應多個GID,。在一個用戶所對應的用戶組中,其中有一個稱為主用戶組,,其它的稱為補充用戶組,。 Linux中的每一個文件都具有三種權(quán)限:Read、Write和Execute,。這三種權(quán)限又按照用戶屬性劃分為三組:Owner,、Group和Other,。如圖3所示: 圖3 Linux的文件權(quán)限劃分 從圖3就可以看出文件acct:1. 所有者為root,,可讀可寫可執(zhí)行;2. 所有者所屬的主用戶組為root,,在這個組中的其它用戶可讀可執(zhí)行,;3. 其余的用戶可讀可執(zhí)行。 Linux中的每一個進程都關(guān)聯(lián)有一個用戶,也就是對應有一個UID,,如圖4所示: 圖4 Linux的進程 由于每一個用戶都對應有一個主用戶組,,以及若干個補充用戶組,因此,,每一個進程除了有一個對應的UID之外,,還對應有一個主GID,以及若干個Supplementary GIDs,。這些UID和GID就決定了一個進程所能訪問的文件或者所能調(diào)用的系統(tǒng)API,。例如,在圖4中,,PID為340的進程一般來說,,就只能訪問所有者為u0_a19的文件。 一個進程的UID是怎么來的呢,?在默認情況下,,就等于創(chuàng)建它的進程的UID,也就是它的父進程的UID,。Linux的第一個進程是init進程,,它是由內(nèi)核在啟動完成后創(chuàng)建的,它的UID是root,。然后系統(tǒng)中的所有其它進程都是直接由init進程或者間接由init進程的子進程來創(chuàng)建,。所以默認情況下,系統(tǒng)的所有進程的UID都應該是root,。但是實際情況并非如此,,因為父進程在創(chuàng)建子進程之后,也就是在fork之后,,可以調(diào)用setuid來改變它的UID,。例如,在PC中,,init進程啟動之后,,會先讓用戶登錄。用戶登錄成功后,,就對應有一個shell進程,。該shell進程的UID就會被setuid修改為所登錄的用戶。之后系統(tǒng)中創(chuàng)建的其余進程的UID為所登錄的用戶,。 進程的UID除了來自于父進程之外,,還有另外一種途徑。上面我們說到,,Linux的文件有三種權(quán)限,,分別是Read,、Wirte和Execute。其實還有另外一個種權(quán)限,,叫做SUID,。例如,我們對Android手機進行root的過程中,,會在里面放置一個su文件,。這個su文件就具有SUID權(quán)限,如圖5所示: 圖5 su的SUID和SGID 一個可執(zhí)行文件一旦被設(shè)置了SUID位,,那么當它被一個進程通過exec加載之后,,該進程的UID就會變成該可執(zhí)行文件的所有者的UID。也就是說,,當上述的su被執(zhí)行的時候,,它所運行在的進程的UID是root,于是它就具有最高級別的權(quán)限,,想干什么就干什么,。 與SUI類似,文件還有另外一個稱為SGID的權(quán)限,,不過它描述的是用戶組,。也就是說,一個可執(zhí)行文件一旦被設(shè)置了GUID位,,么當它被一個進程通過exec加載之后,,該進程的主UID就會變成該可執(zhí)行文件的所有者的主UID。 現(xiàn)在,,小伙伴們應該可以理解Android手機的root原理了吧:一個普通的進程通過執(zhí)行su,,從而獲得一個具有root權(quán)限的進程。有了這個具有root權(quán)限的進程之后,,就可以想干什么就干什么了,。su所做的事情其實很簡單,它再fork另外一個子進程來做真正的事情,,也就是我們在執(zhí)行su的時候,,后面所跟的那些參數(shù)。由于su所運行在的進程的UID是root,,因此由它fork出來的子進程的UID也是root,。于是,子進程也可以想干什么就干什么了,。 不過呢,,用來root手機的su還會配合另外一個稱為superuser的app來使用。su在fork子進程來做真正的事情之前,,會將superuser啟動起來,,詢問用戶是否允許fork一個UID是root的子進程。這樣就可以對root權(quán)限進行控制,,避免被惡意應用偷偷地使用,。 這里是su的源代碼,小伙伴們可以根據(jù)上面所講的知識讀一讀:https://code.google.com/p/superuser/source/browse/trunk/su/su.c?r=2,。 在傳統(tǒng)的UNIX以及類UNIX系統(tǒng)中,,進程的權(quán)限只劃分兩種:特權(quán)和非特權(quán)。UID等于0的進程就是特權(quán)進程,,它們可以通過一切的權(quán)限檢查,。UID不等于0的進程就非特權(quán)進程,它們在訪問一些敏感資源或者調(diào)用一個敏感API時,,需要進行權(quán)限檢查,。這種純粹通過UID來做權(quán)限檢查的安全機制來粗放了。于是,,Linux從2.2開始,,從進程的權(quán)限進行了細分,稱為Capabilities,。一個進程所具有Capabilities可以通過capset和prctl等系統(tǒng)API來設(shè)置,。也就是說,當一個進程調(diào)用一個敏感的系統(tǒng)API時,,Linux內(nèi)核除了考慮它的UID之外,,還會考慮它是否具有對應的Capability。 這里就是Linux所設(shè)計的Capabilities列表,,有興趣的小伙伴可以再讀一讀:http:///linux/man-pages/man7/capabilities.7.html,。 以上就是Linux基于UID/GID的安全機制的核心內(nèi)容。接下來我們再看Android基于Permission的安全機制,,它也有三個角色:apk,、signature和permission,如圖6所示: 圖6 Android的Permission安全機制
Android的APK經(jīng)過PackageManagerService安裝之后,,就相當于Linux里面的User,,它們都會被分配到一個UID和一個主GID,而APK所申請的Permission就相當于是Linux里面的Supplementary GID,。 我們知道,,Android的APK都是運行在獨立的應用程序進程里面的,并且這些應用程序進程都是Zygote進程fork出來的,。Zygote進程又是由init進程fork出來的,,并且它被init進程fork出來后,沒有被setuid降權(quán),,也就是它的uid仍然是root,。按照我們前面所說的,,應用程序進程被Zygote進程fork出來的時候,它的UID也應當是root,。但是,,它們的UID會被setuid修改為所加載的APK被分配的UID。 參照Android應用程序進程啟動過程的源代碼分析一文的分析,,ActivityManagerService在請求Zygote創(chuàng)建應用程序進程的時候,,會將這個應用程序所加載的APK所分配得到的UID和GID(包括主GID和Supplementary GID)都收集起來,并且將它們作為參數(shù)傳遞給Zygote進程,。Zygote進程通過執(zhí)行函數(shù)來fork應用程序進程:
參數(shù)args[0],、args[1]和args[]保存的就是APK分配到的UID、主GID和Supplementary GID,,它們分別通過setuid,、setgid和setgroupsIntarray設(shè)置給當前fork出來的應用程序進程,于是應用程序進程就不再具有root權(quán)限了,。 那么,,Signature又充當什么作用呢?兩個作用:1. 控制哪些APK可以共享同一個UID,;2. 控制哪些APK可以申請哪些Permission,。 我們知道,如果要讓兩個APK共享同一個UID,,那么就需要在AndroidManifest中配置android:sharedUserId屬性,。PackageManagerService在安裝APK的時候,如果發(fā)現(xiàn)兩個APK具有相同的android:sharedUserId屬性,,那么它們就會被分配到相同的UID,。當然這有一個前提,就是這兩個APK必須具有相同的Signature,。這很重要,,否則的話,如果我知道別人的APK設(shè)置了android:sharedUserId屬性,,那么我也在自己的APK中設(shè)置相同的android:sharedUserId屬性,,就可以去訪問別人APK的數(shù)據(jù)了。 除了可以通過android:sharedUserId屬性申請讓兩個APK共享同一個UID之外,,我們還可以將android:sharedUserId屬性的值設(shè)置為“android.uid.system”,,從而讓一個APK的UID設(shè)置為1000。UID是1000的用戶是system,,系統(tǒng)的關(guān)鍵服務都是運行在的進程的UID就是它,。它的權(quán)限雖然不等同于root,不過也足夠大了。我們可以通過Master Key漏洞來看一下有多大,。 Master Key漏洞發(fā)布時,,曾轟動了整個Android界,它的具體情況老羅就不分析了,,網(wǎng)上很多,,這里是一篇官方的文章:http:///corporate-blog/bluebox-uncovers-android-master-key/。現(xiàn)在就簡單說說它是怎么利用的: 1. 找到一個具有系統(tǒng)簽名的APP,,并且這個APP通過android:sharedUserId屬性申請了android.uid.system這個UID。 2. 通過Master Key向這個APP注入惡意代碼,。 3. 注入到這個APP的惡意代碼在運行時就獲得了system用戶身份,。 4. 修改/data/local.prop文件,將屬性ro.kernel.qemu的值設(shè)置為1,。 5. 重啟手機,,由于ro.kernel.qemu的值等于1,這時候手機里面的adb進程不會被setuid剝奪掉root權(quán)限,。 6. 通過具有root權(quán)限的adb進程就可以向系統(tǒng)注入我們熟悉的su和superuser.apk,,于是整個root過程完成。 注意,,第1步之所以要找一個具有系統(tǒng)簽名的APP,,是因為通過android:sharedUserId屬性申請android.uid.system這個UID需要有系統(tǒng)簽名,也就是說不是誰可以申請system這個UID的,。另外,,/data/local.prop文件的Owner是system,因此,,只有獲得了system這個UID的進程,,才可以對它進行修改。 再說說Signature與Permission的關(guān)系,。有些Permission,,例如INSTALL_PACKAGE,不是誰都可以申請的,,必須要具有系統(tǒng)簽名才可以,,這樣就可以控制Suppementary GID的分配,從而控制應用程序進程的權(quán)限,。具有哪些Permission是具有系統(tǒng)簽名才可以申請的,,可以參考官方文檔:http://developer./reference/android/Manifest.html,就是哪些標記為“Not for use by third-party applications”的Permission,。 了解了Android的Permission機制之后,,我們就可以知道: 1. Android的APK就相當于是Linux的UID。 2. Android的Permission就相當于是Linux的GID,。 3. Android的Signature就是用來控制APK的UID和GID分配的,。 這就是Android基于Permission的安全機制與Linux基于UID/GID的安全機制的關(guān)系,,概括來說,我們常說的應用程序沙箱就是這樣的: 圖7 Android的Application Sandbox 接下來我們就終于可以步入正題分析NDK在非root手機上調(diào)試APP的原理了,。首先們需要知道的是,,NDK是通過gdbclient和gdbserver來調(diào)試APP的。具體來說,,就是通過gdbserver通過ptrace附加上目標APP進程去,,然后gdbclient再通過socket或者pipe來鏈接gdbserver,并且向它發(fā)出命令來對APP進程進行調(diào)試,。這個具體的過程可以參考這篇文章,,講得很詳細的了:http://ian-ni-lewis. 第一個關(guān)鍵點是每一個需要調(diào)試的APK在打包的時候,都會帶上一個gdbserver,。因為手機上面不帶有g(shù)dbserver這個工具,。這個gdbserver就負責用來ptrace到要調(diào)度的APP進程去。 第二個關(guān)鍵點是ptrace的調(diào)用,。一般來說,,只有root權(quán)限的進程只可以調(diào)用。例如,,如果我們想通過ptrace向目標進程注入一個SO,,那么就需要在root過的手機上通過向su申請root權(quán)限。但是,,這不是絕對的,。如果一個進程與目標進程的UID是相同的,那么該進程就具有調(diào)用ptrace的權(quán)限,。我們可以看看ptrace_attach函數(shù)的實現(xiàn):
第三個關(guān)鍵點是如何讓gdbserver進程的UID與要調(diào)試的APP進程的UID一樣,。因為在沒有root過的手機上,要想獲得root權(quán)限是不可能的了,,因此只能選擇以目標進程相同的UID運行這個方法,。這就要用到另外一個工具了:run-as。 runs-as其實是一個與su類似的工具,,它在設(shè)備上是自帶的,,位于/system/bin目錄下,它的SUID位也是被設(shè)置了,,并且它的所有者也是root,,我們可以通過ls -l /system/bin/run-as來看到:
第四個關(guān)鍵點是被調(diào)試的APK在其AndroidManifext.xml里必須將android:debuggable屬性設(shè)置為true,。這是為什么呢,?原來,當一個進程具有ptrace到目標進程的權(quán)限時,,還不能夠?qū)δ繕诉M程進行調(diào)試,,還要求目標進程將自己設(shè)置為可dumpable的。我們再回過頭來進一步看看__ptrace_may_access的實現(xiàn):
這下我們就明白NDK在非root手機上調(diào)試APP的原理了:gdbserver通過run-as獲得與目標進程相同的UID,,然后就可以ptrace到目標進程去調(diào)試了。 這一下就引出了run-as這個工具,貌似很強大的樣子,,那我們是不是也可以利用它來做壞事呢,?例如,我們可以在adb shell中運行run-as(run-as屬于shell組,,因此可以執(zhí)行),,并且指定run-as以某一個APK的UID運行,那么不就是可以讀取該APK的數(shù)據(jù)了嗎,?從而突破了Android的應用程序沙箱,。但是這是不可能做到的。 我們可以看一下run-as的源代碼:
1. 檢查自身是不是以shell或者root用戶運行,。 2. 檢查指定的UID的值是否是在分配給APK范圍內(nèi)的值,也就是只可以指定APK的UID,,而不可以指定像system這樣的UID,。 3. 指定的UID所對應的APK的android:debuggable屬性必須要設(shè)置為true。 綜合了以上三個條件之后,,我們才可以成功地執(zhí)行run-as,。 這里還有一點需要提一下的就是,我們在運行run-as的時候,,指定的參數(shù)其實是一個package name,。run-as通過這個package name到/data/system/packages.xml去獲得對應的APK的安裝信息,包括它所分配的UID,,以及它的android:debuggable屬性,。文件/data/system/packages.xml的所有者是system,run-as在讀取這個文件的時候的身份是root,,因此有權(quán)限對它進行讀取,。 這下我們也明白了,你想通過run-as來做壞事是不行的,。同時,,這也提醒我們,在發(fā)布APK的時候,,一定不要將android:debuggable屬性的值設(shè)置為true,。否則的話,就提供了機會讓別人去讀取你的數(shù)據(jù),,或者對你進行ptrace了,。 至些,,我們就通過NDK在非Root手機上的調(diào)試原理完成了Android安全機制的探討了,,不知道各位小伙伴們理解了嗎,?沒理解的沒關(guān)系,可以關(guān)注老羅的新浪微博,,上面有很多的干貨分享:http://weibo.com/shengyangluo。 |
|
來自: android之情殤 > 《laoluo》