WaniCTF 2023 Writeup

Reversingのfermatという問題のWriteupを書きます。

様子を見る

zipファイルを解凍し、fileコマンドでファイル形式を確認します。

fermat: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=163a577f34700eab526e54e030d57fe930b0aa76, for GNU/Linux 3.2.0, not stripped

ELFの実行ファイルであることがわかりました。
実行すると、Input a>と出てきたので適当にeを入力すると

Input b> Input c> (a, b, c) = (0, 0, 0)
Invalid value :(

このような文字が返ってきました。
stringsコマンドで表示可能な文字列を見てもフラグは無さそうです。

Ghidraで静的解析をする

続いてGhidraで静的解析を行います。

Symbol Treeを確認すると、checkという怪しそうな関数名がありました。
check関数の中身を確認すると

undefined8 check(uint param_1,uint param_2,uint param_3)

{
  undefined8 uVar1;
  
  if (((param_1 < 3) || (param_2 < 3)) || (param_3 < 3)) {
    uVar1 = 0;
  }
  else if (param_1 * param_1 * param_1 + param_2 * param_2 * param_2 == param_3 * param_3 * param_ 3)
  {
    uVar1 = 1;
  }
  else {
    uVar1 = 0;
  }
  return uVar1;
}

と書かれていました。どうやら、x3+y3=z3を満たす3以上の引数3つを入力するといいっぽいです。
しかし、フェルマーの最終定理によりこれを満たすx, y, zは存在しません。デバッガを利用するしかなさそうです。

GDBで動的解析をする

次はデバッガであるgdbを使って、動的解析をします。
gdb fermatgdbを立ち上げます。
check関数の動きを見たいので、b checkでbreak pointを設定します。
runで動かすと入力を求められるのでとりあえず、5を3回入力します。

Input a> 5
Input b> 5
Input c> 5
(a, b, c) = (5, 5, 5)

次にGhidraのcheck関数のアセンブラから対象のジャンプ命令の箇所を探します。 gdbに戻ってこの命令の箇所まで飛ばしましょう。 ここでinfo registersを入力してレジスタの状態を確認します。
eflagsにZFが立っていません。JZはZFが立っていないとジャンプしないため、ここでZFを立たせます。 ZFを立たせればよいので、適当にset $eflags=0x246を入力します。
もう一度レジスタの状態を確認すると
eflagsにZFが立っています。このまま進めてみましょう。
何回か進めると無事にフラグが出てきました。