2025 SCIST MID CTF Writeup
SCIST MID CTF Writeup

我是 SCIST_31 /
Web
dig-waf4
跟作業幾乎一樣,但多禁了一些東西,不過空格還是可以用
$IFS
代替,就成功ㄌ
payload: more$IFS/*_*
flag:
SCIST{command_injection_has_somany_combinations!}
Da Vinci Code online 🛜
要在三次內猜到一個 0-10000 中的一個數字,通靈了幾次沒成功只好去看 code 了
發現
1 | getSecretAnswer(command) { |
1 | if (data.type === 'guess') { |
所以就只要用 BurpSuit 修改 Websocket 的內容
1 | {"type":"backdoor","number":100,"command":"SHOW_ME_THE_ANSWER_PLZ"} |
1 | {"status":"secret","answer":7586} |
就能成功獲得 Flag 了~
flag: SCIST{WC_5c1St_Sc0r3bo4rD_1s5u3}
nosql injection blind2
跟作業的 nosql injection 一樣,但每個字的 range 變大了 (000 ~ ),因為之前 code 就是用二分搜,所以基本上就改一點點就能得到 Flag ㄌ
1 | import requests |
flag: SCIST{WOW_y0u_4r3_7h3_BLIND}
calculator
看了一下程式碼,發現在 server.js 有一個 eval,所以就可以直接注入,並發現 dockerfile 裡面有寫 flag 的名稱,就得到 flag ㄌ
1 | wss.on('connection', (ws) => { |
1 | COPY flag_3298fh9u32niaergjfwe9ij923.txt / |
payload:
(() => require("fs").readFileSync("/flag_3298fh9u32niaergjfwe9ij923.txt", "utf8"))()
flag: SCIST{TRy_70_dO_5Om3_C@1cU1A7Or}
Misc
Trick or Treat
這題是一個 nim game,可以用 nim sum 來解,就寫個 script 輕鬆得到 flag
1 | from pwn import * |
flag: SCIST{trick-or-treat? trick-xor-treat!}
Crypto
Elgamal oracle - 首殺 /
是個白箱,看看是怎麼加密的
1 | import itertools |
每次只會給 ciphertext 解密後的最後一個 byte,試著運用 homomorphic 來解這題
\(c2 \cdot k \mod p = m \cdot k \mod p\)
我們每次可以得到
\(m \mod 256\)
希望可以迭代做到
\(\lfloor \frac{m}{256} \rfloor \mod 256 , \lfloor \frac{m}{256 ^ 2} \rfloor \mod 256, \lfloor \frac{m}{256 ^ 3} \rfloor \mod 256, \cdots\)
想到可以用反模數來計算,令 \(s \equiv 256^{-1} \mod p\)
\[ \begin{align*} \lfloor \frac{m}{256} \rfloor \mod 256 &= (m - (m \mod 256)) \cdot s \mod p \mod 256 \\ &= (256 + ((m \cdot s \mod p \mod 256) - ((m \mod 256) \cdot s \mod p \mod 256))) \mod 256 \end{align*} \]
就這樣不斷迭代就可以得到 flag ㄌ
1 | from pwn import * |
flag:
SCIST{I said elgamal can perform homomorphic encryption in class. :)}
LCG cipher - 首殺 /
1 | import abc |
可以 byte by byte 的得到 flag,就一個個迭代就可以得到 flag ㄌ
1 | from pwn import * |
flag:
SCIST{using linear congruential generator to implement a stream cipher}
RS256 - 首殺 /
1 | import abc |
這題超級麻煩,要構造一組 token 使得 JWT 驗證出來 header 正確, body
正確(在有效時間), signature 正確,其中 signature 是把 header 和 body
sha256 後再做一次 RSA 加密,不過原理倒是不難,因為 RSA p-1
是 smooth 的,可以用 Pollard’s p-1 factorization 攻擊,sha256 要 hash
的是 secret + {header + ‘.’ + body} ,因為 prase
時後面如果有跟前面相同的鍵值會蓋掉前面的,所以就可以用 LEA 攻擊, 其中
secret
的長度只有可能是 37~43 就一直猜就會對了
1 | from Crypto.Util.number import GCD |

flag:
SCIST{It's a bad practice to implement RS256 of JWT.}
P.S. 為什麼找不到網路上可以用來解 sha256 LEA
的工具或是腳本啊啊啊,其實 LEA 那部份主要是詠唱出來的
Welcome
CATCH THE FLAG!
在首頁的 console 看到

到 [robots.txt](https://mid.ctf.scist.org/robots.txt)
看到
1 | User-agent: * |
進到 /cnZjdmN2Y3ZfYWd2Yl9kaV9jem16Cg==
發現
1 | flag.addEventListener("click", function() { |
找到第二段 flag E.1O9_w3lc0mE}
,就成功ㄌ
flag: SCIST{c0Ns01E.1O9_w3lc0mE}