2024 CGGC CTF Final Writeup
這次幫國網中心的CGGC 網路守護者挑戰賽出題,總共出了兩題 medium ,分別是 web
的 Converter
和 misc
的 cat flag
Web
Converter
這是一個編碼的網站,可以編碼和解碼 base16
/ base32
/ base64
/ base85
等不同網站
後端是使用 Flask
寫的,為了防止 SSTI
,在 render_template_string
之前我把一些地方 HTML encode
了
encode
時 input
會被 HTML encode
decode
時 input
和 result
都會被 HTML encode
1 | return render_template_string( |
因此,想要 SSTI 就只剩下把東西拿去 encode 之後變成怪怪的東西了,那什麼狀況下 encode 會跑出怪怪的東西呢?其實去查一下四種編碼應該就可以很快發現 base85 的 index table 有可以利用的東西({
和 }
),但這樣其實還是不夠的,因為如果想要構造出 {}
那 input
會需要傳入不存在的字元,偏偏 URL encoding
又只會解碼特定的 binary data
。
那怎麼辦呢,其實在 encoding
的時候還有第二個可控的地方,那就是 Accept-Charset
1 | encoding = request.headers.get('Accept-Charset','utf-8') |
後端會根據輸入的 charset
去將傳入的資料 encode
(但是僅限比較常見系列的編碼)
1 | elif conversion_type == "base85": |
有了這兩個可控的地方,我們就可以找到幾種編碼方式,把不存在的編碼變成存在的字元當成輸入, fuzz
一下就可以找出有效的 payload
我這邊是使用 cp850
去想辦法構造出 {{config}}
,最後找到 qX%15rx%15▒Bà└│
可以弄出 aaa{{config}}1
最終 payload
:
1 | POST /api/convert HTTP/1.1 |
flag:
CGGC{'ÞÑ┐Õ▒àÕ▒àÞÑ┐ÞÑ┐Þ©óެƵ£ì'.encode('cp850').decode('utf-8')}
後記
我看到超多人交的 flag
是把中間那坨拿去 decode
變成 CGGC{西居居西西踢誒服}
,然後發現不會過又改成 CGGC{CGGCCTF}
/ CGGC{cggcctf}
最後才交上面的,超級好笑我很抱歉抱歉ㄉ心
Misc
Cat Flag
這題是給使用者一個 powershell
,將 flag
寫在圖片裡面,透過 AES-CBC
加密後放在資料夾裡面(我會給密碼),參賽者要想辦法用 powershell
偷出 flag
未免也太簡單了對吧?所以我加了一些限制:
- 不能連網,對外連線通通
DROP
掉 - 指令長度不能超過 512 個字元
- 輸出字元數量不能超過檔案本身大小
- 每個
instancer
只能輸入一行指令 - 每次重開
instancer
都會用不同密碼加密
所以,想要弄出 flag 只剩下一條路了,那就是 cat flag
但要怎麼輸出呢?大家馬上聯想到的應該都會是 Base64
,但是其實 base64
會把資料變成大胖呆(原本8個一組的資料變成6個一組),所以比賽現場就會看到一堆人弄出來的圖片長這樣:
啊 flag
咧?在下面被切掉了
我預期的解法有兩種:
- 用
base64
切片抓下來,因為有給密碼了,所以可以透過第一組的明文使用第二組的密碼反向加密回去再和第二組的下半身合併成完整的圖片(這邊是 CBC 的 block cipher 所以要注意不要切爛了會有 padding 的問題) - 因為是限制
char
所以可以想辦法把每個byte
轉成自訂的寬字節就可以一次輸出整個檔案了,但是他又有限制指令只能512 char
,所以可以找一下有沒有什麼內建的編碼方式是可以涵蓋到大部分的組合,然後在自訂沒涵蓋到的編碼就可以了,我這邊是用一個古老的編碼X-Europa
把檔案 print 出來
編碼:
1 | $bytes = [System.IO.File]::ReadAllBytes("cat.flag");$compressedStream = New-Object System.IO.MemoryStream;$result="";$encoding=[System.Text.Encoding]::GetEncoding("x-europa");ForEach ($byte in $bytes){if ($byte -eq 0){$result+="嗨"}elseif($byte -eq 127){$result+="哈"}else{$result+=$encoding.GetString($byte)}};$result |
解碼:
1 |
|
弄出來的資料大概會長這樣:
1 | Salted__◀¶8ήρêWΒ▲À§§Τ║ΞÓCΖ↕É3$Ôoο│Ϋ*á‼αύ,{┘ÇÕyPUΪχZ\æUΰal!ë;ΞΈ>♬΄8nÆsÂΰηUv0Z·HO¡λg◘│.~┘ώ↕òΥbQ▼ΖΉPÜΎüκχ┘ΎÄ:q─É└xνϊî/'ηΊ5ίïΐ?èΘQν>γ0b*kςO6Dv└║Aïί&t◙ºω$↕°δ΄§ΥäΤnΆ1ö♂CοJ▼♪;βJΥΧ(ä.óhώ═2ÅE%δÕ◙◙╝G^Ό!ψ♂M○J╚¿9ºYΥΠΟÍ\○TAlérνyΦβ*Äλz¡'Ξίê→ÍìΦöίς║7Ñ+_☼/v/Θàξ4GΣLάæS♬6+FρθΕÑRôsÓτδ/┘YØàÅ¿Νό Bωy→Ήχ4e"χÅηαH哈θόψώvu8cçãvßή═PqΏ-‼ΗÂ─ϊΉ│↕τUEÊ║Ά9ώΗέΘDèe&哈ÖÍιwφή◙pάεΕé"╚ËP)9◙╝嗨áω▼0↓哈▲ί→SΔεTSòk£όÕΜÄΡ^┐L%♂oοÑΥüμñΤηòύΡút^ÀΓVRεδ↑ξΰ)Ü○═eN│Ú$b+$°ϊ3ö▲Σçν┐IN♂õΨΈ7z←[:)éÁ8♪◙ºöQMΰν┘QsσΉ♪↑IρΜYΊº°üï·jr↓S'ε‼ηζς¡4اΈΟÅ-╝τÚσBΗ?BΙ/x←+Äa♬`g/ϋΟΤëK║]Êãω←ΏÀ£_¡αóÂΰÍ^α└zϋp╔Nï·øáâ2ΘËΌΛÀLL%tÕÓDλmI◘ΦÊ↑ξK-^ë+,\γÄrßP─óÊhη"4♂♬ί:ρ"Iã6jÑΔΌª┌§/[O]αüöΔÂ╚┐:0õ1♂ύBθοÀÑBghϋRsºÀ哈F"▼◘îΜaί;q╔ΞώΧΡBÚ┌·F7哈ύφ哈 μø<À΄)ªö─¿ÁΧτ╔ΦhρMnζΡØz◘cªÆΥêΕ"à_FΣ]Τοπ.Λr♬┌ØΓ#♂έ║Xá¶Ή↔ÂΒώ8/fSV@υÍÕfΓ3ζΚE·όϊ1ãA+|'ËÑς↓hO┘h╚a"|ïâ9Ά!èàdÉg═xΙóæeΪñήR#<ñ4'4õUΈά→Ξα◘ù,äέ;?H│},π7aΒ◀η║χΏ\x◙e1ώÂÂ@Vΐx‼6ΛÅzφ&ύÉâ○όeΑι<─@¶ΐΊu+°ΟªεΝΞΑΙd=7öÅ═+ßÁ\îΙ|嗨f£ΛñùΨßWÂ=΄*·eO,΄άω@XΨC,ìñu΄t JÄ▲dîîóîωI.ρѶæuùÍπGôΡαjìΗù↑õ=ώûΑêηTæ{ÍNVroKüΆώg<euÂΠα`ήξ↓Αkό♬+§ÔόxW▲Ά*ζΘΧΰãΌ╗Læ+ ┐ρü0΄mlϊ4┌Ι]υêαφb¿Οό│]:\ìιΛ y¶άΜ(HúÅY§◘#ÃΕφàS嗨Æù↓Ëê9Gώæ.▼NΖJχ╚Pαè┐@Á(£nÂeìº↓maq§Ν0nØûEÚÕh哈kDGaíυ↔ó@SmabΰκTL‼/Ξy`♀sΠ{mΉKENeΒΏκΥΆ<Γ¶!▶@╗┌MÀϋVv¿$i↕Í↑aUEΈx◀^οΌûP╚◀àMϊk@▲£çèAÄvzÆNæÇέλ9ω▲xt嗨ª└ΓnîAθάï#Ηè↕à |y╚έãΤόGjρt·ρø↓r↑Éκtåς1y=É♀1A΄ω=╝┐ΞΠN0éïζΜ◘π|F╔óüα╗ΈôΊ↑υZξΞ_3Ñ♀ξöΟæºψζΜ╚Ãææΰωå♀λΗÆ5nτ♪è5°¡ΨºvΪGΛΜώΒΣ1öΝq╝öôοψW4èaσΝDΤÓs┘o╔=│¶ÉΌ♂ϋcëάbάΔÍbνü┐Ι&ΥTEυBΉϋ↔8ιîΡ═Í!ΆΔΌ+¶2öτ◙zΧΕ[<→,ΆòcÄj-Óf£7Q▶N)ΗΜ>92kκêΫ`↔tφβ┘JwìÇ^ÅX|Τ7~3ê4└Ϋz£ÅκysΏ Y▼ΞôuË\UΜOÄοΉIoíΖB/ZΦΙΝVqwFAXΣ|:╝?έ╝iBV$ΧΫαYn΄a↔ΙBÂ-$Ά▼ι═Mεμλ¶ Ψóύá┌νÓÔ┘▲0σ←R¶εFWω↑sψ?.â#δ§ί┌ΓΉay£%]ΣÁUΟtÖUζνξ/ÔyöC☼╚M▼=;Ι"8─ÄUÖÑΫãΐ>δ4ξή:ΣβºόΛΒ~║O↓Lΐύw"χ♂-↓ÚnèωrÅí◘ΜÚíVtΰ(ΓZPΚiê9FήΟ°r─σ◀ί♬?¿ΓόhXEΈ,υm; |
後記
這題是用 @chummy 的 CTF Instancer 架的,會依序動態開 100 個 port給大家連 instancer ,結果有某海狗直接狂戳 port 幫大家把 instancer 都關起來(我完全沒想到可以這樣玩QQ)
後來臨時加了驗證 CTFd Token
的機制,感謝賽博水電工 Chummy 現場幫 instancer 加新功能,然後兩個人在那邊寫爛扣
2024 CGGC CTF Final Writeup
https://trianglesnake.com/2024/12/07/2024-CGGC-CTF-Final-Writeup/