Binary(Linux)2 for CTF

 CTF用のBinary問題として「デバッグ環境を検知するプログラム」を作成する。マルウェアによってはこのようにデバック環境を検知した場合にプログラムの挙動を変えるものがあるため、そのようなマルウェアをどのように解析するかを問う問題とする。

■プログラムの流れ
1.入力を求めるプロンプトを表示
2.答えが合わないとNot Correctと表示
3.正しい答えの場合でも、プログラムがTrace(デバッグ環境にある)されていることを
  検知した場合エラーを返す

■プログラムソースコード

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/ptrace.h>

int main(){

    char input[50];
    char output[50];
    int i=0;

    printf("input:");
    scanf("%s", input);

    /* password is "1234pass" */

    if(strcmp( crypt(input,"$1$seed"),"$1$seed$9Pv9DYRW3TuJN8QETcvoE1")==0){
       if(ptrace(PTRACE_TRACEME, 0, 1, 0) == -1) {
           printf("Sorry..because inside gdb..\n");
           return 1;
       }
        strncpy(output, crypt("CTF","$1$seed")+9,4);
        printf("Flag is %s\n", output);
    }
    else
        printf("Not Correct.\n");

    return 0;
}

■プログラム解析方法
 gdbを用いて解析する場合、正しい入力を何らかの方法(例えばstrcmp関数に渡す引数を書き換える)で得たとしても、gdb上で実行している場合は、その次に待ち受けるptrace関数によりgdbでのデバッグ環境であることを検知され「Sorry..because inside gdb..」と表示されFlagを得ることができない。
 Flagを得るには正しい入力のルートに導いたあと、ptrace関数の結果を無効にする操作が必要になる。具体的には以下の操作をgdbで行う。
・ptrace関数をBreakポイントに設定
・finishコマンドでptrace関数を抜けた状態でStopする
・この時点でptace関数の結果がraxレジスタに格納されている。
 ※gdb環境だとその値は「-1」である。
・次の命令はcmp命令でraxレジスタの値が「-1」かどうか比較されるため、その前に
 raxレジスタをデバック状態ではない「0」にする。(「set $rax=0」を実行する。)
・Continueで正しいFlagを得ることができる。

gdb上でプログラムを実行した画面。正しい入力でもFlagは表示されない。

gdbのdisasコマンドでmain関数のアセンブラを確認すると「ptrace関数」の結果が格納される「raxレジスタ」の値がcmp命令で比較されていることがわかる。

再びgdbでBreakポイントをptrace関数にセットし、その結果がraxレジスタに格納された後に「set $rax=0」で結果を書き換え、実行を継続すると正しいFlagを得ることができる。

コメントをどうぞ