今天的題目是:
流程跟之前幾題很像:
讀程式找候選密碼 → 用程式內相同的雜湊方式處理候選 → 找出跟提供的 hash 檔相同的那個密碼 → 用該密碼對被 XOR 加密的 flag 檔解密。
解題步驟與思路
一、先把題目檔案下載到 webshell:
wget https://artifacts.picoctf.net/c/19/level4.py
wget https://artifacts.picoctf.net/c/19/level4.flag.txt.enc
wget https://artifacts.picoctf.net/c/19/level4.hash.bin
ls -1
二、檢視 level4.py:
level4.py 大致包含三個重要部分(我把它拆成可理解的段落):
flag_enc = open('level4.flag.txt.enc', 'rb').read()
correct_pw_hash = open('level4.hash.bin', 'rb').read()
rb 表示以 binary 形式讀入(雜湊檔與被加密的檔通常是二進位)。
correct_pw_hash 就是題目提供的「目標 hash bytes」。
def str_xor(secret, key):
# extend key to secret length
new_key = key
i = 0
while len(new_key) < len(secret):
new_key = new_key + key[i]
i = (i + 1) % len(key)
return "".join([chr(ord(a) ^ ord(b)) for a,b in zip(secret,new_key)])
把 key 的字元循環延伸(repeat)直到跟 secret(密文)一樣長。
逐字元用 ord() 取得 ASCII code,做 XOR(^),再用 chr() 把結果轉回字元。
這就是簡單「流式 XOR」的實作:cipher = plain XOR key,反過來 plain = cipher XOR key 也成立。
import hashlib
def hash_pw(pw_str):
pw_bytes = bytearray()
pw_bytes.extend(pw_str.encode())
m = hashlib.md5()
m.update(pw_bytes)
return m.digest()
這題使用的是 MD5(hashlib.md5())。
m.digest() 回傳的是 raw bytes(16 bytes for MD5),剛好能和 level4.hash.bin 直接比對。
pos_pw_list = ["...","...","..."] # 100 個候選
但我不用暴力全部爆破,而是依程式提供的候選做 hash 比對。
三、比對 hash,找出正確密碼:
我直接把候選自動帶入,並嘗試常見演算法(本題最後是 MD5 命中)。
下面是一段我在 webshell 執行的 Python 程式:
python3 - <<'PY'
import hashlib, re
src = open('level4.py','r',encoding='utf-8').read()
raw = re.search(r'pos_pw_list\s*=\s*\[([^\]]*)\]', src, re.S).group(1)
cands = re.findall(r"""['"]([^'"]+)['"]""", raw)
target = open('level4.hash.bin','rb').read()
for pw in cands:
if hashlib.md5(pw.encode()).digest() == target:
print("FOUND:", pw)
break
PY
系統輸出:程式會直接告訴你是哪一個候選的 MD5 與 level4.hash.bin 相符。
四、用找到的密碼執行 XOR 還原 flag:
找到密碼後,我把它當 key 用 str_xor 來解 level4.flag.txt.enc:
python3 - <<'PY'
def str_xor(secret, key):
new_key = key
i = 0
while len(new_key) < len(secret):
new_key = new_key + key[i]
i = (i + 1) % len(key)
return "".join(chr(ord(a)^ord(b)) for a,b in zip(secret,new_key))
enc = open('level4.flag.txt.enc','rb').read()
#若 decode() 失敗,可用 latin-1 fallback
try:
enc_s = enc.decode()
except:
enc_s = enc.decode('latin-1')
pw = "<the_correct_password>" # 把這裡換成上一步找到的密碼
print(str_xor(enc_s, pw))
PY
執行結果就是明文 flag:picoCTF{fl45h_5pr1ng1ng_ae0fb77c}
小心得
「先讀程式」往往勝過「盲目爆破」——很多題目把關鍵線索藏在附檔裡。學會用 Python 快速自動化(把候選丟進 hash 函式),可以省很多時間。
理解基本概念:chr()/ord()、encode()/decode()、XOR、hash digest 長度(MD5=16B、SHA1=20B、SHA256=32B)會很常用。
每題解完把步驟記下,久了你會有自己的「題型速查表」,遇到類似題目就能迅速上手。💪💪