沒開玩笑,真的要猜拳。
今天選的是 picoCTF 的 RPS
,本來以為是什麼 per second,點進去看才知道是 rock, paper, scissors
,總之來猜拳吧 XD
I hear something good happens if you win 5 times in a row.
題目說要我們連贏五次,應該是可以拿到 flag 吧,我們先連看看題目:
一開始先選擇 1
開始遊戲,然後打 rock/paper/scisssors
來猜拳。並且時間到還會被踢下線 XD
接著來看程式碼,從 main()
開始看,會看到呼叫了 tgetinput()
來取得 user 的輸入,tgetinput()
中有一行比較特別的:
ready_for_reading = select(1, &input_set, NULL, NULL, &timeout);
這邊使用了 select()
,查詢了一下發現是一個 syscall,用途是在多個 file descriptor (e.g. stdin, stdout, socket) 中選擇一個或多個進行操作(讀/寫),還可以設定 timeout ,剛才我被踢下線應該就是這個地方造成的。
我們再往後看會看到 read_bytes = read(0, input, l-1);
,可以發現傳進 tgetinput()
的第二個參數 l
是讀取的長度,因此我們在輸入猜拳時的允許長度是 100,總之雖然看起來蠻多行,但這個 function 都是在處理輸入。
接著看 play()
,判定輸贏的方式是用尋找字串的方式,我們直接舉個例子:
假設電腦出布,我們如果出剪刀會贏布,程式會在我們的輸入中尋找 scissors
的字串,聽起來好像很合理?
老實說,一開始的思路走的是找什麼 overflow 之類的,但一無所獲。靜下來思考一下,跟朋友玩猜拳時,偶爾耍白爛想說只要一手出一個,有一手贏的機率就高很多。啊這題好像可以一次出三種耶 XD
程式檢查的邏輯是在輸入中找尋可以贏的那個的字串,並且大小限制是 100,那我們三個都出呢?
果然是這樣 XD,雖然可以用複製貼上大法,這邊還是寫個 exploit 給它個尊重:
from pwn import *
p = remote("saturn.picoctf.net", 62039)
context.log_level = "debug"
payload = b"rock/paper/scissors"
for i in range(5):
p.sendlineafter("exit the program", b"1")
p.sendlineafter("(rock/paper/scissors):", payload)
flag = p.recvuntil(b"}")
print(flag)
看來有時候漏洞不見得需要用高深的技巧來挖掘,也有可能只是一些邏輯不夠縝密,算是學到一課 XD