fsop深入学习

1.相关结构复习

画成思维导图了:思维导图

2.相关代码复习

libc2.24以上的检查代码

static inline const struct _IO_jump_t *
IO_validate_vtable (const struct _IO_jump_t *vtable)
{
  /* Fast path: The vtable pointer is within the __libc_IO_vtables
     section.  */
  uintptr_t section_length = __stop___libc_IO_vtables - __start___libc_IO_vtables;
  const char *ptr = (const char *) vtable;
  uintptr_t offset = ptr - __start___libc_IO_vtables;
  if (__glibc_unlikely (offset >= section_length))
    /* The vtable pointer is not in the expected section.  Use the
       slow path, which will terminate the process if necessary.  */
    _IO_vtable_check ();
  return vtable;
}

vtable必须要满足 在 __stop___IO_vtables 和 __start___libc_IO_vtables 之间,而我们伪造的vtable通常不满足这个条件。但是_IO_str_jumps 与__IO_wstr_jumps就位于 __stop___libc_IO_vtables 和 __start___libc_IO_vtables 之间, 所以我们是可以利用他们来通过 IO_validate_vtable 的检测的,只需要将vtable填成_IO_str_jumps 或__IO_wstr_jumps就行。
利用方式两种:

    1.利用__IO_str_jumps中的_IO_str_finsh函数
    2.利用__IO_str_jumps中的_IO_str_overflow函数

3.利用方式

利用_IO_str_finsh函数实现函数调用

void _IO_str_finish (FILE *fp, int dummy)
{
  if (fp->_IO_buf_base && !(fp->_flags & 1))
    ((void (*)(void))fp + 0xE8 ) (fp->_IO_buf_base); // call qword ptr [fp+E8h]
  fp->_IO_buf_base = NULL;
  _IO_default_finish (fp, 0);
}

条件:

  1. fp->_IO_buf_base为真

  2. fp->_flags & 1 为假 // fp->_flags=0

fp+E8处填成system_addr ,还有 fp->_IO_buf_base填上binsh_addr地址主要是利用__IO_str_jumps地址错位的方法,通过_IO_str_jumps - 8来调用_IO_str_finish

利用_IO_str_overflow函数实现函数调用

__int64 __fastcall IO_str_overflow(_IO_FILE *fp, unsigned int a2)
{
  int v2; // ecx
  signed __int64 result; // rax
  _QWORD *v4; // rdx
  char *v5; // r12
  unsigned __int64 v6; // r13
  unsigned __int64 v7; // r14
  __int64 v8; // rax
  __int64 v9; // r15
  _QWORD *v10; // rax
  _QWORD *v11; // rax

  v2 = fp->_flags;
  if ( fp->_flags & 8 )
    return (unsigned int)-(a2 != -1);
  if ( (fp->_flags & 0xC00) == 0x400 )
  {
    v4 = fp->_IO_read_ptr;
    v11 = fp->_IO_read_end;
    BYTE1(v2) |= 8u;
    LODWORD(fp->_flags) = v2;
    fp->_IO_write_ptr = v4;
    fp->_IO_read_ptr = v11;
  }
  else
  {
    v4 = fp->_IO_write_ptr;
  }
  v6 = (char *)fp->_IO_buf_end - (char *)fp->_IO_buf_base
  if ( (char *)v4 - (char *)fp->_IO_write_base >= v6 + (a2 == -1) )
  {
    if ( v2 & 1 )
      return 0xFFFFFFFFLL;
    v7 = 2 * v6 + 100;
    if ( v6 > v7 )
      return 0xFFFFFFFFLL;
    v8 = ((__int64 (__fastcall *)(unsigned __int64)) fp + 0xE0)(2 * v6 + 100); // call
    v9 = v8;
    .........
  }
  .......
  .......
}

条件:

  1. fp->_flags = 0
  2. fp->_IO_buf_base = 0
  3. fp->_IO_buf_end = (bin_sh_addr - 100) / 2#如果bin/sh地址以奇数结尾可以+1以避免向下取整
  4. fp->_IO_write_ptr = 0xffffffff
  5. fp->_IO_write_base = 0
  6. fp->_mode = 0
  7. fp+0xe0 = system
     

fp->_flags & 8 得为0,(fp->_flags & 0xC00) == 0x400 得为0 , fp->_flags & 1 得为0, 所以我这里就将fp->_flags设置为0 ; 并设置fp->_IO_write_ptr - fp->_IO_write_base > fp->_IO_buf_end - fp->_IO_buf_base
这样我们才能够绕过检查,来到函数调用(fp + 0xE0)(2 * v6 + 100)也就是 (fp + 0xE0)(2 * (fp->_IO_buf_end - fp->_IO_buf_base) + 100)
我们可以将 fp + 0x E0设置成 system函数地址 ,fp->_IO_buf_base设置成0,fp->_IO_buf_end设置成 (binsh_addr-100)/2这样就设置了system参数,fp->_IO_write_ptr设置成一个很大的正值
这里有个坑,就是(binsh_addr-100)/2, 当/bin/sh地址存在于libc中,它/2再*2 若是除不尽有余数的话 会影响最后的参数地址,我们为了避免这种情况我们尽量在堆内存中填入"/bin/sh\x00", 因为堆内存往往都是2的倍数对齐。

 

进行任意地址读写

大佬的详细博客


 

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值