(the same as MFCTF)雖然沒有解很多題,但還是放上來好了XD
Cat Slayer ᶠᵃᵏᵉ | Nekogoroshi
trial and error
Microchip
從原始碼來看,他是 4 個 4 個為一組進行運算,再反過來輸出,而其中的 key 因為只會用到 4 個,所以用 Output 中的前四個 bytes 對應 AIS3 便可解出 key[4] = [69, 42, 87, 10]
,接著就寫個程式逆回去吧
1
2
3
4
5
6
7
8
output = "=Js&;*A`odZHi'>D=Js&#i-DYf>Uy'yuyfyu<)Gu"
key = [69, 42, 87, 10]
for i in range(0, 40, 4):
for j in range(4):
num = ord(output[i+3-j])-key[j]
while num < 33 or num > 126:
num += 96
print(chr(num), end='')
Microcheese
我原本沒發現 bug,還在理解 splitmix64()
,結果官方宣告有 bug 並修改釋出後的題目,對照兩者的程式碼發現當遊戲進行到一半,如果選擇 0, 1, 2 以外可以跳過自己的回合,於是就一直跳過,在最後一步的時候拿走最後一顆,就拿到 FLAG了
Microchess
這題是上面 Microcheese 修改後的版本,當我看了 splitmix64()
這個函式很久但還是找不到漏洞
於是就點開了 hint,裡面寫到某一部跟 md5、sha1 類似,因為原先看到能輸入遊戲代碼就大概想到要從那邊突破,原本是想把 secret 找出來然後重製我要的局面,但看到 hint 就想到了 LEA。
然後又看到這個結構,它會把上一個 block 運算過後的值拿去跟下一個 block 再運算一次
而這個函數會把字串填滿到八的倍數
所以我們可以想辦法把局面控制到 8 的倍數(e.g. 1,2,15,1)這樣就不需要 padding,而我們希望在後面加一堆,一進入遊戲就把那堆取走,這樣就換我們必勝了(如果會玩的話)。因此我們可以多製造一段 b',1/x00/x00/x00/x00/x00/x00'
。把原先 hash 出的值,跟我們新加的 block 再 hash 一次,就有新局面的代碼了,接著就是玩遊戲,拿 flag (我不會玩遊戲,試了快 10 分鐘才贏)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def splitmix64(x: int) -> int:
U64_MASK = 0xFFFFFFFFFFFFFFFF
x = (x + 0x9E3779B97F4A7C15) & U64_MASK
x = ((x ^ (x >> 30)) * 0xBF58476D1CE4E5B9) & U64_MASK
x = ((x ^ (x >> 27)) * 0x94D049BB133111EB) & U64_MASK
return x ^ (x >> 31)
def f(a: int, b: int) -> int:
for i in range(16):
a, b = b, a ^ splitmix64(b)
return b
result = "2ae827becef7c60c"
mes = b',1\x00\x00\x00\x00\x00\x00'
blocks = [int.from_bytes(mes[i:i+8], 'big') for i in range(0, len(mes), 8)]
result_int = int(result, 16)
for block in blocks:
fake_int = f(result_int, block)
print(fake_int.to_bytes(8, 'big').hex())