本文主要爲比賽途中記錄的解題過程,部份爲解開後才補充。有些失敗的嘗試過於瑣碎而省略。
🅸🆔 aka IID as ScriptKiid
Flag: NCKUCTF{我會遵守以上規則因為我是好駭客}
Flag: NCKUCTF{Welcome to 2024 NCKUCTF Freshmen Cup!}
對行動版使用者不太友善,尤其是不能複製
※ 大部份含執行檔的題目應在相容 Linux 的環境執行。我使用 WSL 2。
直接執行。會贏喔。
Flag: NCKUCTF{w0W_yOU_4re_vErY_gooD_47_ROck_PAPeR_5cS50RS!!}
直接執行。會贏喔。 命運的輪盤停不下來!!(惱
使用 objdump
,沒印出任何有意義的文字來。用 gdb
也沒看得到的符號。
使用 strings
,斷編殘簡中有十分甚至九分破碎。
關鍵詞察覺。
下載最新 UPX 於目錄展開,game
接受程式展開。
它終於製作意義來了,那我們可以開始了。
底下的一列逗號分隔數字應該是 flag 內容,但加密了不可看。
gdb
操作紀錄:完全沒有那種勝利的希望。
Flag: NCKUCTF{0M9!!D!d_yoU_beA7_tH3_8!G_90n=fR3Ecss?}
隨機輸入,輸出看起來是固定的,用 strings
看看好了。
欸還有其它影片網址,都點點看好了。(終)
Flag: NCKUCTF{w4I7_a_MiNU73_kaITō_kiddOoa4}
給了一份 flag-decryptor.py
與 chal.asm
。
flag-decryptor.py
會要求一份整數清單檔。chal.asm
是純文字檔,內容是 Python 組譯碼。看起來題目想要挑戰者寫出再現手稿來執行,但我有個不同的想法。
用 pyc-xasm
(需使用 master 版本)把 chal.asm
轉成 .pyc
檔執行。
但題目的 chal.asm
還不是 pyc-xasm
能直接處理的語法,這麼不同的語法有改寫的必要。
參考 https://github.com/rocky/python-xasm/blob/master/HOW-TO-USE.rst 改寫。
chal-Baby_Python_Assembly-edited.pyasm
不支援有特殊符號的函式名,所以把 <listcomp>
改成 _listcomp_
。
爲了確認有正確轉換,用 pydisasm
(需使用 master 版本)檢査逆轉換結果。
速報:最期待的畫面要出現了。
慟!python
不幸拒絕了轉換結果,更拒絕了合法的 Python 手稿轉換物。用 xpython
繞過。
沒改好,再改一下就好。
Flag 取得:NCKUCTF{baby_pyth0N_1s_s0_EzZzzzzZZzzzzzZzzzz_Vincent55_is_so_Electric_OrzZzZZ_a8b40af05731c9612abeec69772de8e8ca8be759000d272e89f9ea5d413b2477}
至於 what_is_this_function()
,原程式沒有用到,所以改造一下組譯碼讓程式列出呼叫結果:
chal.pyasm
xpython chal.pyc
是個 數列,與 flag 沒有直接的關係。
先用 strings
看看題目正不正常,發現確實是 C# .NET 程式,使用 dnSpy 看。
可以看到 Game
初始化時會直接解密 flag,而且「正常破關」(後述)就能看到 flag。
因爲是 32-bit 應用程式,debugger 無法啟動,要重新下載 32-bit 的 dnSpy,在下載的空檔確實挑戰。
啊啊啊啊莫明自動新遊戲了。
既然 32-bit 的 dnSpy 下載好了,就直接用 debugger 看吧。
Flag: NCKUCTF{WHo_I5_RO63R?}
※ 用 dnSpy 可以直接編輯反編譯結果,並重新編譯回執行檔(不會更動原執行檔,但可另存新檔)。
加上的這行實際上不會中止程式執行,只剩下放置中斷點的意義。
不過實際編譯時會報吿 base..ctor();
一行無法剖析的錯誤,直接整行刪掉(應該沒問題,吧?)。
實際上 Game.checkGameOver()
會呼叫 Game.checkGoal()
檢査有沒有値爲 this.goal
的方塊,但 this.goal = 2486
,要造出「2486」的方塊才能「正常過關」,所以正常遊玩時不可能顯示 flag。
Reverse Any%
根據源始碼:
路徑字串串接時,接上 //
可以使路徑回到根目錄。
把 //flag
base64 編碼後得到 Ly9mbGFn
,但直接瀏覽會因爲被認爲是圖片而無法顯示,但可以確定應該就是這個檔案。
直接按 Ctrl-S 存檔存成文字檔。
打開即爲 flag。
Flag: NCKUCTF{os.path.join can join to root}
給的源始碼:
唯一能利用的是 target
。command
固定是 0 到 2 或到 -3,而只有 0 與 1 會用到 target
。
輸入的 target
必須符合正規表達式 ^[+-9A-~]*$
,也就是只允許 +
到 9
及 A
到 ~
的字元。査 man ascii
可知允許英數字或標點 +,-./[\]^_`{|}~
之一,注意不含空格。其中在 bash
中有語法作用的有:
\
(後一字元有特殊語法意義時,去除意義)[[
… ]]
(眞假條件測試)(需要空白)|
(命令間管道){
… }
(命令區塊)(需要空白)~
(家目錄)(需要空白)`
… `
(執行命令後,將輸出文字直接代入 `
… `
)看來最有希望的是用 |
後接不讀資料流輸入的命令,例如用 |ls
執行 ls
。但還無法執行需要引數的命令。bash
沒有內建無引數輸出單個空格的命令,所以做不到。除非我漏了其它語法。
我漏了花括弧展開。
https://www.gnu.org/software/bash/manual/html_node/Brace-Expansion.html
已知會執行 bash -c "{command} {target}"
,假設 command
爲 ping -c 1 -W 1
:
|{ls,/}
→ bash -c "ping -c 1 -W 1 |{ls,/}"
→ ping -c 1 -W 1 | ls /
,取得 flag 檔名。|{cat,/flag-8dbb2510222d6fc4}
→ bash -c "ping -c 1 -W 1 |{cat,/flag-8dbb2510222d6fc4}"
→ ping -c 1 -W 1 | cat /flag-8dbb2510222d6fc4
,取得 flag 內容。
Flag: NCKUCTF{C0Mm4nD_1NJ3c710n_1s_fUn_effad912477740e3}
査看使用者端碼,可知是 PHP,但沒發現網址有使用査詢語法。
隨便上傳圖片,發現訊息是裸文字。
嘗試用檔案名稱注入,沒有反應,多餘的 PHP 碼被瀏覽器自動註解。
在 Windows 介面上傳的話,特殊標點會因爲 Windows 檔案系統的規則先在本地被過濾,但在 WSL 介面可看到未過濾的檔案名,所以用 WSL 開瀏覽器繞過。
另一個可注入的點是使用者名,但有設定允許字元。
剩下的只能是上傳內容了。
考慮首頁的 "Shell me if you can!",査 Google 後猜測要使用 Webshell 的技巧,但是上傳格式必須是列出的圖片格式。
實際上傳後發現檔案類型的檢査不看副檔名,而是看內容標頭,所以要包 PHP 碼在圖片內。可以用 exiftool
達成。
注意 .php
等 PHP 副檔名特別被禁止,在 uploads/
目錄下也禁止存取。
最後採用的自作原始 PNG 檔:(大小:1×1 像素)
但是只要副檔名是非 PHP 檔,進入上傳位址都不會成功執行(副檔名是 HTML 檔時則 PHP 碼被瀏覽器自動註解)。嘗試利用首頁中的圖片顯示也失敗。
繼續搜尋,發現一份 Webshell 類守則:https://github.com/Mehdi0x90/Web_Hacking/blob/main/File Upload.md
Random uppercase and lowercase :
.pHp, .pHP5, .PhAr
把含有 PHP 片段的 dot.png
改名爲 dot.pHp
後上傳,成功執行。大意了,沒有檢査大寫。
最後對原始的 dot.png
分別加入 "<?php system('ls /'); ?>"
與 "<?php system('cat /flag'); ?>"
的 PHP 碼,取得 flag。
Flag: NCKUCTF{YOu_are_a_g00d_H4cK3r_Just_Lik3_Vincent55_V3ry_N1c3_945dae36ca165274}
後來發現用 .gif
更方便(下述)。
這次有提示。
如上一題炮製,但會強制被當作圖片,可能是 .htaccess
的關係。
實際看 HTTP 標頭發現沒有 Content-Type:
爲了上傳方便,改用 Python 操作,注意要設定 cookies。
參考 upload.php
的使用者端碼(<input type="file" id="avatar" name="avatar" required="">
),用 Python 送對應的請求。
後來發現用 .gif
更方便,可以直接純文字編輯(後述)。
以下是用上述方法印出的上一題的源始碼:
index.php
login.php
register.php
upload.php
reset.php
logout.php
上一題沒有 images/info.php
。
上一題的 .htaccess
:
.PhP
呢?
上一題的 uploads/.htaccess
:
上一題沒有 images/.htaccess
。
發現可以使用 reset.php
新增使用者,而且不會檢査使用者名稱,並且 login.php
也不檢査。可以直接進 login.php
來避免 session 結束。
我用 Python 操作,但實際上可以直接用開發者工具編輯。
突破使用者名稱後,現在可以控制上傳的目錄了,只是無法繞過副檔名的限制而無法執行。
嘗試了很久都不成功,直到注意到 images/.htaccess
是上一題沒有的。
最終 attack.py
:
使用方式:
Flag: NCKUCTF{YOu_are_7te_real_g00d_H4cK3r_Vincent55_15_V3ry_h4ppY_3281a4f52da44276}
這一題的大部份程式碼與上一題相同,除了以下。
這一題的 images/info.php
:
這一題沒有 .htaccess
。
這一題的 images/.htaccess
:
實際上是把 .htaccess
移到 images/.htaccess
。
下載源始碼後直接搜尋關鍵字。
嘗試根據函式要求調整設定,Font Size 用開發者工具編輯。但沒用。
題目有提示主題與下載版是不同的,所以參數應該不一樣。
參考 theme.ts
:
可得知要利用 cookies 修改 Theme
物件。
送出預設參數後會得到名爲 theme
的 cookie。
如果名爲 PHPSESSID
,刪除該 cookie 後重新整理網頁。
pirmaryColor
可知是 Theme
類別的成員內容。嘗試造新的 cookie 來呼叫 isMagicTheme
的 setter,代回瀏覽器。
重新整理網頁後取得 flag。
Flag: NCKUCTF{40px0npr0t0type}
實際的參數是 Light、#5c85f5
、32px,不過用 cookie 代入參數但不設定 isMagicTheme
的話不會顯示 flag。
假裝競賽已經結束了,資訊差攻擊不知道競賽結束時間的聊天機器人。
可能會需要清 cookie 重設聊天狀態來重現這段聊天。
NCKUCTF{I_LOVE_drinking_MAGIC_COFFEE}
活動名與 flag 怎麼是顚倒的?
下載源始碼並操作,可發現只要成功註冊、通過驗證、並登入,就能拿到 flag。但使用者註冊後不會有人去驗證。
目標是成功執行 verifyaccount()
。
先用 Python 算出賬密的 MD5 雜湊値與驗證網址,然後開始考慮如何從 localhost 呼叫。
attack-url.py
:
可得到 http://localhost:39999/verifyaccount?user=使用者名稱&verify=算出的MD5雜湊値
猜測要利用憑證驗證的機制進行 SSRF 攻擊。用相關的關鍵字搜尋後找到已有的攻擊手稿:https://github.com/ChickenLover/x509-SSRF/blob/master/gen_cert.py
不過直接生產憑證丟上去不被接受。我硏究了一段時間,注意到在 vscode 中有 deprecation 訊息提到 cryptography
函式庫。
嘗試用 cryptography
改寫,但遇到困難,所以照 cryptography
函式庫給的敎學重寫憑證生產器,同時引入 gen_cert.py
中的攻擊手段。
https://cryptography.io/en/latest/x509/tutorial/
gen_cert.py
將生產結果上傳後按「Check」,跳出錯誤,表示驗證器有做事,去戳了目標 URL,而且有回應(要已用對應的賬密註冊)。
此時再輸入賬密登入則成功取得 flag。
Flag: NCKUCTF{CEr7ifiC47e_iz_VerY_fUn_4ffbfda07a61b24f}
Web Any%
一開始會印出源始碼。注意源始碼有提示如何不用 import
關鍵字存取需引入的模組。
可以發現會抓函式呼叫的語法。嘗試用奇怪的方式繞過但失敗,才注意到它沒檢査空白。
注意要用 print()
等使用標準輸出的函式才會有輸出。
Flag: NCKUCTF{the_\t_is_the_tricky_part}
沒用到 \t
(TAB)。
一開始會印出程式碼。
可以發現會抓函式呼叫的語法,而且只要有圓括號就不行。
試了一陣子發現是 exec()
不是 eval()
,所以允許用分號(;
)分隔語句,但無法使用需要換行的 decorator。
讀搜尋到的相關文章時,發現可以修改既有函式的類別的成員,允許定義 magic 方法來重載運算子。
最後選擇把內建函式 help
的加法(+
)重載成 exec
函式,用 help + 任意字串
的方式執行命令,字串中會使用到的圓括號用轉義字元的方式表示。
Flag: NCKUCTF{decorator_can_also_RCE!}
沒用到 decorator。
用題目關鍵字作網路搜尋,猜測是要求 least significant bits 並組成字串,而不是對原音作差異音檔。
偷懶用網路上的手稿改。
attack-BabyLSB.py
:
最後偷懶用 strings
過濾非文字輸出(加快後續處理),用 less
的搜尋功能偷懶找 flag。
Flag: NCKUCTF{l5b_15_u53d_1n_b16_f1l35}
問答題。
"an" 8-bit
可知 255 + 1
會 overflow 變成 0
,所以 x + 5 = 255 + 1
,得 x
爲 251
。
0x4040c0 - 0x4040a0 = 0x20 = 32
,所以要 32 個字元後加 NCKUCTF
。
※ 用 123456789012345678901234567890
的塡法可以刷鍵盤輸入同時能避免忘了塡了幾個字元。
Flag: NCKUCTF{H0p3_U_LeaRNed_Some_concept_Ab0ut_Sof7w4r3_sEcur1tY_N0w_0a05f6c72945fc781e1a0f5b54dc285a}
直接 Google 搜尋可知:
從 Python 3.8 開始,可以直接用 pow(b, -1, m)
求 的最小非負同餘値。
、、與 理論上有無限多組解,只是實際使用上是取最小非負正整數。
是指 而與 的最大公因數爲 的正整數的數量,但因爲源始碼中的 2048-bit 是個質數,。
直接執行可得到 flag。
Flag: NCKUCTF{R54_D0es_n07_w0rk_1ik3_7h15}
用 CTF tools image online
搜尋相關工具,隨便選了一個。https://georgeom.net/StegOnline/image
不小心按了「Browse Bit Planes」後看到以下隱藏圖片。
「之樹」
直接以圖搜尋可以發現這張圖是用圖庫圖改的,所以重點應該會在與原圖不同的地方。
不同之處:
因爲有 64 枝,所以嘗試從左到右深度優先造訪來編號,看看能找到什麼規律。
發現箭頭所指的全是 8 的倍數,而且全沒加點,所以猜測是 ASCII 碼,而加點表示位元爲 1。
偷前面的 attack-BabyLSB.py
的 bin2str()
來用。
attack-good_hacker.py
:
Flag: NCKUCTF{1h4cky0u}
首先先檢査題述圖片有沒有問題。沒有(應該)。
源始碼:
首先先檢査 secret.php
。沒有東西。
剩下的因素只有 ?payload=負載
的「負載」要塡什麼。不能超過 6 個位元。
嘗試 \0
會顯示執行錯誤,表示眞的有在執行。
Shell 命令注入不可行,payload 太短,除非有短命令。
盲解不是好策略。試試看嘗試存取 https://chall.nckuctf.org:29205/pwn_me
會發生什麼事。
程式察覺。
用 objdump
査看字串的位置:
Meowing
:0x2004
N0m N0m
:0x200d
其它物件的位置:
pwn_me
:0x4049
組譯碼分析:
main
:
可知 1 號參數的第 2 字元爲 #
時會印出 Meowing
,可以在 payload 塡入 0#
做到。
剩下的是如何印出 Whale
。用 ;命令
的方式會遇到 payload 太長的問題,所以應該做不到。有可能輸入特別字串的 pwd
(輸出所在目錄路徑)失敗,whoami
(輸出使用者名稱)太長不能用。
嘗試搜尋網路資訊,分析看看 pwn_me
有沒有可利用之處,也沒有。
所以想嘗試用 shell 命令注入的方式塡入大量字串、塡入 Whale
、塡入花括號展開,塡入檔案萬用字元例如 ?#???
展開。
這時突然想到能不能知道有哪些檔案的問題,想到題目是 Docker,與 CertificateChecker 的 Docker 目錄比較後,試了幾個路徑。
沒有拒絕存取。雖然還沒什麼幫助,再試試別的路徑。
要件察覺。
下載 f
後執行,發現直接輸出 Whale
。
最後 payload 塡入 0#;./f
的 URL 編碼結果 0%23;.%2ff
,成功取得 flag。
Flag: NCKUCTF{my_first_pwn+web+rev_XD}
Misc Any%
Given a RSA private key and an encrypted file, how to decrypt RSA?
"an" RSA
用搜尋到的命令嘗試,但會解密失敗。
換個方法,參考 https://cryptography.io/en/latest/hazmat/primitives/asymmetric/rsa/ 寫手稿。
attack-BabyRSA.py
:
cat id_rsa-BabyRSA
,發現它是以 -----BEGIN OPENSSH PRIVATE KEY-----
開頭的,需要轉換成 PEM 格式。
先複製一份來操作。注意必須確定檔案模式是 x00
(-(r)(w)(x)------
)(非擁有者不能讀寫或執行),否則 ssh-keygen
會拒絕轉換。
用 openssl
解碼則是出現亂碼,不過使用 -oaep
參數則是報吿:
發現可能是 padding 的問題,參考 https://docs.openssl.org/3.1/man1/openssl-rsautl/#options 試試不同的 padding。
openssl rsautl -decrypt -inkey id_rsa-attack-BabyRSA -in encrypted-BabyRSA.bin -raw
Flag: NCKUCTF{U_kn0w_how_to_Use_RSA_4e5a3a8f19a14928}
「[新手友善]」
因爲時間安排上的關係,還有題目沒動手解,只好先吿一段落。
Trivial 塡完表單的提示訊息即包含 flag。
Flag: NCKUCTF{Thanks for your playing, have a nice day!}
但是送出時按錯與貼錯送了這次 CTF 第一個與唯好幾個錯誤 flags。
Welcome Any%