前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >CVE-2021-3156:Sudo中基于堆的缓冲区溢出 (Baron Samedit)

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

作者头像
Khan安全团队
发布2021-02-12 13:03:37
8270
发布2021-02-12 13:03:37
举报
文章被收录于专栏:Khan安全团队Khan安全团队

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行) ):
代码语言:javascript
复制
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_policy_main()中,set_cmnd()将命令行参数连接到基于堆的缓冲区“ user_args”(行864-871)中,并取消转义元字符(行866-867),“用于sudoers匹配和记录目的”:

代码语言:javascript
复制
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()中的转义代码被稍有不同的条件所包围:

代码语言:javascript
复制
819     if (sudo_mode & (MODE_RUN | MODE_EDIT | MODE_CHECK)) { 
... 
858             if (ISSET(sudo_mode, MODE_SHELL|MODE_LOGIN_SHELL)) {
代码语言:javascript
复制
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行),则会出错退出:

代码语言:javascript
复制
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):

代码语言:javascript
复制
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”以单个反斜杠字符结尾的参数:

代码语言:javascript
复制
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):

代码语言:javascript
复制
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

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-01-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Khan安全团队 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 技术细节
  • Qualys覆盖范围
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档