SUID提權:CVE-2021-4034漏洞全解析


本篇是SUID提權系列文章的第三篇。建議大家按順序先閱讀前兩篇鋪墊。

第一篇我們講解了SUID提權的基礎知識,linux系統的用戶、文件權限與進程憑證。

第二篇我們講解了SUID提權的原理,只要讓具有SUID-root權限的程序加載我們的提權so,我們就贏了。

第三篇我們來講解CVE-2021-4034漏洞的原理以及利用過程。本文會按照下面主題進行分享:

  • 什么是CVE?
  • CVE-2021-4034介紹
  • 漏洞分析
  • 漏洞利用
  • 漏洞修復

0x1 什么是CVE?

CVE的英文全稱是Common Vulnerabilities & Exposures(公共漏洞和暴露)。

官網:
https://cve.mitre.org/。

0x2 CVE-2021-4034介紹

CVE-2021-4034是一個SUID提權漏洞,利用具有SUID-root權限的pkexec,精心構造參數及運行環境,使其加載我們准備好提權的so。

CVE-2021-4034:
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-4034

0x3 漏洞分析

看看如何讓pkexec加載任意so。我們需要准備一份pkexec源碼。

pkexec源碼:
https://www.freedesktop.org/software/polkit/releases/polkit-0.120.tar.gz

查看src/programs/pkexec.c文件。部分代碼如下:

435 main (int argc, char *argv[])
436 {
...
534   for (n = 1; n < (guint) argc; n++)
535     {
...
568     }
...
610   path = g_strdup (argv[n]);
...
629   if (path[0] != '/')
630     {
...
632       s = g_find_program_in_path (path);
...
639       argv[n] = path = s;
640     }

我們來分析一下這一段代碼,
534-568行:main函數處理命令行參數。
610-640行:pkexec在 PATH 環境變量的目錄中搜索要執行的程序,如果其路徑不是絕對路徑,argv[n] = path = s。

不幸的是,如果命令行參數argc的數量為0,那么:

  • 在第 534 行,n賦值為1;
  • 在第 610 行,從argv[n]也就是argv[1]越界讀取指針路徑;
  • 在第 639 行,指針s被越界寫入argv[1]。

但是從這個越界的argv[1]中讀取和寫入的到底是什么?要回答這個問題,我們必須簡短地離題。我們需要了解execve函數,函數原型如下:

int execve(const char *filename, char *const argv[], char *const envp[]); 

當我們使用execve函數啟動一個新程序時,內核將我們的參數、環境變量字符串和指針(argv和envp)復制到新程序堆棧的末尾;
例如execve("/usr/bin/pkexec",{"program","-option",...},{"value","PATH=name",...})的內存布局如下:

|---------+---------+-----+------------|---------+---------+-----+------------| 
| argv[0] | argv[1] | ... | argv[argc] | envp[0] | envp[1] | ... | envp[envc] | 
|----|----+----|----+-----+-----|------|----|----+----|----+-----+-----|------| 
"program" "-option"           NULL      "value" "PATH=name"          NULL 

顯然,因為argv和envp指針在內存中是連續的,如果argc為0,那么越界argv[1]實際上是envp[0],指向我們的第一個環境變量“value”的指針。所以:

  • 610行:將要執行的程序的路徑從argv[1](即envp[0])中越界讀取,並指向“value”;
  • 632行:這個路徑“value”被傳遞給 g_find_program_in_path函數,在我們的 PATH 環境變量的目錄中搜索一個名為“value”的可執行文件;如果找到這樣的可執行文件,則將其完整路徑返回給pkexec第 632 行的指針s;
  • 639行:這個完整路徑被越界寫入argv[1](即envp[0]),從而覆蓋了我們的第一個環境變量。

下面兩段話,值得多讀幾篇,本漏洞的核心:

  • 如果我們的PATH環境變量是“PATH=name”,並且如果目錄“name”存在(在當前工作目錄中),並且包含一個名為“value”的可執行文件,那么指向字符串“name/value”的指針將被越界寫入envp[0];

  • 如果我們的PATH是“PATH=name=.”,並且如果目錄“name=.” 存在並包含一個名為“value”的可執行文件,那么指向字符串“name=./value”的指針將被越界寫入envp[0]。

0x4 漏洞利用

利用代碼:
https://github.com/berdav/CVE-2021-4034

第639行越界寫入后不久,在第702行將完全清除其環境變量。我們需要精心選擇一個環境變量進行利用,這里就考驗大家的知識廣度了,此漏洞很早就被發現,但是成功利用是在最近。

639       argv[n] = path = s;
...
657   for (n = 0; environment_variables_to_save[n] != NULL; n++)
658     {
659       const gchar *key = environment_variables_to_save[n];
...
662       value = g_getenv (key);
...
670       if (!validate_environment_variable (key, value))
...
675     }
...
702   if (clearenv () != 0)

為了將錯誤消息打印到stderr,pkexec調用g_printerr函數

88 log_message (gint     level,
89              gboolean print_to_stderr,
90              const    gchar *format,
91              ...)
92 {
...
125   if (print_to_stderr)
126     g_printerr ("%s\n", s);
...
383 validate_environment_variable (const gchar *key,
384                                const gchar *value)
385 {
...
406           log_message (LOG_CRIT, TRUE,
407                        "The value for the SHELL variable was not found the /etc/shells file");
408           g_printerr ("\n"
409                       "This incident has been reported.\n");

g_printerr函數通常打印UTF-8錯誤消息,但如果環境變量CHARSET不是UTF-8,它可以在另一個charset中打印消息(注意:CHARSET不是安全敏感的,它不是“不安全”的環境變量)。要將消息從UTF-8轉換為另一個字符集,g_printerr函數調用了glibc的函數iconv_open。

要將消息從一個字符集轉換為另一個字符集,iconv_open函數執行了小型共享庫;通常,這些三胞胎(源字符集、目的字符集和庫名稱)從默認配置文件/usr/lib/gconv/gconv-modules中讀取。環境變量GCONV_PATH可以強制iconv_open函數讀取另一個配置文件;當然,GCONV_PATH是“不安全”的環境變量之一(因為它會導致執行任意庫),但被ld.so從SUID程序的環境變量中移除。

不幸的是,CVE-2021-4034允許我們將GCONV_PATH重新引入pkexec的環境,並以root用戶身份執行我們自己的共享庫。

0x5 漏洞修復

去掉pkexec的SUID-root權限就好了

chmod 0755 /usr/bin/pkexec

歡迎大家使用常用聊天軟件關注、點贊、評論交流~


免責聲明!

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



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