アイジア

CTF, 情報セキュリティの学んだことメモ

ksnctf 11 Riddle

ksnctfの11問目を解いていきます。リバースエンジニアリングの問題です。 ksnctf.sweetduet.info

実行と解析

f:id:favoritte15:20181115222445p:plain

Riddle.exeを実行すると、簡単な質問(答えは1問目「Human」,2問目「Greenland」, 3問目「42」)の後唐突にフラグを聞かれます。分からないのでIDAで開いて解析を行います。 f:id:favoritte15:20181115222719p:plain

exeファイルの挙動は、下のテキストにクイズの答えを入力すると「Correct!」、不正解だと「Wrong...」と表示されるというものでした。その文字列を出力する周辺に、正解文字列との比較を行っている処理がある可能性が高いので、まずはヒントになる文字列を探します。 f:id:favoritte15:20181115223039p:plain

右上のNames windowに「Wrong」が、すぐ上に「Correct」「Conglatulation」という文字列があります。ダブルクリックすると左のウィンドウが遷移します。 f:id:favoritte15:20181115223250p:plain

「Correct」も「Wrong...」も「Conglatulation」sub_401980というサブルーチンで使われているのでこれが正解文字列との判定処理だと思われます。sub_401980をダブルクリックして詳細を見ていきます。
f:id:favoritte15:20181119130348p:plain
実際に「Wrong...」とダイアログを表示する処理を行っているのは「loc_E1A7D」というルーチンであるようです。ここにジャンプする命令の直前の比較命令(cmp)を分析することでフラグが分かりそうです。
ここからは静的解析だけでは厳しいのでここから実際にプログラムを動かして解析していきます。
f:id:favoritte15:20181119131326p:plain
f:id:favoritte15:20181119131457p:plain
f:id:favoritte15:20181119131515p:plain
上の画像のようにE1A7Dにジャンプする命令にブレークポイントを仕掛けます(右クリック->[Add Breakpoint])。 仕掛けたら実際にプログラムを実行します。F9を押すとIDAがプログラムをロードし、もう一度F9を押すとRiddle.exeが実行されるので「What is the flag?」の問まで進めます。ここではとりあえず「FLAG_」と入力してブレークポイントに当たります。 f:id:favoritte15:20181119131918p:plain
最初のブレークポイントは0x15と入力文字列の数(EAX)を比べています。フラグの文字数は21文字であることが分かりました。
f:id:favoritte15:20181119133256p:plain
入力文字列が21文字になるように「FLAG_ABCDEFGHIJKLMNOP」と入力し、2番目のブレークポイントに当たります。直前の比較命令において、edxには「0D1BFFBF」が入っていますがこれが何の値かわかりません。
f:id:favoritte15:20181119134234p:plain
見た感じこのsub_E17D0というサブルーチンでこの値が入力文字列をもとにしてつくられているみたいです。

XOR命令を探せ

簡単なリバースエンジニアリングの問題においてこの手の変換処理はXORでやっていることが多いので、サブルーチンでそれを探します。ただし、
オペランドレジスタがどちらも同じ→そのレジスタの値を0にしているだけ
オペランドにスタックの位置を表すレジスタ(esp, ebp)が含まれている→サブルーチンの補正とかのための処理
なのでここでは無視します。そうして探していくと以下の命令が見つかりました。
f:id:favoritte15:20181119143232p:plain

xor [eax+esi], cl

ここのXOR命令、入力文字数分だけ繰り返されています。最高に怪しいポイント。くり返し実行してECXの値を見ていくと[eax+esi]のアドレスには順に0xF9, 0xB3,0x5A,0x4A,0x18,0xEA, 0x5D, 0x36, 0x99, 0xE2, 0x6, 0x3F, 0xA1, 0x20, 0x47, 0x1F, 0xF5, 0x86, 0xA3, 0xAC, 0x7が格納されます。

FLAG→0x46 0x4C 0x41 0x47
[eax+esi]→0xF9, 0xB3 ,0x5A ,0x4A
この値をそれぞれXORすると「0xBF,0xFF, 0x1B, 0x0D」になり先ほどのEDXの値(0D1BFFBF)と一致します。やはりXORで文字列が変換されているようです。
この場所で4文字ずつ比較を5回行い20文字分を照合しています。その後、

f:id:favoritte15:20181119153027p:plain
↑ここの部分で0xC7でXORした最後の文字と0xACを比較しています。 メカニズムが分かったのでここからプログラムを書いてフラグを求めます。

#include <stdio.h>

int main(){
    char a[22] = {0xF9, 0xB3, 0x5A, 0x4A, 0x18, 0xEA, 0x5D, 0x36, 0x99, 0xE2, 0x6, 0x3F, 0xA1, 0x20, 0x47, 0x1F, 0xF5,
    0x86, 0xA3, 0xAC, 0xC7};
    char b[22] = {0xBF, 0xFF, 0x1B, 0x0D, 0x47, 0xA7, 0x18, 0x4F, 0xCB, 0xD6, 0x5C, 0x59, 0x95, 0x61, 0x73, 0x57,0xB3,
    0xD1, 0x94, 0x9F, 0xAC};
    char flag[22];
    
    int i;
    for(i=0; i<21; i++){
        flag[i] = a[i]^b[i];
    }
    printf("%s\n", flag);   
}