首页
社区
课程
招聘
原创]无路远征——GLIBC2.37后时代的IO攻击之道(一)house_of_琴瑟琵琶
发表于: 2023-2-4 17:31 20725

原创]无路远征——GLIBC2.37后时代的IO攻击之道(一)house_of_琴瑟琵琶

2023-2-4 17:31
20725

此攻击方式可以在2.34-2.36使用,至于更早的版本,本人没有使用过,看源码应该可以。

这个攻击手段主要是利用_IO_obstack_jumps,其中_IO_obstack_overflow_IO_obstack_xsputn 都可以触发,攻击链如下。

但实际过程中_IO_obstack_overflow容易触发assert (c != EOF); ,所以一般选择第二条链。

可以看出其中只有2个函数有赋值,其他都为空。

_IO_obstack_file只是在_IO_FILE_plus后面加了一个obstack的指针,我们可以利用错位利用法。

在刚才跳表中,仅_IO_obstack_overflow_IO_obstack_xsputn两个函数有赋值,函数内容如下。为了避免绕过_IO_obstack_overflow中的assert (c != EOF);我们一般用_IO_obstack_xsputn

简单绕过一些内容后用来触发_obstack_newchunk

触发CALL_CHUNKFUN

显然这里使用了结构体中的指针(*(h)->chunkfun)((h)->extra_arg, (size)),并且第一个参数可控,同时需要保证(((h)->use_extra_arg)为1。

我知道很多师傅就是来抄板子的。

2.37之后取消_IO_obstack_jumps但此链通过某种手段仍然有效。

后记:
一次偶然的机会发现,有外国友人也发现了这条链,https://nasm.re/posts/babyfile/#obstack-exploitation
本人没有抢注的意思,只是这个是我自己研究发现的,而且这条链比较简单,所以也写了出来。

_IO_obstack_overflow
        obstack_1grow (obstack, c);
                _obstack_newchunk (__o, 1);   
                         new_chunk = CALL_CHUNKFUN (h, new_size);
                                     (*(h)->chunkfun)((h)->extra_arg, (size))
_IO_obstack_overflow
        obstack_1grow (obstack, c);
                _obstack_newchunk (__o, 1);   
                         new_chunk = CALL_CHUNKFUN (h, new_size);
                                     (*(h)->chunkfun)((h)->extra_arg, (size))
_IO_obstack_xsputn
        obstack_grow (obstack, data, n);;
                _obstack_newchunk (__o, __len);
                         new_chunk = CALL_CHUNKFUN (h, new_size);
                                     (*(h)->chunkfun)((h)->extra_arg, (size))
_IO_obstack_xsputn
        obstack_grow (obstack, data, n);;
                _obstack_newchunk (__o, __len);
                         new_chunk = CALL_CHUNKFUN (h, new_size);
                                     (*(h)->chunkfun)((h)->extra_arg, (size))
/* the jump table.  */
const struct _IO_jump_t _IO_obstack_jumps libio_vtable attribute_hidden =
{
  JUMP_INIT_DUMMY,
  JUMP_INIT(finish, NULL),
  JUMP_INIT(overflow, _IO_obstack_overflow),
  JUMP_INIT(underflow, NULL),
  JUMP_INIT(uflow, NULL),
  JUMP_INIT(pbackfail, NULL),
  JUMP_INIT(xsputn, _IO_obstack_xsputn),
  JUMP_INIT(xsgetn, NULL),
  JUMP_INIT(seekoff, NULL),
  JUMP_INIT(seekpos, NULL),
  JUMP_INIT(setbuf, NULL),
  JUMP_INIT(sync, NULL),
  JUMP_INIT(doallocate, NULL),
  JUMP_INIT(read, NULL),
  JUMP_INIT(write, NULL),
  JUMP_INIT(seek, NULL),
  JUMP_INIT(close, NULL),
  JUMP_INIT(stat, NULL),
  JUMP_INIT(showmanyc, NULL),
  JUMP_INIT(imbue, NULL)
};
/* the jump table.  */
const struct _IO_jump_t _IO_obstack_jumps libio_vtable attribute_hidden =
{
  JUMP_INIT_DUMMY,
  JUMP_INIT(finish, NULL),
  JUMP_INIT(overflow, _IO_obstack_overflow),
  JUMP_INIT(underflow, NULL),
  JUMP_INIT(uflow, NULL),
  JUMP_INIT(pbackfail, NULL),
  JUMP_INIT(xsputn, _IO_obstack_xsputn),
  JUMP_INIT(xsgetn, NULL),
  JUMP_INIT(seekoff, NULL),
  JUMP_INIT(seekpos, NULL),
  JUMP_INIT(setbuf, NULL),
  JUMP_INIT(sync, NULL),
  JUMP_INIT(doallocate, NULL),
  JUMP_INIT(read, NULL),
  JUMP_INIT(write, NULL),
  JUMP_INIT(seek, NULL),
  JUMP_INIT(close, NULL),
  JUMP_INIT(stat, NULL),
  JUMP_INIT(showmanyc, NULL),
  JUMP_INIT(imbue, NULL)
};
struct _IO_obstack_file
{
  struct _IO_FILE_plus file;
  struct obstack *obstack;
};
 
struct obstack          /* control current object in current chunk */
{
  long chunk_size;              /* preferred size to allocate chunks in */
  struct _obstack_chunk *chunk; /* address of current struct obstack_chunk */
  char *object_base;            /* address of object we are building */
  char *next_free;              /* where to add next char to current object */
  char *chunk_limit;            /* address of char after current chunk */
  union
  {
    PTR_INT_TYPE tempint;
    void *tempptr;
  } temp;                       /* Temporary for some macros.  */
  int alignment_mask;           /* Mask of alignment for each object. */
  /* These prototypes vary based on 'use_extra_arg', and we use
     casts to the prototypeless function type in all assignments,
     but having prototypes here quiets -Wstrict-prototypes.  */
  struct _obstack_chunk *(*chunkfun) (void *, long);
  void (*freefun) (void *, struct _obstack_chunk *);
  void *extra_arg;              /* first arg for chunk alloc/dealloc funcs */
  unsigned use_extra_arg : 1;     /* chunk alloc/dealloc funcs take extra arg */
  unsigned maybe_empty_object : 1; /* There is a possibility that the current
                      chunk contains a zero-length object.  This
                      prevents freeing the chunk if we allocate
                      a bigger chunk to replace it. */
  unsigned alloc_failed : 1;      /* No longer used, as we now call the failed
                     handler on error, but retained for binary
                     compatibility.  */
};
struct _IO_obstack_file
{
  struct _IO_FILE_plus file;
  struct obstack *obstack;
};
 
struct obstack          /* control current object in current chunk */
{
  long chunk_size;              /* preferred size to allocate chunks in */
  struct _obstack_chunk *chunk; /* address of current struct obstack_chunk */
  char *object_base;            /* address of object we are building */
  char *next_free;              /* where to add next char to current object */
  char *chunk_limit;            /* address of char after current chunk */
  union
  {
    PTR_INT_TYPE tempint;
    void *tempptr;
  } temp;                       /* Temporary for some macros.  */
  int alignment_mask;           /* Mask of alignment for each object. */
  /* These prototypes vary based on 'use_extra_arg', and we use
     casts to the prototypeless function type in all assignments,
     but having prototypes here quiets -Wstrict-prototypes.  */
  struct _obstack_chunk *(*chunkfun) (void *, long);
  void (*freefun) (void *, struct _obstack_chunk *);
  void *extra_arg;              /* first arg for chunk alloc/dealloc funcs */
  unsigned use_extra_arg : 1;     /* chunk alloc/dealloc funcs take extra arg */
  unsigned maybe_empty_object : 1; /* There is a possibility that the current
                      chunk contains a zero-length object.  This
                      prevents freeing the chunk if we allocate
                      a bigger chunk to replace it. */
  unsigned alloc_failed : 1;      /* No longer used, as we now call the failed
                     handler on error, but retained for binary
                     compatibility.  */
};
static int _IO_obstack_overflow (FILE *fp, int c)
{
  struct obstack *obstack = ((struct _IO_obstack_file *) fp)->obstack;
  int size;
 
  /* Make room for another character.  This might as well allocate a
     new chunk a memory and moves the old contents over.  */
  assert (c != EOF);  // 此处不可控
  obstack_1grow (obstack, c);
 
  /* Setup the buffer pointers again.  */
  fp->_IO_write_base = obstack_base (obstack);
  fp->_IO_write_ptr = obstack_next_free (obstack);
  size = obstack_room (obstack);
  fp->_IO_write_end = fp->_IO_write_ptr + size;
  /* Now allocate the rest of the current chunk.  */
  obstack_blank_fast (obstack, size);
 
  return c;
}
static int _IO_obstack_overflow (FILE *fp, int c)
{
  struct obstack *obstack = ((struct _IO_obstack_file *) fp)->obstack;
  int size;
 
  /* Make room for another character.  This might as well allocate a
     new chunk a memory and moves the old contents over.  */
  assert (c != EOF);  // 此处不可控
  obstack_1grow (obstack, c);
 
  /* Setup the buffer pointers again.  */
  fp->_IO_write_base = obstack_base (obstack);
  fp->_IO_write_ptr = obstack_next_free (obstack);
  size = obstack_room (obstack);
  fp->_IO_write_end = fp->_IO_write_ptr + size;
  /* Now allocate the rest of the current chunk.  */
  obstack_blank_fast (obstack, size);
 
  return c;
}
static size_t _IO_obstack_xsputn (FILE *fp, const void *data, size_t n)
{
  struct obstack *obstack = ((struct _IO_obstack_file *) fp)->obstack;
 
  if (fp->_IO_write_ptr + n > fp->_IO_write_end)
    {
      int size;
 
      /* We need some more memory.  First shrink the buffer to the
     space we really currently need.  */
      obstack_blank_fast (obstack, fp->_IO_write_ptr - fp->_IO_write_end);
 
      /* Now grow for N bytes, and put the data there.  */
      obstack_grow (obstack, data, n); //执行次函数
 
      /* Setup the buffer pointers again.  */
      fp->_IO_write_base = obstack_base (obstack);
      fp->_IO_write_ptr = obstack_next_free (obstack);
      size = obstack_room (obstack);
      fp->_IO_write_end = fp->_IO_write_ptr + size;
      /* Now allocate the rest of the current chunk.  */
      obstack_blank_fast (obstack, size);
    }
  else
    fp->_IO_write_ptr = __mempcpy (fp->_IO_write_ptr, data, n);
 
  return n;
}
static size_t _IO_obstack_xsputn (FILE *fp, const void *data, size_t n)
{
  struct obstack *obstack = ((struct _IO_obstack_file *) fp)->obstack;
 
  if (fp->_IO_write_ptr + n > fp->_IO_write_end)
    {
      int size;
 
      /* We need some more memory.  First shrink the buffer to the
     space we really currently need.  */
      obstack_blank_fast (obstack, fp->_IO_write_ptr - fp->_IO_write_end);
 
      /* Now grow for N bytes, and put the data there.  */
      obstack_grow (obstack, data, n); //执行次函数
 
      /* Setup the buffer pointers again.  */

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2023-3-15 08:30 被我超啊编辑 ,原因: 错字
收藏
免费 5
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//