通过命令行解析越权代码分析 – sudo – (CVE-2021-3156)

sudo 越权(CVE-2021-3156)

[root@localhost ~]# rpm -qa | grep sudo
sudo-debuginfo-1.8.6p7-21.el7_3.x86_64
sudo-1.8.6p7-21.el7_3.x86_64
sudo-devel-1.8.6p7-21.el7_3.x86_64

看一下执行一个命令的时候简单的代码流程

main
  --->parse_args  // 解析命令行参数,会使用‘\’对字符进行转译
  --->set_cmnd    // 解析字符,根据一定的规则解析命令行字符串

这个漏洞主要就是在绕过parse_args字符转译然后再进入set_cmnd函数进行解析出现heap overflow的情形,hacker利用该漏洞实现sudo提权,那具体是体现在set_cmnd的哪里呢,下面具体看一下:

set_cmnd

static int
set_cmnd(void)
{
....

    if (sudo_mode & (MODE_RUN | MODE_EDIT | MODE_CHECK)) {
...

    /* set user_args */
    if (NewArgc > 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++) {
            while (*from) {
            if (from[0] == '\\' && !isspace((unsigned char)from[1]))
                from++;
            *to++ = *from++;
            }
            *to++ = ' ';
        }
        *--to = '\0';
        } else {
...
    }
    }
...
}

代码的关键点是在[19-25]行代码,这段代码是将已经cmdline的参数从NewArgs到user_args的buffer里面,其中关键点是下面这行判断:

if (from[0] == '\\' && !isspace((unsigned char)from[1])) //<======(1)
    from++;
    *to++ = *from++;

正常会判断from[0]字符是否是‘\’开头,然后from[1]又不是空格,利用from++跳过‘\’。例如:

正常

\a----> from[0] = ‘\\' from[1] = ‘a’,那么*to = 'a’

但是有一种特殊情况就是一段字符是以‘\’结束

异常 (注意null不是空格)

xxx\--->from[0] = '\\' from[1] = 'null'

这种情况就会存在问题,我们使用GDB查看一下

[root@localhost ~]# gdb --args sudoedit -s '\' 1234567812345678
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-120.el7
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /usr/bin/sudo...Reading symbols from /usr/lib/debug/usr/bin/sudo.debug...done.
done.
(gdb) 

断点:

b /usr/src/debug/sudo-1.8.6p7/plugins/sudoers/sudoers.c:942

gdb 打印:

Breakpoint 2, set_cmnd () at ./sudoers.c:942
942                     for (to = user_args, av = NewArgv + 1; (from = *av); av++) {
(gdb) p NewArgv[0]
$1 = 0x5608cffa8d5e "sudoedit"
(gdb) p NewArgv[1]
$2 = 0x7fff7884b5ed "\\"
(gdb) p NewArgv[2]
$3 = 0x7fff7884b5ef "1234567812345678"

查看内存的内容

(gdb) x/8xg 0x7fff7884b5ed
0x7fff7884b5ed: 0x363534333231005c      0x3635343332313837
0x7fff7884b5fd: 0x41505f434c003837      0x435f687a3d524550
0x7fff7884b60d: 0x00382d4654552e4e      0x45524444415f434c
0x7fff7884b61d: 0x4e435f687a3d5353      0x5800382d4654552e
(gdb) x/20xb 0x7fff7884b5ed
0x7fff7884b5ed: 0x5c    0x00    0x31    0x32    0x33    0x34    0x35    0x36
0x7fff7884b5f5: 0x37    0x38    0x31    0x32    0x33    0x34    0x35    0x36
0x7fff7884b5fd: 0x37    0x38    0x00    0x4c
(gdb) 

可以看到在NewArgv[1]之后紧接着就是NewArgv[2]的内容:

0x5c --->'\', 0x00--->'null'

这个时候代码from[0] = '\' from[1] = 'null'就会执行from++,之后再执行to++=from++

933  for (size = 0, av = NewArgv + 1; *av; av++)
(gdb) p size
$9 = 19
(gdb) p sudo_user.cmnd_args
$17 = 0x5621ffd68ee0 ""
(gdb) x/8xg 0x5621ffd68ee0
0x5621ffd68ee0: 0x0000000000000000      0x0000000000000000
0x5621ffd68ef0: 0x0000000000000000      0x0000000000009111 <===(1)
0x5621ffd68f00: 0x0000000000000000      0x0000000000000000
0x5621ffd68f10: 0x0000000000000000      0x0000000000000000
(gdb) b /usr/src/debug/sudo-1.8.6p7/plugins/sudoers/sudoers.c:959
Breakpoint 4 at 0x7fcf991e5b71: file ./sudoers.c, line 959.
(gdb) c
Continuing.

Breakpoint 4, set_cmnd () at ./sudoers.c:959
959                     *--to = '\0';
(gdb) x/8xg 0x5621ffd68ee0
0x5621ffd68ee0: 0x3736353433323100      0x3736353433323138
0x5621ffd68ef0: 0x3635343332312038      0x3635343332313837 <===(2)
0x5621ffd68f00: 0x0000000000203837      0x0000000000000000
0x5621ffd68f10: 0x0000000000000000      0x0000000000000000

(gdb) p to
$19 = 0x5621ffd68f02 " "
(gdb) p/d 0x5621ffd68f02-0x5621ffd68ee0
$20 = 34

可以看到字符串变长了,(1)是原来的内存地址的值,利用该漏洞可以修改其他地址的值如(2)处。但是按照正常的流程,进入parse_args函数会进行‘\’转译,具体看一下这个函数

parse_args

int
parse_args(int argc, char **argv, int *nargc, char ***nargv, char ***settingsp,
    char ***env_addp)
{
  ...

    /*
     * For shell mode we need to rewrite argv
     */
    if (ISSET(mode, MODE_RUN) && ISSET(flags, MODE_SHELL)) { // <======(1)
    char **av, *cmnd = NULL;
    int ac = 1;

    if (argc != 0) {
...

        cmnd = dst = emalloc2(cmnd_size, 2);
        for (av = argv; *av != NULL; av++) { 
        for (src = *av; *src != '\0'; src++) {
            /* quote potential meta characters */
            if (!isalnum((unsigned char)*src) && *src != '_' && *src != '-')
            *dst++ = '\\';  //<=======(2)
            *dst++ = *src;
        }
        *dst++ = ' ';
        }
        if (cmnd != dst)
        dst--;  /* replace last space with a NUL */
        *dst = '\0';

        ac += 2; /* -c cmnd */
    }

...
}

可以看到(2)的地方进行转译,如

"xxxx\" --->from[0]='\',from[1]='\' 

可以看到只要正常经过parse_args,也是不会出现上面说的heap overflow的问题,所以可以看到如果想利用该CVE就是要不经过parse_args的转译然后进入set_cmnd解析,那具体是需要什么样的条件代码才会走到这里呢?

parse_args(a)

if (ISSET(mode, MODE_RUN) && ISSET(flags, MODE_SHELL)) 

当mode为MODE_RUN且flags为MODE_SHELL时才成立;

set_cmnd(b)

   if (sudo_mode & (MODE_RUN | MODE_EDIT | MODE_CHECK)) {
      if (ISSET(sudo_mode, MODE_SHELL|MODE_LOGIN_SHELL)) {

可以看到如果不想让代码进入(a)而进入(b)就有一种可能:

(mode != MODE_RUN)&&(sudo_mode == MODE_SHELL&& sudo_mode == MODE_EDIT)

-s 是set MODE_SHELL ,sudoedit是set MODE_EDIT,当然还有其他可能但是在代码里面都已经过滤掉无法成功避免只进b不进a,就不在这里展开了

具体指令就可以如下:

sudoedit -s '\' xxxxxxxxx

小结:这里就是简单分析一下这个CVE代码,了解了这个漏洞其实poc还是满复杂的,一般不易攻破除非有人公布poc方法。

参考链接:https://packetstormsecurity.com/files/161160/Sudo-Heap-Based-Buffer-Overflow.html

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇