- Source: sknbCTF 2025
- Author: claustra01
シンプルなpyjail問。print(flag)かそれに近いプログラムを実行すれば良いが、英数字禁止・8文字以下という制約がある。
#!/usr/bin/env python3
import string
from collections.abc import __builtins__
flag = "sknb{dummy}"
title = """
_ _ _ __ _ _ _
_ __ _ __(_)_ __ | |_ __ _ ___ | |/ _| ___| |__ __ _| | | ___ _ __ __ _ ___
| '_ \\| '__| | '_ \\| __/ _` |/ _ \\| | |_ / __| '_ \\ / _` | | |/ _ \\ '_ \\ / _` |/ _ \\
| |_) | | | | | | | || (_| | (_) | | _| | (__| | | | (_| | | | __/ | | | (_| | __/
| .__/|_| |_|_| |_|\\__\\__, |\\___/|_|_| \\___|_| |_|\__,_|_|_|\\___|_| |_|\\__, |\\___|
|_| |___/ |___/
"""
print(title)
line = input(">>> ")
for c in line:
if c in string.ascii_letters + string.digits:
print("Invalid character")
exit(0)
if len(line) > 8:
print("Too long")
exit(0)
bi = __builtins__
del bi["help"]
try:
eval(line, {"__builtins__": bi}, locals())
except Exception:
pass
except:
raise Exception()
pythonでは、eval()で文字列を実行する時にUnicord NFKCによって正規化されるという仕様がある。例えばprintはprintに変換されるので、これを用いて英数字のフィルタをバイパスすることができる。
また、pythonでは例外が発生したときにトレースログが出力されるので、例えば{}[flag]のような入力ができればKeyErrorが発生し、そのログ中にflagの内容を含ませることができる。しかし、この問題ではException発生時は何も出力されないようになっており、これを用いるにはBaseExceptionを発生させる必要がある。
結論から言うと、ここではexit()を用いれば良い。これはSystemExitを発生させ、SystemExitはBaseExceptionに含まれるがExceptionには含まれない。これでexit(flag)という10文字のpayloadを作ることが出来たが、この問題ではさらにあと2文字削る必要がある。
ここで話は変わるが、pythonのUnicord NFKC正規化には合字(ligature)を分割するという仕様がある。例えばflという合字はflに変換される。そのため、ⅺとflを用いることで2文字減らすことができ、8文字のpayloadを作ることができる。
最終的なpayloadはこうなる。eⅺt(flag)
これを入力するとflagが得られた。
sknb{3v4l_1s_fr33_but_fr33d0m_1sn't}