daiki0508'足跡

Ghidraを使ってCTFの問題を解いてみる

前回の記事でGhidraのインストールと基本操作を学習したので、今回はGhidraを使って実際にCTFの問題を解いてみましょう。
まだ、前回の記事を見ていない人は下記のリンクからどうぞ!

daiki0508.hatenablog.com

問題紹介

今回解析した問題はSECCON_CTF_Beginner'sのmaskという問題です。
問題ファイルは下記のサイトにあるのでダウンロードしてみてください。

github.com

実際に解いてみる

1. fileコマンドでmaskファイルの形式を調べる
するとx86/x64のELF形式のファイルだと分かる。よって、Ghidraでファイルの中身を見てみることにする。

2. Ghidraで解析作業をする
maskファイルをGhidraで読み込んで解析すると

undefined8 main(int param_1,long param_2)

{
  int input_len;
  size_t input_len;
  long in_FS_OFFSET;
  int (long)idx;
  byte input_arg [64];
  byte param1 [64];
  byte param2 [72];
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  if (param_1 == 1) {
    puts("Usage: ./mask [FLAG]");
  }
  else {
    strcpy((char *)input_arg,*(char **)(param_2 + 8));
    input_len = strlen((char *)input_arg);
    input_len = (int)sVar2;
    puts("Putting on masks...");
    (long)idx = 0;
    while ((long)idx < iVar1) {
      param1[(long)idx] = input_arg[(long)idx] & 0x75;
      param2[(long)idx] = input_arg[(long)idx] & 0xeb;
      (long)idx = (long)idx + 1;
    }
    param1[input_len] = 0;
    param2[input_len] = 0;
    puts((char *)param1);
    puts((char *)param2);
    input_len = strcmp((char *)param1,"atd4`qdedtUpetepqeUdaaeUeaqau");
    if ((input_len == 0) && (input_len = strcmp((char *)param2,"c`b bk`kj`KbababcaKbacaKiacki"), input_len == 0)
       ) {
      puts("Correct! Submit your FLAG.");
    }
    else {
      puts("Wrong FLAG. Try again.");
    }
  }
  if (local_10 == *(long *)(in_FS_OFFSET + 0x28)) {
    return 0;
  }
                    /* WARNING: Subroutine does not return */
  __stack_chk_fail();
}
すると上のようなソースコードが「decompiler」ウィンドウに表示されると思います。
(上のコードは分かりやすいように若干書き換えてあります)

3. maskファイルがどのようなプログラムなのか考える
まず、先ほどのGhidraで解析したソースコードの以下の部分に注目してください。

param1[(long)idx] = input_arg[(long)idx] & 0x75;
param2[(long)idx] = input_arg[(long)idx] & 0xeb;

上記よりparam1には入力した任意の文字列と0x75との論理積が代入され、
param2の値には入力した任意の文字列と0xebとの論理積が代入される。

また、下記のコードより
 input_len = strcmp((char *)param1,"atd4`qdedtUpetepqeUdaaeUeaqau");
    if ((input_len == 0) && (input_len = strcmp((char *)param2,"c`b bk`kj`KbababcaKbacaKiacki"), input_len == 0) 
) {
      puts("Correct! Submit your FLAG.");
}

param1が『atd4`qdedtUpetepqeUdaaeUeaqau』
param2が『c`b bk`kj`KbababcaKbacaKiacki』になるのであれば、それがflagと言える。

4. 解析コードを書いてみる
先ほど考えたmaskのアルゴリズムからflagを逆算するコードをpythonで書いてみる。
また、flag文字列はparam1かparam2のどちらかになればいい(論理和)のでORを求めるのが良いと思う。

  # -*- coding: utf-8 -*-
param1 = 'atd4`qdedtUpetepqeUdaaeUeaqau'
param2 = 'c`b bk`kj`KbababcaKbacaKiacki'
ans = ''
for i,j in zip(param1, param2):
    ans += chr(ord(i) | ord(j))    # ordでASCIを取得、chrでASCIを文字列にする

print(ans)


これを実行することで以下のflagを得ることが出来た。
ctf4b{dont_reverse_face_mask}