お正月にCTFを作って友達や親戚、先生などに配ったものを、4/9である今日に(?)、(少し変えて)Qiitaにポストします。
低レイヤの勉強にもなると思いますので、解いていただければ幸いです。
問題です!の前に、機械語を簡単に解説します。
よく見るように、機械語は0と1から出来ています。
例えば8ビット固定長のCPUを考えてみます。
ここでは、レジスタ(intの変数のような)は16個です。
この記事ではint8だとします。
「インストラクション」(コマンドのようなもの)は以下のようになっているとします(スペースは見やすさのために書いています):
0000 xxxx:レジスタxxxx(2進法でレジスタの番号0000から1111までの数が入る)をインクリメント
0001 xxxx:レジスタxxxxの中身が0だったらexit
0010 xxxx:xxxxに二進法で書いてある絶対アドレスまでジャンプ(goto)(ビット単位、例えば0001だと最初のインストラクションの途中にジャンプする)
これを使えば、256回ループしてexitするプログラムなどが書けます(int8がオーバーフローして0に戻って来る):
000000000001000000100000
(0000 0000 0001 0000 0010 0000)
問題です!
Webサービスを想像してください。
このサービスは、機械語を送ると実行してくれます。
このサービスで使われているマシンは変わっています。
このマシンは、16個の8ビットレジスタを持つ8ビット固定長のCPUです。
インストラクション:
0000 xxxx:レジスタxxxxをインクリメント(+= 1)
0001 xxxx:レジスタxxxxの中身が0だったらexit
0010 xxxx:xxxxに二進法で書いてある絶対アドレスまでジャンプ(goto)(ビット単位、例えば0001だと最初のインストラクションの途中にジャンプする)
0011 0000:秘密のメッセージを送信(下位4ビットが0000でなければエラー)
0100 0000:「🧶」を送信(下位4ビットが0000でなければエラー)
このサービスから秘密のメッセージを取得するのはこのままなら簡単です。00110000
ですが、このサービスは送った機械語を実行する前に以下のコードでスキャンします:
def scan(x: List[bool]) -> bool:
# xは機械語です(boolでビットを表しています)。
# 安全と判断すればTrue、危険だと判断すればFalseを返します。
for i in range(0, len(x), 8):
f, t = False, True
if x[i:i+8] == [f, f, t, t, f, f, f, f]:
return False
return True
どのような機械語を入力すれば、秘密のメッセージを送信してもらえるでしょうか?
答えは64個の「🧶」のあとで!(スクロールしないと見えなくするために)
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
🧶
答えは、
0010 0101 0 0100 0000
です!
アラインされていないところへジャンプすることで、スキャンをかいくぐり、実行するという仕掛けでした!
この問題は、NaCl(NACL • x86で信頼できないプログラムを安全に実行するためのシステム • Native Client: A sandbox for Portable, Untrusted x86 Native Code)がコンセプトになっています。
お楽しみいただけたらうれしいです!