# picoCTF-General_Skills writeup picoCTF All General Skills Challenge Writeup [TOC] ## picoCTF 2025 ### FANTASY CTF > EASY 題目給了一個 `netcat` 連線資訊 連進去之後一直按 `Enter` 會出現第一個選單 ``` Options: A) *Register multiple accounts* B) *Share an account with a friend* C) *Register a single, private account* [a/b/c] > ``` 隨便選一個就好 繼續按 `Enter` 會遇到第二個選單 ``` Options: A) *Play the game* B) *Search the Ether for the flag* [a/b] > ``` 這次選 `a` 繼續按 `Enter` 按完就有 flag 了 --- ### Rust fixme 1 > EASY 這個是考 rust 的 code review ```rust use xor_cryptor::XORCryptor; fn main() { // Key for decryption let key = String::from("CSUCKS") // How do we end statements in Rust? // Encrypted flag values let hex_values = ["41", "30", "20", "63", "4a", "45", "54", "76", "01", "1c", "7e", "59", "63", "e1", "61", "25", "7f", "5a", "60", "50", "11", "38", "1f", "3a", "60", "e9", "62", "20", "0c", "e6", "50", "d3", "35"]; // Convert the hexadecimal strings to bytes and collect them into a vector let encrypted_buffer: Vec<u8> = hex_values.iter() .map(|&hex| u8::from_str_radix(hex, 16).unwrap()) .collect(); // Create decrpytion object let res = XORCryptor::new(&key); if res.is_err() { ret; // How do we return in rust? } let xrc = res.unwrap(); // Decrypt flag and print it out let decrypted_buffer = xrc.decrypt_vec(encrypted_buffer); println!( ":?", // How do we print out a variable in the println function? String::from_utf8_lossy(&decrypted_buffer) ); } ``` 按照這些步驟修正錯誤的語法 1. `let key = String::from("CSUCKS")` 結尾加入 `;` 2. `ret;` 改成 `return;` 3. `println!(":?", String::from_utf8_lossy(&decrypted_buffer));` 把 `":?"` 改成 `"{}"` 修正錯誤的語法後 在專案根目錄下執行 `cargo run` 就會輸出 flag 了 --- ### Rust fixme 2 > EASY 一樣的 code review ```rust use xor_cryptor::XORCryptor; fn decrypt(encrypted_buffer:Vec<u8>, borrowed_string: &String){ // How do we pass values to a function that we want to change? // Key for decryption let key = String::from("CSUCKS"); // Editing our borrowed value borrowed_string.push_str("PARTY FOUL! Here is your flag: "); // Create decrpytion object let res = XORCryptor::new(&key); if res.is_err() { return; // How do we return in rust? } let xrc = res.unwrap(); // Decrypt flag and print it out let decrypted_buffer = xrc.decrypt_vec(encrypted_buffer); borrowed_string.push_str(&String::from_utf8_lossy(&decrypted_buffer)); println!("{}", borrowed_string); } fn main() { // Encrypted flag values let hex_values = ["41", "30", "20", "63", "4a", "45", "54", "76", "01", "1c", "7e", "59", "63", "e1", "61", "25", "0d", "c4", "60", "f2", "12", "a0", "18", "03", "51", "03", "36", "05", "0e", "f9", "42", "5b"]; // Convert the hexadecimal strings to bytes and collect them into a vector let encrypted_buffer: Vec<u8> = hex_values.iter() .map(|&hex| u8::from_str_radix(hex, 16).unwrap()) .collect(); let party_foul = String::from("Using memory unsafe languages is a: "); // Is this variable changeable? decrypt(encrypted_buffer, &party_foul); // Is this the correct way to pass a value to a function so that it can be changed? } ``` 要修復的點 1. `borrowed_string: &String` 改為 `borrowed_string: &mut String` 2. `let party_foul` 改為 `let mut party_foul` 3. `&party_foul` 改為 `&mut party_foul` 更改完後一樣 `cargo run` 就會輸出 flag --- ### Rust fixme 3 > EASY ```rust use xor_cryptor::XORCryptor; fn decrypt(encrypted_buffer: Vec<u8>, borrowed_string: &mut String) { // Key for decryption let key = String::from("CSUCKS"); // Editing our borrowed value borrowed_string.push_str("PARTY FOUL! Here is your flag: "); // Create decryption object let res = XORCryptor::new(&key); if res.is_err() { return; } let xrc = res.unwrap(); // Did you know you have to do "unsafe operations in Rust? // https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html // Even though we have these memory safe languages, sometimes we need to do things outside of the rules // This is where unsafe rust comes in, something that is important to know about in order to keep things in perspective // unsafe { // Decrypt the flag operations let decrypted_buffer = xrc.decrypt_vec(encrypted_buffer); // Creating a pointer let decrypted_ptr = decrypted_buffer.as_ptr(); let decrypted_len = decrypted_buffer.len(); // Unsafe operation: calling an unsafe function that dereferences a raw pointer let decrypted_slice = std::slice::from_raw_parts(decrypted_ptr, decrypted_len); borrowed_string.push_str(&String::from_utf8_lossy(decrypted_slice)); // } println!("{}", borrowed_string); } fn main() { // Encrypted flag values let hex_values = ["41", "30", "20", "63", "4a", "45", "54", "76", "12", "90", "7e", "53", "63", "e1", "01", "35", "7e", "59", "60", "f6", "03", "86", "7f", "56", "41", "29", "30", "6f", "08", "c3", "61", "f9", "35"]; // Convert the hexadecimal strings to bytes and collect them into a vector let encrypted_buffer: Vec<u8> = hex_values.iter() .map(|&hex| u8::from_str_radix(hex, 16).unwrap()) .collect(); let mut party_foul = String::from("Using memory unsafe languages is a: "); decrypt(encrypted_buffer, &mut party_foul); } ``` 這次要修正的點只有一個 把這個不安全的操作片段用 `unsafe` 包起來 ```rust let decrypted_slice = std::slice::from_raw_parts(decrypted_ptr, decrypted_len); ``` 修改後 ```rust let decrypted_slice = unsafe { std::slice::from_raw_parts(decrypted_ptr, decrypted_len) }; ``` 一樣執行 `cargo run` 就會有 flag --- ### YaraRules0x100 > MEDIUM 把檔案下載下來後用密碼 `picoctf` 解壓縮得到 `suspicious.exe` 先用 `file` 查看後知道有用 `upx` 加殼 ```bash ❯ file suspicious.exe suspicious.exe: PE32 executable (GUI) Intel 80386, for MS Windows, UPX compressed ``` 用 `upx` 解殼後用 `strings` 查看特徵字串並撰寫規則 ```bash upx -d suspicious.exe ``` 規則寫完後用 `socat` 送出測試 ```bash socat -t60 - TCP:standard-pizzas.picoctf.net:55501 < rule.yar ``` 最後經過不斷測試後和 ~~ChatGPT~~ 得到這個成功通過檢測的規則 ```yar rule TestRule { meta: description = "基於字串模式和API調用檢測可疑執行檔" strings: // UPX壓縮檔案的標記 $upx1 = "UPX0" ascii $upx2 = "UPX1" ascii $upx3 = "UPX!" ascii // 可疑字串:可能與惡意行為相關的字串 $mal_str1 = "NtQueryInformationProcess" ascii wide // 進程查詢,常見於反調試 $mal_str2 = "DebugActiveProcess" ascii wide // 調試進程的API,通常與惡意程式有關 $mal_str3 = "DebugActiveProcessStop" ascii wide // 停止調試進程 $mal_str4 = "CreateProcessA" ascii wide // 用於創建新進程,可能會用來執行惡意程式 $mal_str5 = "TerminateProcess" ascii wide // 用於終止進程,可能用於終止監控或防禦程序 $mal_str6 = "CreateToolhelp32Snapshot" ascii wide // 用於列舉進程,常用於惡意進程掃描 $mal_str7 = "Process32FirstW" ascii wide // 進程掃描API $mal_str8 = "Process32NextW" ascii wide // 進程掃描API $mal_str9 = "Sleep" ascii wide // 用來使程序等待一段時間,常見於惡意行為 $mal_str10 = "OutputDebugStringW" ascii wide // 用於輸出調試字串,可能被用來進行調試或偵測 // 十六進制模式:可能的惡意代碼或數據 $hex1 = { 25 64 64 64 6C 21 30 } // "%dddl!0" $hex2 = { 62 33 60 35 64 35 6C 35 } // "b3`5d5l5" $hex3 = { 6E 74 51 75 65 72 79 49 6E 66 6F 72 6D 61 74 69 6F 6E 50 72 6F 63 65 73 73 } // "NtQueryInformationProcess" // 常見的 PE 區段名稱:用來識別檔案結構 $sect1 = ".text" ascii // 程式碼區 $sect2 = ".rdata" ascii // 只讀數據區 $sect3 = ".data" ascii // 數據區 $sect4 = ".rsrc" ascii // 資源區 $sect5 = ".reloc" ascii // 重定位區 $sect6 = ".upx" ascii // UPX 壓縮區 condition: uint16(0) == 0x5A4D and // MZ 頭標誌,檢查是否為 PE 檔案 filesize < 100MB and // 檔案大小小於 100MB,避免錯誤檢測大檔案 ( // 1. 檢測 UPX 壓縮過的檔案並與可疑字串匹配 (1 of ($upx*) and 1 of ($mal_str*)) or // 2. 檢測可疑的 API 調用,並結合檢查 PE 檔案的區段名稱 (all of ($mal_str*) and 1 of ($sect*)) or // 3. 檢測特徵的惡意位元模式 (1 of ($hex*)) ) } ``` --- ## picoCTF 2024 ### Binary Search > EASY 按照題目給的連線資訊 `ssh` 進去後就是一般的猜數字遊戲 用 `二分搜` 的概念就可以通關了 --- ### Time Machine > EASY 題目檔案下載下來解壓縮後的資料夾內有一個文字檔 ```txt This is what I was working on, but I'd need to look at my commit history to know why... ``` 根據裡面的 `commit history` 我們可以猜測和 `git` 有關 查看 `git log` 就會找到 flag ``` bash git log ``` --- ### Super SSH > EASY 直接根據題目給的連線資訊和帳號密碼 `ssh` 進機器就會輸出 flag ```bash ssh -p 54321 ctf-player@titan.picoctf.net ``` --- ### endianness > EASY 根據題目給的挑戰原始碼的關鍵片段 我們可以知道 `little_endian` 指的是字串中的每個字母 `由後往前` 組成的 `hex` `bin_endian` 指的是字串中的每個字母 `由前往後` 組成的 `hex` ```c char *find_little_endian(const char *word) { size_t word_len = strlen(word); char *little_endian = (char *)malloc((2 * word_len + 1) * sizeof(char)); for (size_t i = word_len; i-- > 0;) { snprintf(&little_endian[(word_len - 1 - i) * 2], 3, "%02X", (unsigned char)word[i]); } little_endian[2 * word_len] = '\0'; return little_endian; } char *find_big_endian(const char *word) { size_t length = strlen(word); char *big_endian = (char *)malloc((2 * length + 1) * sizeof(char)); for (size_t i = 0; i < length; i++) { snprintf(&big_endian[i * 2], 3, "%02X", (unsigned char)word[i]); } big_endian[2 * length] = '\0'; return big_endian; } ``` 因為題目沒有限制連線時間 所以 `nc` 進去取得字串後對照 `ascii` 表就可以得出 要輸入的內容 ``` Word: aaqua Enter the Little Endian representation: 6175716161 Enter the Big Endian representation: 6161717561 ``` > `a` -> `0x61`, `q` -> `0x71`, `u` -> `0x75` --- ### Commitment Issues > EASY 根據題目標題可以猜到跟 `git` 的 `commit` 有關 檔案下載下來後用 `git log` 可以看到有一筆過往的 `commit` ![](https://hackmd.io/_uploads/ByzrPWPxex.png =500x) 用 `git checkout` 查看過往的 `commit` 紀錄 ```bash git checkout ea859bf3b5d94ee74ce5ee1afa3edd7d4d6b35f0 ``` 這時候再次查看 `message.txt` 就會看到 flag 了 --- ### Collaborative Development > EASY 這題提到了協作 所以我們聯想到跟 `git branch` 分支功能有關 先查看所有的分支 ```bash git branch -a ``` 可以發現除了當前的 `main` 還有其他三個分支 ``` feature/part-1 feature/part-2 feature/part-3 * main ``` 用 `git switch` 切換過去每個分支後查看 `flag.py` 就可以搜集到每一段 flag 了 ```bash git switch feature/part-1 git switch feature/part-2 git switch feature/part-3 ``` --- ### Blame Game > EASY 根據題目的敘述可以推測一樣和 `commit` 有關 直接 `git log` 發現非常多筆 分不出來 所以改成針對目錄下唯一的檔案查看 `log` 後就可以在 `Author` 的地方看到 flag ```bash git log message.py ``` --- ### binhexa > EASY 這題 `nc` 到題目後 會叫我們進行一些 `binary` 運算 我們用 `python` 解題 因為要回傳 `binary` 所以用 `bin()` 包起來 以下提供幾個範例 > 要注意在數字最前面加入 `0b` python 才會當成是 `binary` ```python num1 = 0b11011011 num2 = 0b10011110 bin(num1 << 1) bin(num1 >> 1) bin(num1 & num2) ``` 得到的結果會是 `0bxxxxxxxx` 要記得把 `0b` 去掉再回傳給題目 運算到最後一個的時候要改成回傳 `hex` 把運算後的結果(記得要有 `0b` ) 一樣使用 python ```python hex(result) ``` 會得到 `0x???` 會傳這個數值就可以得到 flag --- ### dont-you-love-banners > MEDIUM 題目給了兩個連線資訊 先 `nc` 到第一台機器後得到一個 password ```bash ❯ nc tethys.picoctf.net 61195 SSH-2.0-OpenSSH_7.6p1 My_Passw@rd_@1234 ``` 連線到第二台機器並回答問題 回答完就可以正常連線進機器了 ```bash ❯ nc tethys.picoctf.net 53159 ************************************* **************WELCOME**************** ************************************* what is the password? My_Passw@rd_@1234 What is the top cyber security conference in the world? DEFCON the first hacker ever was known for phreaking(making free phone calls), who was it? John Draper player@challenge:~$ ``` 接下來我們可以知道 flag 在 `/root` 底下 但我們沒有權限讀取 這邊有兩個解法: 1. symlink 符號連結 在 `/root` 底下雖然 `flag.txt` 讀不到 但我們可以讀到 `script.py` ```python import os import pty incorrect_ans_reply = "Lol, good try, try again and good luck\n" if __name__ == "__main__": try: with open("/home/player/banner", "r") as f: print(f.read()) except: print("*********************************************") print("***************DEFAULT BANNER****************") print("*Please supply banner in /home/player/banner*") print("*********************************************") try: request = input("what is the password? \n").upper() while request: if request == 'MY_PASSW@RD_@1234': text = input("What is the top cyber security conference in the world?\n").upper() if text == 'DEFCON' or text == 'DEF CON': output = input( "the first hacker ever was known for phreaking(making free phone calls), who was it?\n").upper() if output == 'JOHN DRAPER' or output == 'JOHN THOMAS DRAPER' or output == 'JOHN' or output== 'DRAPER': scmd = 'su - player' pty.spawn(scmd.split(' ')) else: print(incorrect_ans_reply) else: print(incorrect_ans_reply) else: print(incorrect_ans_reply) break except: KeyboardInterrupt ``` 因為我們知道這個 `script.py` 是以 `root` 身份執行的 所以可以控制檔案內引用的 `/home/player/banner` 我們可以把 `/root/flag.txt` 連結到 `/home/player/banner` 這樣一登入機器時就會顯示 flag ```bash ln -s --force /root/flag.txt /home/player/banner ``` ![](https://hackmd.io/_uploads/Syqlg7Plel.png =500x) 2. root password crack 爆破 root 密碼 我們可以讀取 `/etc/passwd` 和 `/etc/shadow` 所以把 `root` 的密碼存到本地後 用 `unshadow` 產生 hash 再用 `john` 搭配 `rockyou.txt` 爆破密碼 ``` passwd : root:x:0:0:root:/root:/bin/bash shadow : root:$6$6QFbdp2H$R0BGBJtG0DlGFx9H0AjuQNOhlcssBxApM.CjDEiNzfYkVeJRNy2d98SDURNebD5/l4Hu2yyVk.ePLNEg/56DV0:19791:0:99999:7::: ``` ```bash unshadow passwd shadow > hash ❯ john --wordlist=rockyou.txt hash Press 'q' or Ctrl-C to abort, almost any other key for status iloveyou (root) 1g 0:00:00:00 DONE (2025-05-06 13:19) 8.333g/s 1066p/s 1066c/s 1066C/s 123456..diamond ``` 得到密碼後就可以用 `su` 切換到 `root` 接著就可以讀取 `/root/flag.txt` 了 ```bash player@challenge:~$ su root su root Password: iloveyou root@challenge:/home/player# ``` --- ### SansAlpha > MEDIUM 這題是一個 shell escape 的 sandbox 連線進去後 發現所有字母都被擋了 嘗試 `*/*` 發現 flag.txt 的位置 但是沒辦法讀取 改成使用 `?` 查看有沒有可用的指令 ```shell SansAlpha$ /???/?????? /bin/base32: extra operand '/bin/base64' Try '/bin/base32 --help' for more information. ``` 找到 `base64` 指令 我們先確定 `flag.txt` 的位置 `/home/ctf-player/blargh/flag.txt` ```shell SansAlpha$ */* bash: blargh/flag.txt: Permission denied SansAlpha$ ../* bash: ../ctf-player: Is a directory SansAlpha$ ./* bash: ./blargh: Is a directory SansAlpha$ /????/??????????/??????/???????? bash: /home/ctf-player/blargh/flag.txt: Permission denied ``` 確定好位置後就可以用 `base64` 加密了 但這時候遇到了一個問題 有另一個指令 `/bin/x86_64` 因為我們是用 `?` 這個萬用匹配字元 所以 `x86_64` 也符合條件 要繞過這個限制就要新增 `[!_]` 這個指令的意思是「任何不是底線的字元」這樣一來 `x86_64` 就不符合條件了 ```shell SansAlpha$ /???/????64 /????/??????????/??????/???????? /bin/base64: extra operand '/bin/x86_64' Try '/bin/base64 --help' for more information. ``` 最終的 payload : ```shell SansAlpha$ /???/???[!_]64 /????/??????????/??????/???????? cmV0dXJuIDAgcGljb0NURns3aDE1X211MTcxdjNyNTNfMTVfbTRkbjM1NV8zNmE2NzRjMH0= ``` --- ## picoCTF 2023 ### repetitions > EASY 一個經過 6 次 `base64` 加密的字串 所以重複解密 6 次就可以了 --- ### useless > MEDIUM 用題目給的資訊 `ssh` 連線進機器後有一個 shell script ```bash #!/bin/bash # Basic mathematical operations via command-line arguments if [ $# != 3 ] then echo "Read the code first" else if [[ "$1" == "add" ]] then sum=$(( $2 + $3 )) echo "The Sum is: $sum" elif [[ "$1" == "sub" ]] then sub=$(( $2 - $3 )) echo "The Substract is: $sub" elif [[ "$1" == "div" ]] then div=$(( $2 / $3 )) echo "The quotient is: $div" elif [[ "$1" == "mul" ]] then mul=$(( $2 * $3 )) echo "The product is: $mul" else echo "Read the manual" fi fi ``` 在最下面那一行可以看到 `Read the manual` 所以我們用 `man` 查看這個腳本的名稱 `useless` ```bash man useless ``` --- ### Special > MEDIUM 這次也是一個 shell 的 sandbox escape 這題的解法有非常多種 這裡整理一下我的及網路上找到的各種解法 1. ${0} 因為 ${0} 是只自己的意思 又因為當前執行的是 shell 所以執行 ${0} 就會再開啟一個 shell 新的 shell 就不會有限制了 2. <something><something>cmd 隨便輸入測試一下之後可以知道他似乎只會改前兩個字元 所以我們只要在前面加兩個 padding 就可以執行 command 了 ```bash ((ls)) ""ls"" ``ls`` aa`ls` a;`ls` a;$(ls) ``` 3. ${parameter=cmd} 這是 shell 的變數預設語法 他的意思是 如果變數 `parameter` 尚未定義 就把 `cmd` 指定給 `parameter` 並回傳 `parameter` 的值 也就是說 ```bash ${a=ls} ``` 其實等價於 ```bash a=ls $a ``` 4. Command Substitution 命令替換 我們先看一個例子 在這個例子中我們可以看到 我們執行 `ls` 後 輸出的結果被當成指令執行了 這是因為 `Command Substitution 命令替換` 會將一個命令的輸出作為參數傳給另一個命令 所以要注意 payload 中必須要有分隔符號 `;` 和命令替換 ` `` ` `$()` ```bash Special$ a;`ls` A;`ls` sh: 1: A: not found sh: 1: blargh: not found ``` 知道這個技巧後 我們就可以想辦法讓他成功執行 `bash` 隨便找一個需要等待直到有輸入的指令 輸入 `bash` 後按下 `Ctrl + D` 就會成功執行了 > 在下面這個例子中 ``` a;`cat` ``` 就會變成 ``` a;`bash` ``` > 等待直到有輸入的指令我測試了一下除了 `cat` 還有 `tee` 和 `dd` 也都可以成功執行 ```bash Special$ a;`cat` A;`cat` sh: 1: A: not found bash <Ctrl + D> ctf-player@challenge:~$ ``` 進到 shell 後就可以任意翻找檔案了 查看 `/etc/passwd` 可以找到這題的原始碼 ``` ctf-player:x:1000:1000::/home/ctf-player:/usr/local/Special.py ``` ```python #!/usr/bin/python3 import os from spellchecker import SpellChecker spell = SpellChecker() while True: cmd = input("Special$ ") rval = 0 if cmd == 'exit': break elif 'sh' in cmd: print('Why go back to an inferior shell?') continue elif cmd[0] == '/': print('Absolutely not paths like that, please!') continue # Spellcheck spellcheck_cmd = '' for word in cmd.split(): fixed_word = spell.correction(word) if fixed_word is None: fixed_word = word spellcheck_cmd += fixed_word + ' ' # Capitalize fixed_cmd = list(spellcheck_cmd) words = spellcheck_cmd.split() first_word = words[0] first_letter = first_word[0] if ord(first_letter) >= 97 and ord(first_letter) <= 122: fixed_cmd[0] = chr(ord(spellcheck_cmd[0]) - 0x20) fixed_cmd = ''.join(fixed_cmd) try: print(fixed_cmd) os.system(fixed_cmd) except: print("Bad command!") ``` --- ### Specialer > MEDIUM 這題不知道被 ban 了哪些指令 嘗試過後發現 `echo` 沒有被擋 於是使用 `echo *` 當作 `ls` 用 `echo $(< <filepath>)` 當作 `cat` 嘗試一下就可以找到 flag 了 ```bash Specialer$ echo * abra ala sim Specialer$ echo $(< ala/kazam.txt) return 0 picoCTF{<flag>} ``` --- ### Permissions > MEDIUM 這題是一個經典的提權題目 用 `sudo -l` 查看有哪些指令是可以以 `root` 身份執行而不用密碼的 ```bash picoplayer@challenge:~$ sudo -l [sudo] password for picoplayer: Matching Defaults entries for picoplayer on challenge: env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin User picoplayer may run the following commands on challenge: (ALL) /usr/bin/vi ``` 發現可以執行 `vi` 用 `vi` 查看 `/root/.flag.txt` ```bash sudo /usr/bin/vi /root/.flag.txt ``` --- ### chrono > MEDIUM 根據題目敘述可以知道跟 linux 的排程 tasks 有關 查看 `crontab` 的設定檔就解開了 ```bash cat /etc/crontab ``` --- ## Beginner picoMini 2022 ### runme.py > EASY 直接查看檔案或者用 `python` 跑一次都會有 flag --- ### PW Crack 1 > EASY 把密文和解密的 script 放在同一個目錄後 查看 `level1.py` 得到 password 執行 `level.py` 輸入密碼 `1e1a` 就會輸出 flag 了 --- ### PW Crack 2 > EASY 跟第一關一樣的做法 但這次的 `level2.py` 裡面給的密碼是 `hex` ``` chr(0x64) + chr(0x65) + chr(0x37) + chr(0x36) ``` > 可以用 python `print(chr(0x64))` 來得到 ascii 解密後得到 `de76` 一樣執行並輸入密碼就可以得到 flag --- ### HashingJobApp > EASY 連線進去題目後會給你一串文字要你回傳 `md5` 因為給的連線時間還蠻長的 所以不用寫腳本 把字串丟到 [CyberChef 的 md5 功能](https://gchq.github.io/CyberChef/#recipe=MD5()) 就會得到回傳的 `md5` 值 連續回答幾關後就回得到 flag ![](https://hackmd.io/_uploads/rJtBFVdgee.png =600x) --- ### Glitch Cat > EASY 這題連線進去給了一串文字 用 python 的 `print` 把這些字串轉成 ascii 就會有完整的 flag ```python print('picoCTF{gl17ch_m3_n07_' + chr(0x62) + chr(0x64) + chr(0x61) + chr(0x36) + chr(0x38) + chr(0x66) + chr(0x37) + chr(0x35) + '}') ``` --- ### fixme1.py > EASY 這題給了一個 python script 要我們把它復原 最下面這一行左邊多了空格 把空格刪掉再執行就好了 ![](https://hackmd.io/_uploads/r1RDjEulex.png) --- ### fixme2.py > EASY 這次我們可以看到在 `if` 的地方 `=` 只用了一個 改成 `==` 就可以通過了 ![](https://hackmd.io/_uploads/Byex2VOell.png =600x) --- ### convertme.py > EASY 這題要我們把數字從 `Decimal` 十進位轉成 `binary` 二進位 用 [CyberChef](https://gchq.github.io/CyberChef/#recipe=From_Decimal('Space',false)To_Binary('None',4)) 選取 `From Decimal` 和 `To Binary` 設定 `Byte Length` 為 `4` 回傳得到的 `binary` 就會輸出 flag 了 --- ### Codebook > EASY 把兩個檔案放在同個目錄後 用 `python` 執行 `code.py` 就會得到 flag --- ### Serpentine > MEDIUM 這題給了一個 python script 執行後有三個選項 選擇 `print_flag` 會提示查看 source code 查看後發現沒有使用 `print_flag()` 修改程式碼加入 `print_flag()` 再次執行並選擇 b 就好了 ![](https://hackmd.io/_uploads/H1_mkSdxge.png =300x) --- ### PW Crack 3 > MEDIUM 這次一樣要找密碼 但這次密碼有被加密過 `level3.py` 中有給明文的陣列(其中一個會是正確的) 裡面也有給加密的函式 直接仿造加密過程爆破出正確的明文密碼 ```python import hashlib correct_pw_hash = open('level3.hash.bin', 'rb').read() def hash_pw(pw_str): pw_bytes = bytearray() pw_bytes.extend(pw_str.encode()) m = hashlib.md5() m.update(pw_bytes) return m.digest() pos_pw_list = ["6997", "3ac8", "f0ac", "4b17", "ec27", "4e66", "865e"] for i in pos_pw_list: user_pw_hash = hash_pw(i) if user_pw_hash == correct_pw_hash: print(i) break ``` 執行後就會得到正確的密碼了 用這個密碼執行 `level3.py` 就會得到 flag --- ### PW Crack 4 > MEDIUM 這題跟上一題做法一樣 更改 `pos_pw_list` 和 `correct_pw_hash` 就可以了 --- ### PW Crack 5 > MEDIUM 這次的 `pos_pw_list` 變成了字典檔 所以我們要更改讀取方式 改成一行一行讀取 又因為字典檔內的字串不是 `str` 我們在加密前要先轉成 `str` ```python! import hashlib correct_pw_hash = open('level5.hash.bin', 'rb').read() def hash_pw(pw_str): pw_bytes = bytearray() pw_bytes.extend(pw_str.encode()) m = hashlib.md5() m.update(pw_bytes) return m.digest() with open('dictionary.txt', 'r') as pos_pw_list : for i in pos_pw_list: user_pw_hash = hash_pw(str(i.strip())) if user_pw_hash == correct_pw_hash : print(i) break ``` --- ## picoCTF 2021 ### Magikarp Ground Mission > EASY 連線進去後跟著步驟就可以拼出完整的 flag 1. `cat 1of3.flag.txt` 2. `cd /` `cat 2of3.flag.txt` 3. `cd ~` `cat 3of3.flag.txt` --- ### Tab, Tab, Attack > EASY 解壓縮後 輸入 `cd` 之後一直按 `tab` 直到按不了之後會看到一個執行檔 執行後就有 flag 了 --- ### Wave a flag > EASY 先 `chmod +x` 給執行權限後 根據提示執行 `./warm -h` 就有 flag --- ### Python Wrangling > EASY 把檔案都下載下來後 執行 `python3 ende.py -d flag.txt.en` 密碼使用 `pw.txt` 裡面的密碼 --- ### Static ain't always noise > EASY 檔案下載下來後用 `chmod +x` 給 `ltdis.sh` 執行權限 接下來執行 `./ltdis.sh static` 最後查看 `static.ltdis.strings.txt` 就可以找到 flag --- ### Nice netcat... > EASY 連線進去會輸出一堆數字 把這些數字複製起來貼到 [CyberChef 的 From Decimal](https://gchq.github.io/CyberChef/#recipe=From_Decimal('Line%20feed',false)) 選擇 `Line feed` 後就會有完整的 flag --- ### Obedient Cat > EASY 就 `cat flag`。 --- ## picoCTF 2019 ### 2Warm > EASY 題目要我們 convert the number 42 (base 10) to binary (base 2) 用 [CyberChef](https://gchq.github.io/CyberChef/#recipe=From_Decimal('Space',false)To_Binary('Space',4)) 轉換後把答案包在 `picoCTF{}` 提交就可以了 --- ### First Grep > EASY 給了一堆文字的檔案 用 `grep` 找出 flag ```bash! grep picoCTF file ``` --- ### Bases > EASY 題目給了一個 `base64` 字串 `bDNhcm5fdGgzX3IwcDM1` 用 `base64` 解密後 包在 `picoCTF{}` 提交 --- ### Warmed Up > EASY 這題問了 `What is 0x3D (base 16) in decimal (base 10)?` 一樣用 [CyberChef](https://gchq.github.io/CyberChef/#recipe=From_Hex('Auto')To_Decimal('Space',false)) 後包在 `picoCTF` 就好了 --- ### strings it > EASY 根據標題用 `strings` 查看檔案 並用 `grep` 過濾出 flag ```bash! strings strings | grep pico ``` --- ### what's a net cat? > EASY 連線進去就好了 `nc jupiter.challenges.picoctf.org 64287` --- ### Lets Warm Up > EASY 題目說這個字在 `hex` 下是 `0x70` 轉換成 `Dec` 後得到 `112` 又問在 `ascii` 下會是什麼 對照 `ascii` 表就可以知道了 最後包在 `picoCTF` 提交就好了 ``` ❯ ascii Usage: ascii [-adxohv] [-t] [char-alias...] -t = one-line output -a = vertical format -d = decimal table -o = octal table -x = hex table -b binary table -h = this help screen -v = version information Prints all aliases of an ASCII character. Args may be chars, C \-escapes, English names, ^-escapes, ASCII mnemonics, or numerics in decimal/octal/hex. Dec Hex Dec Hex Dec Hex Dec Hex Dec Hex Dec Hex Dec Hex Dec Hex 0 00 NUL 16 10 DLE 32 20 48 30 0 64 40 @ 80 50 P 96 60 ` 112 70 p 1 01 SOH 17 11 DC1 33 21 ! 49 31 1 65 41 A 81 51 Q 97 61 a 113 71 q 2 02 STX 18 12 DC2 34 22 " 50 32 2 66 42 B 82 52 R 98 62 b 114 72 r 3 03 ETX 19 13 DC3 35 23 # 51 33 3 67 43 C 83 53 S 99 63 c 115 73 s 4 04 EOT 20 14 DC4 36 24 $ 52 34 4 68 44 D 84 54 T 100 64 d 116 74 t 5 05 ENQ 21 15 NAK 37 25 % 53 35 5 69 45 E 85 55 U 101 65 e 117 75 u 6 06 ACK 22 16 SYN 38 26 & 54 36 6 70 46 F 86 56 V 102 66 f 118 76 v 7 07 BEL 23 17 ETB 39 27 ' 55 37 7 71 47 G 87 57 W 103 67 g 119 77 w 8 08 BS 24 18 CAN 40 28 ( 56 38 8 72 48 H 88 58 X 104 68 h 120 78 x 9 09 HT 25 19 EM 41 29 ) 57 39 9 73 49 I 89 59 Y 105 69 i 121 79 y 10 0A LF 26 1A SUB 42 2A * 58 3A : 74 4A J 90 5A Z 106 6A j 122 7A z 11 0B VT 27 1B ESC 43 2B + 59 3B ; 75 4B K 91 5B [ 107 6B k 123 7B { 12 0C FF 28 1C FS 44 2C , 60 3C < 76 4C L 92 5C \ 108 6C l 124 7C | 13 0D CR 29 1D GS 45 2D - 61 3D = 77 4D M 93 5D ] 109 6D m 125 7D } 14 0E SO 30 1E RS 46 2E . 62 3E > 78 4E N 94 5E ^ 110 6E n 126 7E ~ 15 0F SI 31 1F US 47 2F / 63 3F ? 79 4F O 95 5F _ 111 6F o 127 7F DEL ``` --- ### mus1c > MEDIUM 把題目給的歌詞丟到 [rockstar](https://web.archive.org/web/20190522020843/https://codewithrockstar.com/online) 得到一串數字 用 [CyberCbef](https://gchq.github.io/CyberChef/#recipe=From_Decimal('Line%20feed',false)) 解碼後 把得到的字串用 `picoCTF` 包起來就好 --- ### 1_wanna_b3_a_r0ck5tar > MEDIUM 這次沒辦法直接 decode 使用 [rockstart-py](https://github.com/yyyyyyyan/rockstar-py) 把這個語言轉成 `python` ```bash pip intall rockstar-py rockstar-py -i lyrics.txt -o lyrics.py ``` 轉換後得到 python 後我們就可以知道要輸入 `10` 和 `170` ```python Rocknroll = True Silence = False a_guitar = 10 Tommy = 44 Music = 170 the_music = input() if the_music == a_guitar: print("Keep on rocking!") the_rhythm = input() if the_rhythm - Music == False: Tommy = 66 print(Tommy!) Music = 79 Jamming = 78 print(Music!) print(Jamming!) Tommy = 74 print(Tommy!) They are dazzled audiences print(it!) Rock = 86 print(it!) Tommy = 73 print(it!) break print("Bring on the rock!") Else print("That ain't it, Chief") break ``` 再次丟到 [rockstar](https://web.archive.org/web/20190522020843/https://codewithrockstar.com/online) 理論上會輸出 但因為他現在好像壞掉了 ~~所以直接抄答案~~ ``` Keep on rocking! 66 79 78 74 79 86 73 Program completed in 118 ms ``` 一樣 decode 後得到的放入 `picoCTF` 就可以了 --- ### flag_shop > MEDIUM 這題是考 `integer overflow 整數溢位` 查看原始碼可以看到這個片段 ```c int number_flags = 0; fflush(stdin); scanf("%d", &number_flags); if(number_flags > 0){ int total_cost = 0; total_cost = 900*number_flags; printf("\nThe final cost is: %d\n", total_cost); if(total_cost <= account_balance){ account_balance = account_balance - total_cost; printf("\nYour current balance after transaction: %d\n\n", account_balance); } else{ printf("Not enough funds to complete purchase\n"); } ``` 從這裡我們可以猜想 如果控制 `total_cost` 變成負數 這樣就會變成 `account_balance = account_balance - (-total_cost)` 實際會是 `+` `int[32]` (32-bit) 的範圍是 `-2147483648 ~ 2147483647` 所以我們讓他大於 `2147483647` > 低 32 位 0 ~ 2147483647 > 高 32 位 2147483647 ~ 4294967295 (這裡因為超過最大值所以會變成負數) 所以實際是 > -2147483647 ~ 0 嘗試 `9999999999` (10個9) 卻發現沒有成功 這是因為取模後沒有超過最大值 ```c 900 * 9999999999 = 8999999999100 // 取低 32 位 8999999999100 % 2^32 = 2043513980 // 結果會是正的 所以不會造成 integer overflow 2043513980 < 2147483647 ``` 嘗試 `999999999` (9個9) 有成功 這是因為取模後有超過最大值 `total_cost` 就會變成 `-1943133060` ```c 900 * 999999999 = 899,999,999,100 // 取低 32 位 899999999100 % 2^32 = 2351834236 // 2351834236 對應的有符號整數是 2^32 - 2351834236 = 1943133060 // 再加上符號位(取負數) -1943133060 // 結果會是負的 所以會造成 integer overflow 2351834236 > 2147483647 ``` 成功拿到一堆錢後就可以去買 flag 了 --- ### plumbing > MEDIUM 連進去後會直接輸出一大坨東西 所以我們先把全部的輸出另存起來 ```bash! nc jupiter.challenges.picoctf.org 4427 > flag.txt ``` 得到全部的輸出後 用 `grep` 尋找 flag ```bash grep picoCTF flag.txt ``` --- ### Based > MEDIUM 連線進去後過三關 1. [From Binary](https://gchq.github.io/CyberChef/#recipe=From_Binary('Space',8)) 2. [From Octal](https://gchq.github.io/CyberChef/#recipe=From_Octal('Space')) 3. [From Hex](https://gchq.github.io/CyberChef/#recipe=From_Hex('None')) > 最後會發現其實都是||oven|| --- ## picoGym Exclusive ### Big Zip > EASY 下載下來後會是一個很亂的資料夾 裡面有一大堆檔案和資料夾 用 `grep` 搜尋 flag ```bash! grep -r "picoCTF{" . ``` --- ### First Find > EASY 根據題目用 `find` 找到 `uber-secret.txt` 裡面就有 flag ```bash! find . -name "uber-secret.txt" ``` ~~但其實用上一題的 grep 就可以解了~~ --- ### ASCII Numbers > MEDIUM 把題目的字串丟到 [CyberChef 的 From Hex](https://gchq.github.io/CyberChef/#recipe=From_Hex('Auto')) 就可以了