美文网首页CTF-PWN
Pwn 知识点总结(1)- 利用 _IO_2_1_stdout_

Pwn 知识点总结(1)- 利用 _IO_2_1_stdout_

作者: Nevv | 来源:发表于2020-04-11 11:53 被阅读0次

利用 _IO_2_1_stdout_泄露libc

1. FILE 结构体

pwndbg> p stdout 
$1 = (struct _IO_FILE *) 0x7ffff7dd0760 <_IO_2_1_stdout_>
pwndbg> ptype stdout
type = struct _IO_FILE {
    int _flags;
    char *_IO_read_ptr;
    char *_IO_read_end;
    char *_IO_read_base;
    char *_IO_write_base;  //  本质上是通过修改这个结构题泄露
    char *_IO_write_ptr;   //  这两个指针地址之间的内容
    char *_IO_write_end;
    char *_IO_buf_base;
    char *_IO_buf_end;
    char *_IO_save_base;
    char *_IO_backup_base;
    char *_IO_save_end;
    struct _IO_marker *_markers;
    struct _IO_FILE *_chain;
    int _fileno;
    int _flags2;
    __off_t _old_offset;
    unsigned short _cur_column;
    signed char _vtable_offset;
    char _shortbuf[1];
    _IO_lock_t *_lock;
    __off64_t _offset;
    struct _IO_codecvt *_codecvt;
    struct _IO_wide_data *_wide_data;
    struct _IO_FILE *_freeres_list;
    void *_freeres_buf;
    size_t __pad5;
    int _mode;
    char _unused2[20];
} *
结构体中的flags
#define _IO_MAGIC 0xFBAD0000 /* Magic number */
#define _OLD_STDIO_MAGIC 0xFABC0000 /* Emulate old stdio. */
#define _IO_MAGIC_MASK 0xFFFF0000
#define _IO_USER_BUF 1 /* User owns buffer; don't delete it on close. */
#define _IO_UNBUFFERED 2
#define _IO_NO_READS 4 /* Reading not allowed */
#define _IO_NO_WRITES 8 /* Writing not allowd */
#define _IO_EOF_SEEN 0x10
#define _IO_ERR_SEEN 0x20
#define _IO_DELETE_DONT_CLOSE 0x40 /* Don't call close(_fileno) on cleanup. */
#define _IO_LINKED 0x80 /* Set if linked (using _chain) to streambuf::_list_all.*/
#define _IO_IN_BACKUP 0x100
#define _IO_LINE_BUF 0x200
#define _IO_TIED_PUT_GET 0x400 /* Set if put and get pointer logicly tied. */
#define _IO_CURRENTLY_PUTTING 0x800
#define _IO_IS_APPENDING 0x1000
#define _IO_IS_FILEBUF 0x2000
#define _IO_BAD_SEEN 0x4000
#define _IO_USER_LOCK 0x8000

_IO_2_1_stdout_一般是这样的,也就是 0xfbad2887:

_IO_MAGIC|_IO_IS_FILEBUF|_IO_CURRENTLY_PUTTING|_IO_LINKED|_IO_NO_READS | _IO_UNBUFFERED |_IO_USER_BUF
如何leak

为了泄露处一些libc上的地址,我们需要修改 _IO_2_1_stdout_的_flags、_IO_write_base、_IO_write_ptr。步骤如下:

  • 一般我们将_flags设置为0xfbad18**。目的是为了设置_IO_CURRENTLY_PUTTING=0x800,_IO_IS_APPENDING=0x1000,IO_MAGIC=0xFBAD0000 (后续看puts实现就会知道为什么要这样设置)
  • 设置_IO_write_base指向想要泄露的地方;_IO_write_ptr指向泄露结束的地址。
  • 之后遇到puts或printf,就会将_IO_write_base指向的内容打印出来。
常见payload
  • p64(0xfbad1800)+p64(0x0)*3+'\x00'

至于为什么将以下三个指针设置为0,原因在put函数的执行流程中可以一探究竟。

    char *_IO_read_ptr;
    char *_IO_read_end;
    char *_IO_read_base;

2. put函数执行流程

IO_sputn
  • 实际最终调用的是 _IO_2_1_stdout_vtable中的__xsputn,也就是_IO_new_file_xsputn函数。

最终会调用到_IO_OVERFLOW,也就是_IO_new_file_overflow,可以看到这里要求 #define _IO_NO_WRITES 8 /* Writing not allowd */标志位不为1,否则就会返回错误。

int
_IO_new_file_overflow (_IO_FILE *f, int ch)
{
  if (f->_flags & _IO_NO_WRITES) /* SET ERROR */
    {
      f->_flags |= _IO_ERR_SEEN;
      __set_errno (EBADF);
      return EOF;
    }
  /* If currently reading or no buffer allocated. */
  // 将 #define _IO_CURRENTLY_PUTTING 0x800置为1  绕过这个if判断,通常没有输出过的话是0,程序输出过就为1
  if ((f->_flags & _IO_CURRENTLY_PUTTING) == 0 || f->_IO_write_base == NULL)
    {
      /* Allocate a buffer if needed. */
      if (f->_IO_write_base == NULL)
    {
      _IO_doallocbuf (f);
      _IO_setg (f, f->_IO_buf_base, f->_IO_buf_base, f->_IO_buf_base);
    }
      /* Otherwise must be currently reading.
     If _IO_read_ptr (and hence also _IO_read_end) is at the buffer end,
     logically slide the buffer forwards one block (by setting the
     read pointers to all point at the beginning of the block).  This
     makes room for subsequent output.
     Otherwise, set the read pointers to _IO_read_end (leaving that
     alone, so it can continue to correspond to the external position). */
      if (__glibc_unlikely (_IO_in_backup (f)))
    {
      size_t nbackup = f->_IO_read_end - f->_IO_read_ptr;
      _IO_free_backup_area (f);
      f->_IO_read_base -= MIN (nbackup,
                   f->_IO_read_base - f->_IO_buf_base);
      f->_IO_read_ptr = f->_IO_read_base;
    }
      if (f->_IO_read_ptr == f->_IO_buf_end)
    f->_IO_read_end = f->_IO_read_ptr = f->_IO_buf_base;
      f->_IO_write_ptr = f->_IO_read_ptr;
      f->_IO_write_base = f->_IO_write_ptr;
      f->_IO_write_end = f->_IO_buf_end;
      f->_IO_read_base = f->_IO_read_ptr = f->_IO_read_end;
      f->_flags |= _IO_CURRENTLY_PUTTING;
      if (f->_mode <= 0 && f->_flags & (_IO_LINE_BUF | _IO_UNBUFFERED))
    f->_IO_write_end = f->_IO_write_ptr;
    }
  if (ch == EOF)        
    return _IO_do_write (f, f->_IO_write_base,
             f->_IO_write_ptr - f->_IO_write_base);
  if (f->_IO_write_ptr == f->_IO_buf_end ) /* Buffer is really full */
    if (_IO_do_flush (f) == EOF)
      return EOF;
  *f->_IO_write_ptr++ = ch;
  if ((f->_flags & _IO_UNBUFFERED)
      || ((f->_flags & _IO_LINE_BUF) && ch == '\n'))
    if (_IO_do_write (f, f->_IO_write_base,
              f->_IO_write_ptr - f->_IO_write_base) == EOF)
      return EOF;
  return (unsigned char) ch;
}
libc_hidden_ver (_IO_new_file_overflow, _IO_file_overflow)
IO_do_write
  • 会跳进new_do_write函数
static
_IO_size_t
new_do_write (_IO_FILE *fp, const char *data, _IO_size_t to_do)
{
  _IO_size_t count;
  if (fp->_flags & _IO_IS_APPENDING)   // 所以这里要设置 _IO_IS_APPENDING 为1,否则不会有输出
    /* On a system without a proper O_APPEND implementation,
       you would need to sys_seek(0, SEEK_END) here, but is
       not needed nor desirable for Unix- or Posix-like systems.
       Instead, just indicate that offset (before and after) is
       unpredictable. */
    fp->_offset = _IO_pos_BAD;
  else if (fp->_IO_read_end != fp->_IO_write_base)  //  或者设置stdout->_IO_read_end == stdout->_IO_write_base
    {
      _IO_off64_t new_pos
    = _IO_SYSSEEK (fp, fp->_IO_write_base - fp->_IO_read_end, 1);
      if (new_pos == _IO_pos_BAD)
    return 0;
      fp->_offset = new_pos;
    }
  count = _IO_SYSWRITE (fp, data, to_do);
  if (fp->_cur_column && count)
    fp->_cur_column = _IO_adjust_column (fp->_cur_column - 1, data, count) + 1;
  _IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_base, fp->_IO_buf_base);
  fp->_IO_write_base = fp->_IO_write_ptr = fp->_IO_buf_base;
  fp->_IO_write_end = (fp->_mode <= 0
               && (fp->_flags & (_IO_LINE_BUF | _IO_UNBUFFERED))
               ? fp->_IO_buf_base : fp->_IO_buf_end);
  return count;
}

3. 利用

​ 一般都是利用unsorted bin的在tcachefastbin的fd上main_arena的地址(同一个chunk既在unsortbin上,也在tcache上),配合UAF之类的漏洞覆盖低位爆破stdout的地址,然后分配到stdout结构体处,达到泄露的目的。

​ 对于没有tcache的 glibc 版本,使用 fastbin attack就好,因为_IO_2_1_stdout_上面就是_IO_2_1_stderr_stderr->__pad2一般是指向_IO_wide_data_2的指针,而_IO_wide_data_2是在libc中的,所以我们可以用其来伪造size。

相关文章

  • Pwn 知识点总结(1)- 利用 _IO_2_1_stdout_

    利用 _IO_2_1_stdout_泄露libc 1. FILE 结构体 结构体中的flags _IO_2_1_s...

  • 新手科普 | CTF PWN堆溢出总结

    新手科普 | CTF PWN堆溢出总结 pwn堆溢出基础 CTF pwn 中最通俗易懂的堆入坑指南CTF pwn ...

  • 【pwnable.tw 系列】start

    概述:本题是pwn的入门级题目,几乎把所有利用的难度都降到最低,应该只是用来让入门者大致了解pwn题的玩法。 1、...

  • b00ks writeup

    混子pwn手,最近在看堆的知识点,刚看完CTF-WiKi上的堆的基础知识,开始学习offbyone的利用先介绍一下...

  • pwn入门的一些学习资料

    总结记录一下pwn入门的一些学习资料 pwn入门学习的网站: CTF Wiki 必备技能: 汇编语言要搞pwn首先...

  • 学习小组Day1笔记--大羽

    一、思维导图 二、知识点总结 知识点1:解决学习中遇到的问题 (1)可以通过引擎搜索或者利用星球学习的资源,即小组...

  • 开坑入PWN第一步

    开始开坑学习PWN了,老师希望我能学PWN,总结PWN题的规律日后好挖洞,于是一个妄想成为web手的菜狗转战大表哥...

  • 安恒一月赛2019 PWN

    0x01 pwn1 0x02 pwn2

  • 本节介绍

    本节主要系统的介绍Pwn中的比较系统的一些学习记录。 自我总结的学习路线 基础 进阶 提高 前置技能 学习Pwn ...

  • 看雪CTF2019Q3第四题:利用_IO_2_1_stout_泄

    1. 基础知识:利用_IO_2_1_stout_泄露信息 在CTF比赛的pwn题目,或者真实的环境下,有时候程序不...

网友评论

    本文标题:Pwn 知识点总结(1)- 利用 _IO_2_1_stdout_

    本文链接:https://www.haomeiwen.com/subject/uxqymhtx.html