#

# IO_buf_base 劫持技术,

# 文章写的会比较凌乱,是在一边解题同时记录

# 环境以及保护

giantbranch@ubuntu:~/Desktop/pwnabletw/hijack_io_buf_base$ strings libc.so.6 | grep ubuntu 
GNU C Library (Ubuntu GLIBC 2.23-0ubuntu11) stable release version 2.23, by Roland McGrath et al.
<https://bugs.launchpad.net/ubuntu/+source/glibc/+bugs>.
giantbranch@ubuntu:~/Desktop/pwnabletw/hijack_io_buf_base$ checksec --file=tw2017parrot
[*] '/home/giantbranch/Desktop/pwnabletw/hijack_io_buf_base/tw2017parrot'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      PIE enabled
giantbranch@ubuntu:~/Desktop/pwnabletw/hijack_io_buf_base$ file tw2017parrot 
tw2017parrot: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=00fbccd873daf9400480cbb4dbd48845f1bb97b8, not stripped
giantbranch@ubuntu:~/Desktop/pwnabletw/hijack_io_buf_base$

ubuntu2.23 下的题目,只有 canary 保护没有开启,没有思路

# 主要代码

int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  size_t size; // [rsp+8h] [rbp-18h] BYREF
  void *buf; // [rsp+10h] [rbp-10h]
  unsigned __int64 v5; // [rsp+18h] [rbp-8h]
  v5 = __readfsqword(0x28u);
  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(stdout, 0LL, 2, 0LL);
  sleep(3u);
  while ( 1 )
  {
    puts("Size:");
    _isoc99_scanf("%lu", &size);
    getchar();
    if ( !size )
      break;
    buf = malloc(size);
    puts("Buffer:");
    read(0, buf, size);
    *((_BYTE *)buf + size - 1) = 0;			// 这个很关键
    write(1, buf, size);
    free(buf);
  }
  exit(0);
}

# 分析

# 初步印象

代码很简单,malloc,free 没有多余的操作,但是 malloc 不会清空申请的堆块中的内存,同时我们可以输入空。但是只有一个堆块,紧邻 topchunk, 无法利用 unsortedbin 泄露 main_arena, 铃响其他办法

malloc 的一个机制

当我们在应用层调用 malloc 申请堆的时候,在 glibc 中实际上调用的是_lib_malloc 函数,但是_lib_malloc 函数只是用来简单的封装_int_malloc 函数的,_int_malloc 函数才是申请堆的核心函数。
_int_malloc 会根据应用层用户申请的内存块大小,从而分配相应的 chunk 给用户使用。

函数的分配堆内存的主要执行流程
①请求大小在 fastbin 的范围内:在 fastbins 中找是否有对应的 chunk 可以使用。
②请求大小在 smallbin 的范围内:在 smallbin 中找是否有对应的 chunk 可以使用。
③请求大小在 largebin 的范围内:先调用 malloc_consolidate 对 fastbins 进行整理加入 unsortedbins。然后在 unsortedbin 中查看是否有满足要求的 chunk 可以使用。
④在 largebin 中寻找可用的 chunk 来使用。
⑤寻找较大的 bin 链中是否有可用的 chunk 来使用。
⑥切割 topchunk 来使用。
⑦topchunk 也不够了,再次调用 malloc_consolidate 整理 fastbins。
⑧topchunk 不够用,再次 malloc_consolidate 之后还没有可以用的,最终调用 sysmalloc(系统调用)申请内存。

pwndbg> p *(struct _IO_FILE_plus *)stdin
$1 = {
  file = {
    _flags = -72540021, 
    _IO_read_ptr = 0x7f0883e32964 <_IO_2_1_stdin_+132> "", 
    _IO_read_end = 0x7f0883e32964 <_IO_2_1_stdin_+132> "", 
    _IO_read_base = 0x7f0883e32963 <_IO_2_1_stdin_+131> "\n", 
    _IO_write_base = 0x7f0883e32963 <_IO_2_1_stdin_+131> "\n", 
    _IO_write_ptr = 0x7f0883e32963 <_IO_2_1_stdin_+131> "\n", 
    _IO_write_end = 0x7f0883e32963 <_IO_2_1_stdin_+131> "\n", 
    _IO_buf_base = 0x7f0883e32963 <_IO_2_1_stdin_+131> "\n", 
    _IO_buf_end = 0x7f0883e32964 <_IO_2_1_stdin_+132> "", 
    _IO_save_base = 0x0, 
    _IO_backup_base = 0x0, 
    _IO_save_end = 0x0, 
    _markers = 0x0, 
    _chain = 0x0, 
    _fileno = 0, 
    _flags2 = 0, 
    _old_offset = -1, 
    _cur_column = 0, 
    _vtable_offset = 0 '\000', 
    _shortbuf = "\n", 
    _lock = 0x7f0883e34790 <_IO_stdfile_0_lock>, 
    _offset = -1, 
    _codecvt = 0x0, 
    _wide_data = 0x7f0883e329c0 <_IO_wide_data_0>, 
    _freeres_list = 0x0, 
    _freeres_buf = 0x0, 
    __pad5 = 0, 
    _mode = -1, 
    _unused2 = '\000' <repeats 19 times>
  }, 
  vtable = 0x7f0883e316e0 <_IO_file_jumps>
}

这是正常的 stdin 的 IO_FILE_plus 结构体,_IO_buf_base 是输入缓冲区开始的地方(其实应该是 read_base 是开始的地方,但是这里肯可能是说 read_base = buf_base),记得程序代码那个末尾写 0 吗。在这里将 _IO_buf_base 最低位改为 0. 这个地址其实是_IO_write_base, 这里就可以实现_IO_FILE_plus 的覆盖

pwndbg> p *(struct _IO_FILE_plus *)stdin
$3 = {
  file = {
    _flags = -72539989, 
    _IO_read_ptr = 0x7f0883e32901 <_IO_2_1_stdin_+33> "", 
    _IO_read_end = 0x7f0883e32928 <_IO_2_1_stdin_+72> "", 
    _IO_read_base = 0x7f0883e32900 <_IO_2_1_stdin_+32> "", 
    _IO_write_base = 0x0, 
    _IO_write_ptr = 0x0, 
    _IO_write_end = 0x0, 
    _IO_buf_base = 0x7f0883e347a8 <__free_hook> "", 
    _IO_buf_end = 0x7f0883e347b8 <next_to_use> "", 
    _IO_save_base = 0x0, 
    _IO_backup_base = 0x0, 
    _IO_save_end = 0x0, 
    _markers = 0x0, 
    _chain = 0x0, 
    _fileno = 0, 
    _flags2 = 0, 
    _old_offset = -1, 
    _cur_column = 0, 
    _vtable_offset = 0 '\000', 
    _shortbuf = "\n", 
    _lock = 0x7f0883e34790 <_IO_stdfile_0_lock>, 
    _offset = -1, 
    _codecvt = 0x0, 
    _wide_data = 0x7f0883e329c0 <_IO_wide_data_0>, 
    _freeres_list = 0x0, 
    _freeres_buf = 0x0, 
    __pad5 = 0, 
    _mode = -1, 
    _unused2 = '\000' <repeats 19 times>
  }, 
  vtable = 0x7f0883e316e0 <_IO_file_jumps>
}