关闭
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
how2heap
无
1101
0
0
mut3p1g
由于在XCTF总决赛上遇到了`house of force`这种没见过的姿势,于是想把关于堆的都看看,于是就找到了这个[how2heap](https://github.com/shellphish/how2heap),下面来学学。 <!--more--> ## 0x01 基础篇 ### 1. first_fit `first_fit`是`linux`的一种分配机制,在需要分配空间的时候,会先选大小最合适的最小的`bins`,具体分配机制可以参照我在前面对[malloc](http://www.mutepig.club/index.php/archives/47/)的分析。 ### 2. fastbin_dup `fastbin_dup`表示,如果一个`fastbin`不在`fastbins`的链表头的话,就能再次释放,(也能多次分配)。从而构成了一次简单的`double free`。具体机制可以参照我在前面对[free](http://www.mutepig.club/index.php/archives/46/)的分析。 ### 3. fastbin_dup_into_stack `fastbin_dup_into_stack`是基于上面的`fastbin_dup`,是可以将栈上的地址通过`malloc`返回回来。但是可利用的不仅仅是`stack`,而是我们可设置的任意值。 `fastbin_dup_into_stack`的实现流程如下: #### a) 初始化 ``` malloc=>a malloc=>b free a free b free a ``` 首先是将`fastbin_dup`实现一遍,现在就在`fastbins`中有了三个`fastbin`:a=>b=>a。 #### b) 伪造fastbins链表 ``` malloc=>c(a) malloc=>b(b) rewrite c->fd=fake_value ``` 在这里通过伪造`fd`,可以导致`fastbins`中出现了第四个`fastbin`:a=>fake_value 这里需要保证`fake_value`对应的`size`应当和该`fastbin`的`size`一致 需要注意的是,这里的`size`只取4字节,因为`sz`被转换为`unsigned int`了 ``` #define fastbin_index(sz) \ ((((unsigned int) (sz)) >> (SIZE_SZ == 8 ? 4 : 3)) - 2) ``` #### c) 获得返回值 ``` malloc=>d(a) malloc=>fake_value ``` 通过返回的`fake_value`,我们就能对对应地址的值进行任意的修改了。 ### 4. unsafe_unlink 这个就是正常`double_free`的套路了,具体可以参照我之前的[分析](http://www.mutepig.club/index.php/archives/23/)。 ## 0x02 进阶篇 ### 1. house_of_spirit 这个技术主要是通过在某处伪造一个堆并释放,这样之后再申请空间的时候就会返回这个地方。 这里同样利用的是`fastbin`释放时判断的失误,只判断了下一个`bin`的大小不能太大或者太小。具体机制可以参照我在前面对[free](http://www.mutepig.club/index.php/archives/46/)的分析。 具体流程如下: 1. 在任意可写处伪造一个`fastbin`,设置其`size`就可以 2. 在第一步构造的`fastbin+size`对应的就是下一个`bin`,设置其`size`为一个合法值就可以 3. 释放第一步伪造的`fastbin`,之后再次`malloc`就会是对应的地址了 利用条件就是能释放我们用来伪造`fastbin`的地址就行。 ### 2. poison_null_byte.c 这个技术可以实现分配两个`chunk`,其中一个较大的`chunk`包含另一个较小的`chunk`。 #### 1) 初始化 ``` malloc=>a(0x100 实际 0x110) malloc=>b(0x200 实际 0x210) malloc=>c(0x100 实际 0x110) free(b) ``` 这里将b释放后,c的头部就变成了下面这样: ``` 0x210 (prev_size) | 0x110(size | not prev_inuse) ``` #### 2) off_by_one 这里修改a的内容,如果能溢出0字节的话,就能修改b的头部的`size`,从而使b头部变成这样: ``` 0 (prev_size) | 0x200(覆盖了最低1字节) ``` #### 3) 伪造prev_size 如果要从b分配空间的话必然会调用`unlink`,而`unlink`存在一个校验是`(chunksize(b) != prev_size (next_chunk(b))` 但这个校验实际上没有什么关系,因为`unlink`的意义是最后一个和最开始的一个进行合并,那么是和我们伪造的块进行校验。 ``` #define unlink(AV, P, BK, FD) { \ if (__builtin_expect (chunksize(P) != prev_size (next_chunk(P)), 0)) \ malloc_printerr ("corrupted size vs. prev_size"); ``` #### 4) 实现攻击 将b释放后,我们可以申请两个较小`chunk`。而上一步伪造`prev_size`也是为了在b的`size`缩小后仍然可以正常将b分配。 接下来将b1和c释放,由于c的`prev_size`仍是b原来的大小,所以会和b(b1)一起合并,这样就将仍在使用的b2算进了为被分配的空闲区域。于是再次申请一个d的时候就将b2包含了进去 ``` malloc=>b1(0x100) malloc=>b2(0x80) free(b1) free(c) malloc(0x300)=>d ``` #### 5) 利用条件 这个技术的关键在于能否溢出到释放后的b的`size`,使得其最低位被覆盖为0 ### 3. house_of_lore 这个技术实现效果与`fastbin_dup_into_stack`类似,都是可以将我们指定的地址通过`malloc`返回回来,不过这里需要我们指定的地址是可供我们修改的,用以伪造堆。 #### 1) 初始化 ``` malloc=>victim(fastbin|smallbin) malloc=>a(1000) free(victim) malloc(b)=>(1200) ``` 首先一开始分配了victim就是我们要利用的堆,接下来分配的a是用来避免释放victim后victim与`topchunk`合并;最后分配的b是为了让victim从`unsorted bin`中进入`smallbins`。 #### 2) 伪造堆 在指定的区域(这里是栈)伪造了下面的两个`chunk`,释放了victim后还要伪造`victim->bk=chunk1`。这一步的作用在后面分析。 ``` chunk1: 0 | 0 victim|chunk2 chunk2: 0 | 0 chunk1| 0 ``` #### 3) 实现攻击 ``` malloc(victim_size)=>p3 malloc(victim_size)=>p4 ``` 这里先看看`malloc`一个`smallbin`的源码: ``` 3630 bck = victim->bk; // 取出倒数第二个bin 3631 if (__glibc_unlikely (bck->fd != victim)) // 即victim->bk->fd!=victim,就会报doublefree 3632 { 3633 errstr = "malloc(): smallbin double linked list corrupted"; 3634 goto errout; 3635 } 3636 set_inuse_bit_at_offset (victim, nb); // 设置victim在使用中 3637 bin->bk = bck; //bin表示smallbins的链表头,bin->bk表示最后一个,现在指向倒数第二个 3638 bck->fd = bin; ``` 由于`smallbin`大小相同就会在一个链表中,所以我们需要申请两个`victim_size`的空间,那么我们现在当然是将释放的victim给分配了出来,对应源码中的victim。那么现在`smallbins`的最后一个`bin`就应该指向`victim->bk=chunk1`了。 那么执行完p3的分配后,链表变成了下面这样: ``` victim=>chunk1 ``` 出现这样的情况是由于原本是`smallbin`的分配是FIFO,即先进先出。而`chunk1`是我们强行加上去的,所以链表头仍然是`victim`。 同时我们还需要注意这个校验`bck->fd != victim`,这也是为什么要伪造`chunk1`和`chunk2`的原因。分配的时候只看了前后关系,并没有校验其大小。 通过上面的分析我们能知道,分配p4的时候就会将`chunk1`分配出去了,这样我们就能通过修改p4修改栈上的数据,从而改变返回地址。 #### 4) 利用条件 可以看到,这里实现攻击的根本原因是`smallbin`分配时校验不严的问题。 在上述的三个步骤中,第一步、第三部都是正常的分配、释放;第二步只要有输入存储到栈上或别的地方就能实现,但关键就是如何在释放了victim后还能修改victim->bk。 所以应用条件就是在释放了某个`smallbin`后,如果还能篡改其`bk`,并且能够泄露伪造堆的地址,就能实现攻击。 ### 4. overlapping_chunks #### 1) 初始化 ``` malloc(0x100)=>p1 malloc(0x100)=>p2 malloc(0x80)=>p3 free(p2) ``` #### 2) 伪造size ``` p2->size = p2->size + p3->size + 1 = 0x181 malloc(0x180-8)=>p4 ``` 在第一步释放掉p2之后,p2会加入`unsorted bin`。如果此时将其大小修改,再次申请空间就会将p2加入对应的`smallbins`,从而能够分配出来。 这样实现的效果就是p4将p3包含了起来,且两块都在使用中,这样就能利用`unlink`进行任意地址写了。 #### 3) 利用条件 这个攻击利用条件就是,当我们释放掉一个`smallbin`或者`largebin`后,在其还在`unsorted bin`中就能修改其`size`。 ### 5. overlapping_chunks_2 #### 1) 初始化 ``` malloc(1000)=>p1 malloc(1000)=>p2 malloc(1000)=>p3 malloc(1000)=>p4 malloc(1000)=>p5 free(p4) ``` #### 2) 伪造size ``` p2->size=p2->size + p3->size + 2*SIZE_SZ + 1 free(p2) malloc(2000)=>p6 ``` 此时再释放掉p2,就会将p2和刚才释放的p4空间合并(其实可以不用释放p4)。 #### 3) 利用条件 和上面类似,不过是在释放前就将`size`修改了。 ### 6. house_of_force #### 1) 原理分析 分析一下源码,下面是当申请的空间无法由`bins`进行分配时而从`topchunk`中分离空间的过程: ``` 4069 use_top: 4084 4085 victim = av->top; 4086 size = chunksize (victim); 4087 4088 if ((unsigned long) (size) >= (unsigned long) (nb + MINSIZE)) 4089 { 4090 remainder_size = size - nb; 4091 remainder = chunk_at_offset (victim, nb); 4092 av->top = remainder; 4093 set_head (victim, nb | PREV_INUSE | 4094 (av != &main_arena ? NON_MAIN_ARENA : 0)); 4095 set_head (remainder, remainder_size | PREV_INUSE); 4096 4097 check_malloced_chunk (av, victim, nb); 4098 void *p = chunk2mem (victim); 4099 alloc_perturb (p, bytes); 4100 return p; 4101 } ``` 从这里可以看到,`av->top`实际上是可以被改写的,而再次申请空间后返回的正是`av->top`,这样我们就能覆写任意地址为任意值了 ``` 1300 #define chunk_at_offset(p, s) ((mchunkptr) (((char *) (p)) + (s))) 4091 remainder = chunk_at_offset (victim, nb); 4092 av->top = remainder; ``` 但是还有一个条件,所以我们需要让`top`的大小非常大才能通过这个判断 ``` 4088 if ((unsigned long) (size) >= (unsigned long) (nb + MINSIZE)) ``` 有一个骚操作,是设置`topchunk`的`size`为`aim_value+offset`,`offset = aim_addr - topchunk_addr`,这样通过一次`malloc(offset-0x10)`后,就能从`top`将`topchunk_addr`分配出去,同时通过设置新的`top`的`size`来设置`*aim_addr = aim_value` 但这个方法多了个约束,就是`size`必须是合法的(maybe就是修改的`aim_addr`得以8结尾) #### 2) 利用条件 如果我们能修改`topchunk`的`size`使其为-1就可以了,还有个条件就是我们输入的`nb`需要能抵达我们需要的地址(能输入负数或者大小满足限制)。 ### 7. unsorted_bin_attack #### 1) 初始化 ``` malloc(400|任意大小)=>a malloc(500)=>b // 不让a释放后和topchunk合并 free(a) ``` #### 2) 伪造bk ``` a->bk = stack malloc(400) ``` 在第一步的最后释放a之后,a会加入`unsorted bin`。之后将`a->bk=stack`,那么再次申请后会将a返回,但是`unsorted bin`也会进行处理,源码如下: ``` 3766 /* remove from unsorted list */ 3767 unsorted_chunks (av)->bk = bck; 3768 bck->fd = unsorted_chunks (av); 3772 if (size == nb) 直接将victim返回 ``` 所以这里先是将`bck->fd`置为`unsorted bin`的链表头地址,之后再返回的指针。最终实现`stack->fd=unsorted_bin_addr`从而泄露`libc`地址的目的。 #### 3) 利用条件 这个技术主要是用来泄露`unsorted bin`的地址的,条件也是需要在释放a之后,能够修改`a->bk`。 #### 4) 补充 同时`unsoted bin`可以`malloc`一个我们可控的地址,下面给个`demo` ``` #include <stdio.h> #include <stdlib.h> #include <string.h> size_t *top; int size = 0x200; void shell(){ system("/bin/sh"); } void show_top(char* a,int i){ top = (size_t *) ( (char *) a + size - 8); printf("%i times after a: %p\n",i, top[1]); printf("%i times after all: %p start:%p end:%p\n",i, top[1],top, top + *(top+1)/8); } int main(){ int i; size_t *a = malloc(size); size_t *d = malloc(size); free(a); a[1] = d-2; d[1] = d-2; size_t c = malloc(size); size_t b = malloc(size); size_t e = malloc(size); size_t f = malloc(size); printf("%p %p %p %p %p %p",a,b,c,d,e,f); return 0; } ``` 这里`malloc`的是堆上的地址`d`,因为我们需要伪造一个堆,所以如果我们能控制`unsorted bin`的`bk`,并且可以在一个地方伪造一个堆,那么就能`malloc`到伪造的那个地址去了 ### 8. house_of_einherjar #### 1) 初始化 ``` malloc(0x38)=>a malloc(0xf8)=>b ``` #### 2) 伪造堆 我们可以在知道地址的任何地方伪造这个堆,比如`stack`、`heap`、`bss`。这里以`stack`为例,其头部记作`stack`。 ``` 0x100 [prev_size] | 0x100 [size] stack [fwd] | stack [bck] stack [fwd_nextsize] | stack [bck_nextsize] ``` #### 3) off_by_one 由于b的`prev_size`实际上是在a的申请范围之内,所以可以直接修改。而溢出了一字节可以将b的`PERV_INUSE`修改为0。那么修改后b的头如下: ``` b-SIZE_T*2-stack | 0x100 ``` 这里给b申请正好0x100的大小也是为了溢出后只将`PREV_INUSE`给覆盖掉。 #### 4) 实现攻击 ``` free(b) malloc(0x200) ``` 首先释放b之后,由于其前一块是未在使用的,所以需要调用`unlink`将其取出,所以需要伪造`stack`的头部从而绕过`unlink`的校验。之后会将`stack`和b进行合并,并加入到`unsorted bin`中。最后申请对应的大小就能将`stack`返回了。 #### 5) 利用条件 如果我们能够溢出一个字节就能实现攻击。 ### 9. largebin attack `largebin attack`与`unsorted bin attack`类似,只是`largebin attack`会将堆的地址写到指定的地址 关键源码如下 ``` 3814 if (fwd != bck) // 如果链表不为空,这里应该先看最下面如果为空的情况 3815 { 3816 /* Or with inuse bit to speed comparisons */ 3817 size |= PREV_INUSE; // 上一块在使用中 3818 /* if smaller than smallest, bypass loop below */ 3819 assert (chunk_main_arena (bck->bk)); 3820 if ((unsigned long) (size) 3821 < (unsigned long) chunksize_nomask (bck->bk)) // 如果victim比largebin中最小的还小,那么直接加入到最后 3822 { 3823 fwd = bck; // 链表头 3824 bck = bck->bk; // 最后一个bin 3825 3826 victim->fd_nextsize = fwd->fd; 3827 victim->bk_nextsize = fwd->fd->bk_nextsize; 3828 fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim; 3829 } 3830 else 3831 { 3832 assert (chunk_main_arena (fwd)); 3833 while ((unsigned long) size < chunksize_nomask (fwd)) // 正向循环,从最大的开始,直到找到恰好大于等于自己的bin 3834 { 3835 fwd = fwd->fd_nextsize; 3836 assert (chunk_main_arena (fwd)); 3837 } 3838 3839 if ((unsigned long) size 3840 == (unsigned long) chunksize_nomask (fwd)) // 如果大小相等,则直接插入就可以了 3841 /* Always insert in the second position. */ 3842 fwd = fwd->fd; // 为了避免修改nextsize,所以分配第二个bin 3843 else // 如果大于,则还需要修改nextsize 3844 { 3845 victim->fd_nextsize = fwd; 3846 victim->bk_nextsize = fwd->bk_nextsize; 3847 fwd->bk_nextsize = victim; 3848 victim->bk_nextsize->fd_nextsize = victim; 3849 } 3850 bck = fwd->bk; 3851 } ``` 如果存在一个和`largebin`在一个链表但是比它小的`unsorted bin`,那么将其插入到`largebin`和我们构造的`chunk`之间就会执行下面的语句: ``` 3845 victim->fd_nextsize = fwd; 3846 victim->bk_nextsize = fwd->bk_nextsize; 3847 fwd->bk_nextsize = victim; 3848 victim->bk_nextsize->fd_nextsize = victim; ``` 那么我们构造`largebin->bk_nextsize = chunk`,之后实现的效果就是 ``` victim->bk_nextsize->fd_nextsize = victim => fwd->bk_nextsize->fd_nextsize = victim => chunk->fd_nextsize = victim ```
觉得不错,点个赞?
提交评论
Sign in
to leave a comment.
No Leanote account ?
Sign up now
.
0
条评论
More...
文章目录
No Leanote account ? Sign up now.