CVE-2021-3156:Sudo中基于堆的缓冲区溢出 (Baron Samedit)

admin 2021年1月28日00:45:50评论52 views字数 4969阅读16分33秒阅读模式


CVE-2021-3156:Sudo中基于堆的缓冲区溢出 (Baron Samedit)


CVE-2021-3156:Sudo中基于堆的缓冲区溢出 (Baron Samedit)



            sudo中的堆溢出漏洞,该漏洞在类似Unix的主要操作系统上都可以使用。通过利用此漏洞,任何没有特权的用户都可以使用默认的sudo配置在易受攻击的主机上获得root特权。


            Sudo是一个功能强大的实用程序,大多数(如果不是全部)基于Unix和Linux的操作系统都包含Sudo。它允许用户使用其他用户的安全特权运行程序。该漏洞本身已经隐藏了将近十年。它于2011年7月引入(提交8255ed69),在其默认配置下会影响从1.8.2到1.8.31p2的所有旧版本以及从1.9.0到1.9.5p1的所有稳定版本。


            成功利用此漏洞,任何没有特权的用户都可以在易受攻击的主机上获得root特权。Qualys安全研究人员已经能够在Ubuntu 20.04(Sudo 1.8.31),Debian 10(Sudo 1.8.27)和Fedora 33(Sudo 1.9.2)上独立验证漏洞并开发多种利用漏洞并获得完整的root用户特权。其他操作系统和发行版也可能会被利用。


            

            视频验证:https://player.vimeo.com/video/504872555


技术细节

如果执行了Sudo以在“ shell”模式下运行命令(shell -c命令):

  • 通过-s选项设置Sudo的MODE_SHELL标志;或者 要么

  • 通过-i选项,该选项设置Sudo的MODE_SHELL和MODE_LOGIN_SHELL标志;然后,在Sudo main()的开头,parse_args()通过连接所有命令行参数(第587-595行)并用反斜杠转义所有元字符(第590-591行)来重写argv(第609-617行) ):


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 != ''; 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; 615616         argv = av; 617         argc = ac; 618     }


稍后,在sudoers_policy_main()中,set_cmnd()将命令行参数连接到基于堆的缓冲区“ user_args”(行864-871)中,并取消转义元字符(行866-867),“用于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_args”缓冲区,并再次从“ from”开始递增,并指向空终止符之后的第一个字符(即,超出参数的范围);

  • 第865-869行的“ while”循环读取越界字符并将其复制到“ user_args”缓冲区。

换句话说,set_cmnd()容易受到基于堆的缓冲区溢出的影响,因为复制到“ user_args”缓冲区的越界字符不包括在其大小中(在第852-853行计算)。


但是,从理论上讲,任何命令行参数都不能以单个反斜杠字符结尾:如果设置了MODE_SHELL或MODE_LOGIN_SHELL(第858行,这是到达易受攻击的代码的必要条件),则设置了MODE_SHELL(第571行)和parse_args()已经转义了包括反斜杠在内的所有元字符(即,它以第二个反斜杠转义了每个单个反斜杠)。


但是实际上,set_cmnd()中的易受攻击的代码和parse_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)) {


我们的问题是:是否可以设置MODE_SHELL以及MODE_EDIT或MODE_CHECK(以访问易受攻击的代码),但不能设置默认的MODE_RUN(以避免转义代码)?


答案似乎没有:如果我们设置MODE_EDIT(-e选项,第361行)或MODE_CHECK(-l选项,第423和519行),则parse_args()将从“ valid_flags”(第363和424行)中删除MODE_SHELL ),如果我们指定了无效的标志(例如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);


但是我们发现了一个漏洞:如果我们以“ sudoedit”而不是“ sudo”的身份执行Sudo,则parse_args()会自动设置MODE_EDIT(第270行),但不会重置“ valid_flags”,并且默认情况下,“ valid_flags”包括MODE_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”,那么我们将同时设置MODE_EDIT和MODE_SHELL(但不设置MODE_RUN),则避免转义代码,到达易受攻击的代码,并通过命令行溢出基于堆的缓冲区“ user_args”以单个反斜杠字符结尾的参数:


sudoedit -s '' `perl -e 'print "A" x 65536'` malloc(): corrupted top size Aborted (core dumped)


从攻击者的角度来看,由于以下原因,此缓冲区溢出是理想的:


1)攻击者控制可能溢出的“ user_args”缓冲区的大小(我们串联的命令行参数的大小,在852-854行);

2)攻击者独立控制溢出本身的大小和内容(我们的最后一个命令行参数后面是我们的第一个环境变量,该变量未包含在第852-853行的大小计算中);

3)攻击者甚至可以将空字节写入溢出的缓冲区(每个命令行参数或以单个反斜杠结尾的环境变量在866-868行将空字节写入“ user_args”)


例如,在amd64 Linux上,以下命令分配一个24字节的“ user_args”缓冲区(一个32字节的堆块),并用“ A = a 0B = b 0”(0x00623d4200613d41)覆盖下一个块的大小字段。,其fd字段为“ C = c 0D = d 0”(0x00643d4400633d43),其bk字段为“ E = e 0F = f 0”(0x00663d4600653d45):


env -i 'AA=a' 'B=b' 'C=c' 'D=d' 'E=e' 'F=f' sudoedit -s '1234567890123456789012'--------------------------------------------------------------------- 
--|--------+--------+--------+--------|--------+--------+--------+--------+-- | | |12345678|90123456|789012.A|A=a.B=b.|C=c.D=d.|E=e.F=f.| --|--------+--------+--------+--------|--------+--------+--------+--------+--
size <---- user_args buffer ----> size fd bk


Qualys覆盖范围

QID 374891:基于Sudo堆的缓冲区溢出漏洞

QID在vulnsigs版本VULNSIGS-2.5.90-4和Linux Cloud Agent清单版本lx_manifest-2.5.90.4-3中可用。


参考文献:

https://blog.qualys.com/vulnerabilities-research/2021/01/26/cve-2021-3156-heap-based-buffer-overflow-in-sudo-baron-samedit


https://www.qualys.com/2021/01/26/cve-2021-3156/baron-samedit-heap-based-overflow-sudo.txt

本文始发于微信公众号(Khan安全团队):CVE-2021-3156:Sudo中基于堆的缓冲区溢出 (Baron Samedit)

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2021年1月28日00:45:50
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   CVE-2021-3156:Sudo中基于堆的缓冲区溢出 (Baron Samedit)http://cn-sec.com/archives/255860.html

发表评论

匿名网友 填写信息