CVE-2021-3156: Heap-Based Buffer Overflow in Sudo (Baron Samedit)
CVE-2021-3156簡介:
sudo是一個幾乎無處不在的實用程序,可用於主要的類Unix操作系統。通過利用此漏洞,任何未經授權的用戶都可以使用默認sudo配置在易受攻擊的主機上獲得root權限。
Sudo是一個強大的實用程序,它包含在大多數基於Unix和Linux的操作系統中。它允許用戶以另一個用戶的安全權限運行程序。近10年來,這個漏洞本身一直隱藏在人們的視線中。
根據安全研究員[email protected]的報告:
受到威脅的系統有:
Ubuntu 18.04.5 (Bionic Beaver) - sudo 1.8.21, libc-2.27
Ubuntu 20.04.1 (Focal Fossa) - sudo 1.8.31, libc-2.31
Debian 10.0 (Buster) - sudo 1.8.27, libc-2.28
通過審計Sudo Mode模塊代碼:
571 if (ISSET(mode, MODE_RUN) && ISSET(flags, MODE_SHELL)) {
572 char **av, *cmnd = NULL;
573 int ac = 1;
...
581 cmnd = dst = reallocarray(NULL, cmnd_size, 2);
...
587 for (av = argv; *av != NULL; av++) {
588 for (src = *av; *src != '\0'; src++) {
589 /* quote potential meta characters */
590 if (!isalnum((unsigned char)*src) && *src != '_' && *src != '-' && *src != '$')
591 *dst++ = '\\';
592 *dst++ = *src;
593 }
594 *dst++ = ' ';
595 }
...
600 ac += 2; /* -c cmnd */
...
603 av = reallocarray(NULL, ac + 1, sizeof(char *));
...
609 av[0] = (char *)user_details.shell; /* plugin may override shell */
610 if (cmnd != NULL) {
611 av[1] = "-c";
612 av[2] = cmnd;
613 }
614 av[ac] = NULL;
615
616 argv = av;
617 argc = ac;
618 }
稍後,在sudoers\u policy\u main()中,set\cmnd()將命令行參數連接到基於堆的緩衝區“user\u args”(第864-871行)中,並取消對元字符(第866-867行)的scape,“用於sudoers匹配和日誌記錄目的”:
819 if (sudo_mode & (MODE_RUN | MODE_EDIT | MODE_CHECK)) {
...
852 for (size = 0, av = NewArgv + 1; *av; av++)
853 size += strlen(*av) + 1;
854 if (size == 0 || (user_args = malloc(size)) == NULL) {
...
857 }
858 if (ISSET(sudo_mode, MODE_SHELL|MODE_LOGIN_SHELL)) {
...
864 for (to = user_args, av = NewArgv + 1; (from = *av); av++) {
865 while (*from) {
866 if (from[0] == '\\' && !isspace((unsigned char)from[1]))
867 from++;
868 *to++ = *from++;
869 }
870 *to++ = ' ';
871 }
...
884 }
...
886 }
在第866行,“from[0]”是反斜槓字符,“from[1]”是參數的空終止符(即,不是空格字符);
在第867行,“from”遞增並指向空終止符;
在第868行,空終止符被複制到“user\u args”緩衝區,“from”再次遞增並指向空終止符後面的第一個字符(即,超出參數的邊界);
第865-869行的“while”循環讀取越界字符並將其複製到“user\u args”緩衝區。
換句話說,set\u cmnd()易受基於堆的緩衝區溢出的攻擊,因爲複製到“user\u args”緩衝區的越界字符未包含在其大小中(在第852-853行計算)。
但是,理論上,沒有命令行參數可以以單個反斜槓字符結尾:如果設置了MODE\u SHELL或MODE\u LOGIN\u SHELL(第858行,這是到達易受攻擊代碼的必要條件),則設置MODE\u SHELL(第571行),parse\u args()已經轉義了所有元字符,包括反斜槓(即。,它用第二個反斜槓逃過了每一個反斜槓)。
但實際上,set\u cmnd()中的易受攻擊代碼和
parse\u args()中的轉義代碼被稍微不同的條件包圍:
819 if (sudo_mode & (MODE_RUN | MODE_EDIT | MODE_CHECK)) {
...
858 if (ISSET(sudo_mode, MODE_SHELL|MODE_LOGIN_SHELL)) {
571 if (ISSET(mode, MODE_RUN) && ISSET(flags, MODE_SHELL)) {
我們的問題是:我們是否可以設置模式\u SHELL和模式\u EDIT或模式\u CHECK(以訪問易受攻擊的代碼),而不是默認模式\u RUN(以避免轉義代碼)?
答案似乎是否定的:如果我們設置MODE_EDIT(-e option,第361行)或MODE_CHECK(-l option,第423行和第519行),那麼parse_args()將MODE_SHELL從“valid_flags”(第363行和第424行)中刪除,如果我們指定了MODE_SHELL(第532-533行)之類的無效標誌,則返回錯誤:
358 case 'e':
...
361 mode = MODE_EDIT;
362 sudo_settings[ARG_SUDOEDIT].value = "true";
363 valid_flags = MODE_NONINTERACTIVE;
364 break;
...
416 case 'l':
...
423 mode = MODE_LIST;
424 valid_flags = MODE_NONINTERACTIVE|MODE_LONG_LIST;
425 break;
...
518 if (argc > 0 && mode == MODE_LIST)
519 mode = MODE_CHECK;
...
532 if ((flags & valid_flags) != flags)
533 usage(1);
但是我們發現了一個漏洞:如果我們將Sudo作爲“sudoedit”而不是“Sudo”執行,那麼parse\u args()會自動設置模式\u EDIT(第270行),但不會重置“valid\u flags”,“valid\u flags”默認包括模式\u SHELL(第127行和第249行):
127 #define DEFAULT_VALID_FLAGS (MODE_BACKGROUND|MODE_PRESERVE_ENV|MODE_RESET_HOME|MODE_LOGIN_SHELL|MODE_NONINTERACTIVE|MODE_SHELL)
...
249 int valid_flags = DEFAULT_VALID_FLAGS;
...
267 proglen = strlen(progname);
268 if (proglen > 4 && strcmp(progname + proglen - 4, "edit") == 0) {
269 progname = "sudoedit";
270 mode = MODE_EDIT;
271 sudo_settings[ARG_SUDOEDIT].value = "true";
272 }
因此,如果我們執行“sudoedit-s”,那麼我們同時設置模式\u EDIT和模式\u SHELL(而不是模式\u RUN),我們就避免了轉義代碼,到達易受攻擊的代碼,並通過以單個反斜槓字符結尾的命令行參數使基於堆的緩衝區“user \u args”溢出:
sudoedit-s'\'`perl-e'print“A”x 65536'
malloc(): corrupted top size
Aborted (core dumped)
從攻擊者的角度來看,這種緩衝區溢出非常理想,原因如下:
1) 攻擊者控制可以溢出的“user\u args”緩衝區的大小(在第852-854行連接的命令行參數的大小);
2) 攻擊者獨立控制溢出本身的大小和內容(最後一個命令行參數後面緊跟着第一個環境變量,這些變量不包括在第852-853行的大小計算中);
3) 攻擊者甚至可以將空字節寫入溢出的緩衝區(每個以單個反斜槓結尾的命令行參數或環境變量都會將空字節寫入第866-868行的“用戶參數”)。
例如,在amd64 Linux上,下面的命令分配一個24字節的“user\u args”緩衝區(一個32字節的堆塊)並用。
歡迎關注CSDN:知柯信息安全