引言
今天是我們 General Skills 最後一題,光是基礎技能我們就花了 21 天呢...
畢竟是入門,我們就慢慢來吧!
明天開始會花一天介紹一下 2019 的遊戲,之後就是六個領域各選一至兩題來解囉!
這題算是 General Skills 中技術含量較高的,也需要一點點 C 語言基礎,
非常適合當壓軸~我們開始吧!
General Skills / flag_shop
這題的情境是說有個商店會販賣 flag ,買到你就有 flag 了~
題目給的材料除了商店程式所在伺服器以外,還罕見地給了商店程式的原始碼,用 C 語言寫的!
總之我們先把原始碼下載下來等等備用,檔案名稱是 store.c
。
我們先不看原始碼,先用 nc 玩玩 flag 商店是怎麼運作的:
$ nc jupiter.challenges.picoctf.org 4906
連上後程式輸出:
Welcome to the flag exchange
We sell flags
1. Check Account Balance
2. Buy Flags
3. Exit
Enter a menu selection
看起來是 menu 形式的界面,可以輸入 1,2,3 來決定需要什麼服務。
1 是查看帳戶餘額, 2 是買 flag , 3 是離開,
總之我們先看看目前的餘額,輸入 1 按下 Enter :
Balance: 1100
Welcome to the flag exchange
We sell flags
1. Check Account Balance
2. Buy Flags
3. Exit
Enter a menu selection
可以看到餘額是 1100 ,再來我們試著購買 flag 看看,輸入 2 :
Currently for sale
1. Defintely not the flag Flag
2. 1337 Flag
它販賣兩種 flag ,一個是「絕對不是 flag 的 flag 」,另一個是「 1337 flag 」,
先看看 1337 flag 吧:
1337 flags cost 100000 dollars, and we only have 1 in stock
Enter 1 to buy one
居然需要十萬,而且只有一個,想必這就是解答 flag 了,我們看另一個 flag 是什麼:
These knockoff Flags cost 900 each, enter desired quantity
它說仿冒品只要 900 ,輸入你要的數量。如果你真的買了什麼事都不會發生~
先 ctrl-C 或是第三個選項離開程式吧,等等找到方法後要重開。
總結來說目前正常使用是買不到十萬 flag 的,我們看看原始碼有什麼,
這是 C 語言程式,我等一下只講一下關鍵部份,大家可以先自己找找看突破點在哪裡,
提示是「條件、上限」。
解
答
在
下
面
其實關鍵在 C 語言 int 的數值上限 ,最高是 2147483647 ,也就是 2 的 31 次方 - 1 ,
最高的正數只能到這,一旦再 + 1 ,你會發現變數內容變成 -2147483648 ,這兩數可是天差地遠的!
這個程式的漏洞在:
int number_flags = 0;
fflush(stdin);
scanf("%d", &number_flags);
if(number_flags > 0){
int total_cost = 0;
total_cost = 900*number_flags; // <-- 漏洞
printf("\nThe final cost is: %d\n", total_cost);
if(total_cost <= account_balance){ // <-- 造成這邊可以穿過去
account_balance = account_balance - total_cost; // <-- 幫你加錢
printf("\nYour current balance after transaction: %d\n\n", account_balance);
}
else{
printf("Not enough funds to complete purchase\n");
}
}
當你輸入了 number_flag ,雖然有個 if(number_flags > 0) 讓你無法輸入負數,
但是在我標注漏洞的地方,它把這個數字乘以 900 ,所以如果你 number_flag 很大,
大到一定程度但不會超過上限,這時 if(number_flags > 0) 你可以順利通過,
但下面會乘以 900 ,如果你輸入的數乘以 900 超過 2147483647 , bug 就出現了,
total_cost 會變成負的,那我標注的地方就可以穿過去,下面就會幫你加錢!
所以我們破解的方法,就是輸入一個很大的數,
大概就是乘以 900 後超出 2147483647 一兩千的程度,
因為超出範圍後會從 -2147483648 開始往上加,
-2147483647,
-2147483646,
...
而原本餘額有 1100 ,所以我們要抓超出上限大概 1000 多,
才能加上去以後不至於又爆掉:
account_balance = account_balance - total_cost;
看上面的那句,如果 total_cost 變成 -2147482000 左右 (大概超出上限一兩千會變這樣)
那 1100 - (-2147482000) = 2147483100 ,很剛好不超過 account_balance 的上限。
因此,我們的目標就是「 900 x 輸入的值 = 2147483647大概再超出2000 」,
定量來說,我把輸入的值取: 2147485647 / 900 ~= 2386095 ,估計會得到幾乎最多的錢,
試試看:
Currently for sale
1. Defintely not the flag Flag
2. 1337 Flag
1
These knockoff Flags cost 900 each, enter desired quantity
2386095
輸入我要買 2386095 個冒牌貨 flag :
The final cost is: -2147481796
Your current balance after transaction: 2147482896
厲害了,你花了 -2147481796 個錢,餘額剩下 2147482896 ,
真的相當接近最大值。
我們可以買 flag 啦,而且錢綽綽有餘呢:
Currently for sale
1. Defintely not the flag Flag
2. 1337 Flag
2
1337 flags cost 100000 dollars, and we only have 1 in stock
Enter 1 to buy one1
YOUR FLAG IS: picoCTF{m0n3y_bag5_9c5fac9b}
購買 1337 flag ,輸入 1 買一個,
就得到 flag 啦!