天天看點

Android adb setuid提權漏洞

在main(:72)函數中,首先擷取了RLIMIT_NPROC的值(:83),這個值是Linux核心中定義的每個使用者可以運作的最大程序數。

然後,調用find_adb()函數(:94)來搜尋Android系統中adb程序的PID,具體而言,該函數讀取每個程序對應的檔案的/proc/<pid>/cmdline,根據其是否等于”/sbin/adb”來判斷是否adb程序。

接下來,fork了一個新的程序(:109),父程序退出,而子程序繼續。接下來,在113行建立一個管道。

<code>if</code>

<code>(fork() &gt; 0)</code>

<code>    </code><code>exit</code><code>(0);</code>

<code>setsid();</code>

<code>pipe(pepe);</code>

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

<code>(fork() == 0) {</code>

<code>    </code><code>close(pepe[0]);</code>

<code>    </code><code>for</code>

<code>(;;) {</code>

<code>        </code><code>if</code>

<code>((p = fork()) == 0) {</code>

<code>            </code><code>exit</code><code>(0);</code>

<code>        </code><code>}</code><code>else</code>

<code>if</code> <code>(p &lt; 0) {</code>

<code>            </code><code>if</code>

<code>(new_pids) {</code>

<code>                </code><code>printf</code><code>(</code><code>"\n[+] Forked %d childs.\n"</code><code>, pids);</code>

<code>                </code><code>new_pids = 0;</code>

<code>                </code><code>write(pepe[1], &amp;c, 1);</code>

<code>                </code><code>close(pepe[1]);</code>

<code>            </code><code>}</code>

<code>{</code>

<code>            </code><code>++pids;</code>

<code>        </code><code>}</code>

<code>    </code><code>}</code>

<code>}</code>

建立一個程序後,在子程序之中,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的源碼。

在&lt;android_src&gt;/system/core/adb/adb.c的第918行,我們可以看到如下代碼:

<code>/* then switch user and group to "shell" */</code>

<code>(setgid(AID_SHELL) != 0) {</code>

<code>    </code><code>exit</code><code>(1);</code>

<code>(setuid(AID_SHELL) != 0) {</code>

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

<code>setgid(AID_SHELL);</code>

<code>setuid(AID_SHELL);</code>

簡而言之,原來沒有檢查setuid()函數的傳回值。事實上,在此之前,adb.c中的代碼都是以root權限運作,以完成部分初始化工作。在這一行,通過調用setuid()将使用者從root切換回shell,但setuid()在shell使用者程序數達到上限RLIMIT_NPROC時,會失敗,是以adb.c繼續以root身份運作,而沒有報錯。

我們來看setuid()的man手冊(man 2 setuid),其中有如下說明:

<code>RETURN VALUE</code>

<code>       </code><code>On  success,  zero is returned.  On error, -1 is returned, and errno is</code>

<code>       </code><code>set appropriately.</code>

<code>ERRORS</code>

<code>       </code><code>EAGAIN The uid does not match the current uid and  uid  brings  process</code>

<code>              </code><code>over its RLIMIT_NPROC resource limit.</code>

可以看到,setuid是可能發生錯誤的,并且在uid的程序數超過RLIMIT_NPROC極限時,發生EAGAIN錯誤。

在android的源碼中,setuid()定義于&lt;android_src&gt;/bionic/libc/unistd/setuid.c,實際上引用了一個外部符号__setuid,這個符号在&lt;android_src&gt;/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行,其中一部分代碼如下:

<code>(atomic_read(&amp;new_user-&gt;processes) &gt;= rlimit(RLIMIT_NPROC) &amp;&amp;</code>

<code>        </code><code>new_user != INIT_USER) {</code>

<code>    </code><code>free_uid(new_user);</code>

<code>    </code><code>return</code>

<code>-EAGAIN;</code>

含義很明顯,當目标使用者的程序數達到上限,那系統就不能再将一個程序配置設定給它,因而傳回-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程序,可以被用于與使用者的下一步互動。

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

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

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

上一篇: SQLite Editor

繼續閱讀