Before all
Team: ICEDTEA
Rank: 40/1544
2025 年初第一場 CTF 就獻給了 ICEDTEA 和 IrisCTF
超美的平台前端 orz
CRYPTO
KittyCrypt
main.go
聚焦看一下重點部分:
1 | package main |
加密的主流程其實就是從某個 json 檔案讀取一個 int 的 key,接著把明文每個字母都加上它後再轉成 hex,最後用那個貓咪的 CharSet replace 掉字元
另外它有給一組加解密後的範例+被加密後的 flag,等於說減回去就好難度應該是想下在 GoLang 就是
solve.py
1 | org={ |
knutsacque
對,然後一下難度就三級跳了
chal.sage
1 | import secrets |
簡單來說,這就是一個形如 $ax+by+cz=d$ 的不定方程…嗎(只是有九項)?
並沒有,它調用到了四元數
而四元數有自己的乘法規則,最顯而易見的就是它不滿足交換率
幾個觀察:
- 所有的四元數乘法都可以變成某種矩陣變換,變換後就變成四條不定方程
- 每一個字母的 ascii 在 2^7 附近,但他給的參數量級都在 2^64,有落差可能可以 LLL
首先是變換程矩陣乘法,對照上表,如果兩個四元數 $A \times B = C$,那今天可以寫作:
$$
A_{array}=
\begin{pmatrix}
A_1 \newline
A_i \newline
A_j \newline
A_k \newline
\end{pmatrix}
$$
$$
B_{array}=
\begin{pmatrix}
B_1 & -B_i & -B_j & -B_k \newline
B_i & B_1 & B_k & -B_j \newline
B_j & -B_k & B_1 & B_i \newline
B_k & B_j & -B_i & B_1 \newline
\end{pmatrix}
$$
下標就代表他們在該項的係數,並且這會滿足:
$A \times B=B_{array} \times A_{array}$
把題目給的A都轉成矩陣後,接下來是 LLL
本來我想用一條等式配成類似這樣就結束了:
$$
\begin{pmatrix}
A_{11} & 1 & 0 & … & 0 \newline
-A_{1i} & 0 & 1 & … & 0 \newline
… & … & … & …\newline
-s[0] & 0 & 0 & … & BIG \newline
\end{pmatrix}
$$
中間共 36 項,就是9個A展開成矩陣後的係數但今天即便 flag 只有 8 個字元 LLL 出來都有誤差
所以我幫他們配重拉大差距,最左變都乘以 100,終於可以過 8 個字元 …
但我就在想,後面怎麼辦?
此時注意到只有一條算式有被使用,但總共會出現四條,所以它們四個一起拿來約束這個方程:
$$
\begin{pmatrix}
A_{11} \times 100 & A_{1i} \times 100 & A_{1j} \times 100 & A_{1k} \times 100 & 1 & 0 & … & 0 \newline
-A_{1i} \times 100 & A_{11} \times 100 & -A_{1k} \times 100 & A_{1j} \times 100 & 1 & 0 & … & 0 \newline
… & … & … & …\newline
-s[0] \times 100 & -s[1] \times 100 & -s[2] \times 100 & -s[3] \times 100 & 0 & 0 & … & BIG \newline
\end{pmatrix}
$$
最後開 LLL 出來過濾就拿到 flag 了,特別注意前八個字元已經有了所以其實可以先扣掉:
solve.sage
1 | ## init |
MISC
Cobra’s Den
一個 pyjail:
1 | import builtins |
要讀取flag檔案,先 RECON 一下還有哪些 builtins 可以用:
1 | abs |
列舉出來是這五個,其中 chr 可以結合 whitelist 有的 +()
造出很多字串
比較有趣的是 repr,他就是 print 函數的字串再字串化:
1 | repr('\x00') = "'\\x00'" |
再來,[]<[[]]
是 true,所以是 1,可以一直加,但因為有輸入大小限制所以勢必得找到方法減少使用次數,方法就是從剛剛的 repr 把 x 字母提取出來,因為 payload 會是 open(構造出來的flag字串).read()
,所以它算是相對接近我們要的字母的值
最後就是寫生成任意字母的函數,再注意到 x 比 f, l, a, g 任何一個都還要大,所以很可能要用減的去 chr 函數裡,但沒有減符號可以通過,改用 ~ 的位元運算造出負數
1 | def gen(ch): |
小巧可愛而難懂的一行
最後生出的 Payload:
1 | open(chr(ord(repr(chr(([]<[])))[([]<[[]])+([]<[[]])])+~(([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])))+chr(ord(repr(chr(([]<[])))[([]<[[]])+([]<[[]])])+~(([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])))+chr(ord(repr(chr(([]<[])))[([]<[[]])+([]<[[]])])+~(([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])))+chr(ord(repr(chr(([]<[])))[([]<[[]])+([]<[[]])])+~(([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])+([]<[[]])))).read() |
FORENS
deldeldel
打開看到就是一個 USB 包,檢查和搜尋了一下蠻像鍵盤輸入信號的,用 tshark 把資訊倒出來
1 | tshark -r klogger.pcapng -T fields -e usb.capdata > usb.data |
最後去網路上拔一個解讀器出來就好:
exp.py
1 | normalKeys = {"04": "a", "05": "b", "06": "c", "07": "d", "08": "e", "09": "f", "0a": "g", "0b": "h", "0c": "i", |
輸出結果(節錄):output :Hheey<SPACE><SPACE><SPACE>AAali<DEL><DEL>licce!<SPACE>Ii<SPACE><SPACE>tthink<SPACE><SPACE>Ii''m<SPACE><SPACE>ssupo<DEL>ppooseed<SPACE><SPACE>too<SPACE><SPACE>giivee<SPACE>yoou<SPACE><SPACE><SPACE>tiss<SPACE>fllaag"<RET><RET>iriisctfF{[tthis_ajjd<DEL><DEL><DEL>keyloggeer_iisS_boo<DEL><DEL><DEL>too_hard_two<DEL><DEL><DEL>too<DEL><DEL><DEL>to_use}<RET>g[unknown]a[unknown]
手動改一下,就得到:irisctf{this_keylogger_is_too_hard_to_use}