android root權限破解分析


破解android的root權限的本質是:在系統中加入一個任何用戶都可能用於登陸的su命令。或者說替換掉系統中的su程序,因為系統中的默認su程序需要驗證實際用戶權限,只有root和 shell用戶才有權運行系統默認的su程序,其他用戶運行都會返回錯誤。而破解后的su將不檢查實際用戶權限,這樣普通的用戶也將可以運行su程序,也可以通過su程序將自己的權限提升。 

root破解沒有利用什么Linux內核漏洞(Linux內核不可能有這么大的漏洞存在),可以理解成root破解就是在你系統中植入“木馬su”,說它是“木馬”一點兒都不為過,假如惡意程序在系統中運行也可以通過su來提升自己的權限的這樣的結果將會是災難性 的。所以一般情況下root過手機都會有一個SuperUser應用程序來讓用戶管理允許誰獲得root權限,也算是給系統加了一層保險吧! 


Android的應用程序入口肯定是Java程序。應用程序的啟動者是由系統臨時根據Androidmanifest.xml中定義的權限而創建的臨時用戶。而不像linux那樣是使用登陸者的身份啟動,從而使得進程具有登陸者的所有權限。這也是Android的安全機制之一。 
新的權限機制也帶來新的問題,Android給應用程序的權限是按功能來分,java雖然可以訪問文件系統。但由於應用程序本身是臨時用戶啟動,這個臨時用戶權限十分有限。因此誕生了<越獄/root機器>這樣的產物。 

其實root機器不是真正能讓你的應用程序具有root權限。它原理就跟linux下的像sudo這樣的命令。在系統的bin目錄下放個su程序並且屬主是root並有suid權限。則通過su執行的命令都具有Android root權限。 

當然使用臨時用戶權限想把su拷貝的/system/bin目錄並改屬性並不是一件容易的事情。這里用到2個工具跟2個命令。 
工具就是busybox。不熟悉的同學可以去網上google下。這個太有名了我就不多說了。把busybox拷貝到你有權限訪問的目錄然后給他賦予4755權限,你就可以用它做很多事了。 
當然busybox不能提升權限,真正提升權限的是ratc這個程序,這個程序中一鍵root包里面可以找到,作用是rooting在adb的shell。 
網上介紹Ratc的文章不多,它是rage against the cage 的縮寫。是真正的提升權限的破解程序。雖然我沒看過源代碼,但估計是利用adb源代碼部分內容來實現的, 
原理估計跟模擬器使用adb shell登陸可以獲得root shell差不多。(因為它運行需要adb連接才會成功)。 

使用busybox前先運行ratc,這樣運行busybox的UID將是0,也就是root。 
首先把system目錄改成可讀性的:busybox mount -o remount,rw /system, 
當然你還不能改下面的文件,因為system下文件的所有者都不是你。但你可以偷梁換柱把system下的目錄給換掉。 
使用命令Busybox mount -t tmpfs none /system/xbin,呵呵這下xbin目錄你隨便寫了。 
將su跟busybox弄過去cp /data/data/xxx/su /system/xbin。然后賦權限chmod 4755 /system/xbin/su。 
然后使目錄生效busybox --install -s /system/xbin,別忘善后busybox mount -o remount,ro /system去掉system可寫。 
這樣只是臨時的,只能用su跟busybox能執行一些原來系統沒有權限執行的命令而已。當系統重啟后/system/xbin又變為原來的文件。 
真正要改系統的話需要自己寫內核代碼(相當於windows的驅動程序)。內核文件擁有所有權限。使用busybox命令insmod /data/data/xxx/xxx.ko裝載內核文件,你想干嘛就可以干嘛了。 
當然我們不是搞破解的沒必要去改別人的機器,我們只是想讓自己應用程序具有root權限而已。所以臨時的su就可以了。 
我們用c++寫一個可執行文件。使用socket可以跟java的程序通訊。然后將需要使用root權限才能執行的代碼放在c++程序里,然后java程序中創建新的su進程, 
將c++程序帶全路徑作為參數1。啟動后就可以通過socket調用c++函數去執行你想干的事了。最后程序執行完了別忘了善后busybox umount /system/xbin。 
最后說說要注意的事情,如果機器已經擁有Android root權限的話就不需要做這些事情了,但root過的機器都有裝有個權限管理的程序。會彈出對話框。但這個程序管理能力有限,如果不想讓他彈出的話。也許可以通過改su文件名來解決。有興趣的同學不妨試試。 


-------------------------------------------------------------------------------------------------------------------------------------------------------- 


破解手機Root權限是比較簡單及安全的,破解Root權限的原理就是在手機的/system/bin/或/system/xbin/目錄下放置一個可執行文件“su”,這是一個二進制文件,僅僅在系統中置入這個“su”文件是不會給手機的軟件或硬件造成任何故障。 

下面的代碼是android系統原版的su中的部分代碼,可以看出只允許getuid()為AID_ROOT和AID_SHELL的進程可以使用su進行登陸。 
也就是說,Android系統默認的su程序只能root和shell可以用運行su,這個是安全的。如果把這個限制拿掉,就是root破解了,獲取了root權限
。 
/* Until we have something better, only root and the shell can use su. */ 
myuid = getuid(); 
if (myuid != AID_ROOT && myuid != AID_SHELL) { 
fprintf(stderr,"su: uid %d not allowed to su\n", myuid); 
return 1; 


  在Superuser(一個破解程序)這個android程序中的su不再有上面的一部分,這樣任何進程都可以使用su進行登陸了。 

還有一部分android程序要使用root權限可能的用法類似於(這個也是Superuser中的一部分代碼): 

Process process = Runtime.getRuntime().exec("su"); 
DataOutputStream os = new DataOutputStream(process.getOutputStream()); 
os.writeBytes("mount -oremount,rw /dev/block/mtdblock3 /system\n"); 
os.writeBytes("busybox cp /data/data/com.koushikdutta.superuser/su /system/bin/su\n"); 
os.writeBytes("busybox chown 0:0 /system/bin/su\n"); 
os.writeBytes("chmod 4755 /system/bin/su\n"); 
os.writeBytes("exit\n"); 
os.flush(); 


而在上面提到的Superuser和android原生的su源碼中都有這部分代碼: 

if(setgid(gid) || setuid(uid)) { 
fprintf(stderr,"su: permission denied\n"); 
return 1; 


從出上面的分析可以認為破解android的root權限的實質是:在系統中加入一個任何用戶都可能用於登陸的su命令。當然這首先要取得root權限才能做到。 
在z4root這個android下的破解android的root權限的程序中有一個rageagainstthecage,這就是破解的關鍵。 




-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 

root破解過程的終極目標是替換掉系統中的su程序。但是要想替換掉系統中su程序本身就是需要root權限的,怎樣在root破 解過程中獲得root權限,成為我們研究的重點了。 
下面我們先清點一下我們需要破解系統情況,假設需要破解的Android系統具備如下條件: 

    1、可以通過adb連接到設備,一般意味着驅動程序已經安裝。 
    2、但是adb獲得用戶權限是shell用戶,而不是root。 

要想理解root破解過程我們首先需要了解一下adb工具,SDK中包含adb(android debug briage)工具,設備端有adbd服務程序后台運行,為開發機的adb程序提供服務,adbd的權限,決定了adb的權限。 
具體用戶可查看/system/core/adb下的源碼,查看 Android.mk你將會發現adb和adbd其實是一份代碼,然后通過宏來編譯。 

查看adb.c的adb_main函數你將會發現adbd中有如下代碼: 

    int adb_main(int is_daemon) 

   { 

        ...... 

        property_get("ro.secure", value, ""); 

       if (strcmp(value, "1") == 0) { 

           // don't run as root if ro.secure is set... 

           secure = 1; 

           ...... 

       } 

   

      if (secure) { 

           ...... 

           setgid(AID_SHELL); 

           setuid(AID_SHELL); 

           ...... 

       } 

   } 

從中我們可以看到adbd會檢測系統的ro.secure屬性,如果該屬性為1則將會把自己的用戶權限降級成shell用戶。一般設備出廠的時候在/default.prop文件中都會有: 

    ro.secure=1 

這樣將會使adbd啟動的時候自動降級成shell用戶。 

然后我們再介紹一下adbd在什么時候啟動的呢?答案是在init.rc中配置的系統服務,由init進程啟動。我們查看init.rc中有如下內容: 

    # adbd is controlled by the persist.service.adb.enable system property 

    service adbd /sbin/adbd 

    disabled 

  在init.rc中配置的系統服務啟動的時候都是root權限(因為init進行是root權限,其子程序也是root)。由此我們可以知道在adbd程序在執行: 

    /* then switch user and group to "shell" */ 

    setgid(AID_SHELL); 

    setuid(AID_SHELL); 

代碼之前都是root權限,只有執行這兩句之后才變成shell權限的。這樣我們就可以引出root破解過程中獲得root權限的方法了,那就是讓以上面setgid和setuid函數執行失敗,也就是降級失敗,那就繼續在root權限下面運行了。 

這其實利用了一個RageAgainstTheCage漏洞。這里面做一個簡單說明: 

    1、出廠設置的ro.secure屬性為1,則adbd也將運行在shell用戶權限下; 

    2、adb工具創建的進程ratc也運行在shell用戶權限下; 

    3、ratc一直創建子進程(ratc創建的子程序也將會運行在shell用戶權限下),緊接着子程序退出,形成僵屍進程,占用shell用戶的進程資源,直到到達shell用戶的進程數為 RLIMIT_NPROC的時候(包括adbd、ratc及其子程序),這是ratc將會創建子進程失敗。 
    這時候殺掉adbd,adbd進程因為是 Android系統服務,將會被Android系統自動重啟,這時候ratc也在競爭產生子程序。在adbd程序執行上面setgid和setuid之 前,ratc已經創建了一個新的子進程,那么shell用戶的進程限額已經達到,則adbd進程執行setgid和setuid將會失敗。根據代碼我們發 現失敗之后adbd將會繼續執行。 
    這樣adbd進程將會運行在root權限下面了。 

    3、這時重新用adb連接設備,則adb將會運行在root權限下面了。 

通過上面的介紹我們發現利用RageAgainstTheCage漏洞,可以使adbd獲得root權限,也就是adb獲得了root權限。 
拿到root權限剩下的問題就好辦了,復制破解之后的su程序到系統中,都是沒有什么技術含量的事情了。 

其實堵住adbd的這個漏洞其實也挺簡單的: 

    /* then switch user and group to "shell" */ 

    if (setgid(AID_SHELL) != 0) { 

       exit(1); 

  } 

    if (setuid(AID_SHELL) != 0) { 

      exit(1); 

  } 

如果發現setgid和setuid函數執行失敗,則adbd進程異常退出,就把這個漏洞給堵上了。 
為什么這么多設備都沒有堵上這個漏洞呢?我覺得是設備廠商的策略,雖然知道怎么封堵漏洞但是就是留着個后門給大家,讓第三方給自己定制 rom,提高自己系統的易用性。 

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 

去年的Android adb setuid提權漏洞被用於各類root刷機,漏洞發現人Sebastian Krahmer公布的利用工具RageAgainstTheCage(rageagainstthecage-arm5.bin)被用於z4root等提權工具、Trojan.Android.Rootcager等惡意代碼之中。下面我們來分析這一漏洞的產生原因。 

The Android Exploid Crew小組在后來發布了一份PoC代碼:rageagainstthecage.c。從這份代碼開始着手。 
在main(:72)函數中,首先獲取了RLIMIT_NPROC的值(:83),這個值是Linux內核中定義的每個用戶可以運行的最大進程數。 
然后,調用find_adb()函數(:94)來搜索Android系統中adb進程的PID,具體而言,該函數讀取每個進程對應的文件的/proc/<pid>/cmdline,根據其是否等於”/sbin/adb”來判斷是否adb進程。 
接下來,fork了一個新的進程(:109),父進程退出,而子進程繼續。接下來,在113行創建一個管道。 

rageagainstthecage.c 
代碼: 

if (fork() > 0) 
    exit(0); 

setsid(); 
pipe(pepe); 

重頭戲發生在下面的122到138行,代碼如下: 

rageagainstthecage.c 

代碼:if (fork() == 0) { 
    close(pepe[0]); 
    for (;;) { 
        if ((p = fork()) == 0) { 
            exit(0); 
        } else if (p < 0) { 
            if (new_pids) { 
                printf("\n[+] Forked %d childs.\n", pids); 
                new_pids = 0; 
                write(pepe[1], &c, 1); 
                close(pepe[1]); 
            } 
        } else { 
            ++pids; 
        } 
    } 

新建一個進程后,在子進程之中,exploit代碼不斷地fork()(:125),而新的子進程不斷退出,從而產生大量的僵屍進程(占據shell用戶的進程數)。 
最終,進程數達到上限,fork()返回小於0,於是打印當前已經創建多少子進程,並向管道輸入一個字符(:131)。
在這里,管道的作用是和(:122)fork出來的父進程同步,該進程在141行read這一管道,因而阻塞直至僵屍進程已經達到上限(:131)。 
進一步的,exploit殺掉adb進程,並在系統檢測到這一現象並重啟一個adb之前,再一次fork(),將前一個adb留下的進程空位占據。 
最后,在152行,exploit調用wait_for_root_adb(),等待系統重啟一個adb,這個新建的adb就會具有root權限。 


為什么在shell用戶的進程數達到上限RLIMIT_NPROC以后,新建的adb會具有root權限?我們來看adb的源碼。 
在<android_src>/system/core/adb/adb.c的第918行,我們可以看到如下代碼: 

android_src/system/core/adb/adb.c 
代碼:/* then switch user and group to "shell" */ 
if (setgid(AID_SHELL) != 0) { 
    exit(1); 

if (setuid(AID_SHELL) != 0) { 
    exit(1); 

這已經是漏洞修補以后的代碼。在漏洞最初被發現時,代碼如下: 

android_src/system/core/adb/adb.c 
代碼:/* then switch user and group to "shell" */ 
setgid(AID_SHELL); 
setuid(AID_SHELL); 
簡而言之,原來沒有檢查setuid()函數的返回值。事實上,在此之前,adb.c中的代碼都是以root權限運行,以完成部分初始化工作。 
在這一行,通過調用setuid()將用戶從root切換回shell,但setuid()在shell用戶進程數達到上限RLIMIT_NPROC時,會失敗,因此adb.c繼續以root身份運行,而沒有報錯。 

我們來看setuid()的man手冊(man 2 setuid),其中有如下說明: 
man 2 setuid 
代碼:RETURN VALUE 
       On  success,  zero is returned.  On error, -1 is returned, and errno is 
       set appropriately. 

ERRORS 
       EAGAIN The uid does not match the current uid and  uid  brings  process 
              over its RLIMIT_NPROC resource limit. 
可以看到,setuid是可能發生錯誤的,並且在uid的進程數超過RLIMIT_NPROC極限時,發生EAGAIN錯誤。 


在android的源碼中,setuid()定義於<android_src>/bionic/libc/unistd/setuid.c,實際上引用了一個外部符號__setuid, 
這個符號在<android_src>/bionic/libc/arch_xxx/syscalls/__setuid.S中定義,最終是一個%eax=$__NR_setuid32,%ebx=uid的int 0×80中斷。 

因為只是要分析原理,我們不再鏖戰於Android,轉而看向Linux內核。 
在最新的kernel2.6中,setuid()位於kernel/sys.c的682行,其中,在697行,一切正常的情況下,它會調用set_user()來完成用戶切換。 

set_user()實現於同一文件的587行,其中一部分代碼如下: 
kernel/sys.c 
代碼:if (atomic_read(&new_user->processes) >= rlimit(RLIMIT_NPROC) && 
        new_user != INIT_USER) { 
    free_uid(new_user); 
    return -EAGAIN; 

含義很明顯,當目標用戶的進程數達到上限,那系統就不能再將一個進程分配給它,因而返回-EAGEIN。然后再setuid()中,直接跳過后面的代碼,而返回錯誤。 

至此,整個漏洞的原理已經分析完畢。整理如下: 

1、在Android的shell用戶下,制造大量的僵屍進程,直至達到shell用戶的進程數上限RLIMIT_NPROC; 

2、kill當前系統中的adb進程,並再次占據其進程位置以保持達到上限; 

3、系統會在一段時間后重啟一個adb進程,該進程最初是root用戶,在完成少許初始化工作后,調用setuid()切換至shell用戶; 

4、此時shell用戶的進程數已經達到上限,所以setuid()失敗,返回-1,並且用戶更換沒有完成,adb還是root權限; 

5、adb沒有檢查setuid()的返回值,繼續后續的工作,因此產生了一個具有root權限的adb進程,可以被用於與用戶的下一步交互。 

實際上,setuid在目標用戶進程數達到RLIMIT_NPROC極限時返回錯誤,這一問題可能產生的安全隱患最早可以追溯到2000年。而在2006年,出現了真正利用這一編碼問題的漏洞(CVE-2006-2607)。 

因此,這並不是一個全新的漏洞。我們可以得出幾點結論: 

1、函數返回值一直是忽略的對象,因為getuid()永遠不會失敗,程序員可能會認為setuid()也不會失敗——至少沒有遇到過,因此忽略了對返回值的檢查。檢查一個系統函數是否調用失敗是一個常識,但又是很麻煩的事,如果為了省事而忽略,問題就可能產生了。 

2、Android下的安全問題,很多並非全新的,而且個人判斷將來還會有大量漏洞、惡意代碼產生於傳統思路,而作用於新的平台。面對這一新的平台,我們是否能搶先於攻擊者做好防范准備,是一個需要我們思考和實踐的問題。 




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM