pyjail wiki

jailCTF 2025 - auniquechallenge

Challenge

各識別子の各文字は1回のみ使用可能。

import re
from collections import Counter
code = input()
# 各識別子を抽出
identifiers = re.findall(r'[a-zA-Z_][a-zA-Z0-9_]*', code)
# 各文字が1回のみかチェック
for ident in identifiers:
counter = Counter(ident.lower())
for char, count in counter.items():
if count > 1:
print(f"Character '{char}' appears {count} times in '{ident}'")
exit()
eval(code, {'__builtins__': {}})

Solution

NFKC 正規化による文字の等価性

Python の識別子は NFKC 正規化される。異なる Unicode 文字が同じ ASCII 文字として認識される:

# 全角 'a' と半角 'a' は同じ識別子として動作
= 1
print(a) # 1 が出力される

同じ文字の異なる表現

ASCII全角上付き下付きその他
a𝐚
e-𝐞
sˢ-𝐬

完全なペイロード

# __class__ の各文字を異なるUnicode表現で
__clasˢ__ # c, l, a(全角), s(全角), s(上付き)

Technical Details

NFKC 正規化

import unicodedata
# 正規化の確認
unicodedata.normalize('NFKC', 'a') # 'a'
unicodedata.normalize('NFKC', 'ᵃ') # 'a'
unicodedata.normalize('NFKC', '𝐚') # 'a'
# すべて同じ識別子として動作
= 1
print(a) # 1

利用可能な等価文字

# 'class' を重複なしで
'c' - 通常
'l' - 通常
'a' - 通常 または 'a' または 'ᵃ'
's' - 通常 または 's' または 'ˢ'
's' - (2つ目) 別の表現を使用

Python の識別子処理

# PEP 3131: Python での識別子処理
# 1. ソースコードを読む
# 2. NFKC 正規化を適用
# 3. 正規化後の文字列を識別子として使用

Alternative Solutions

別解1: 数学記号

# 数学太字、斜体なども NFKC 正規化される
𝐚𝐛𝐜 # abc として認識

別解2: 囲み文字

# 一部の囲み文字も正規化される

Flag

jail{...}

References

  • jailCTF 2025 Official
  • PEP 3131 - Supporting Non-ASCII Identifiers
  • Unicode NFKC Normalization