LoginSignup
65
57

More than 5 years have passed since last update.

初心者が雑に紹介するCTF ~bin編~

Last updated at Posted at 2017-03-08

1. はじめに

 前回の投稿からかなり間が空いてしまいましたが、今回もかなり雑にCTFを紹介しようかと思います。今回はbin編ということで、文字通りバイナリやらアセンブリやらデバッガのお話になります。LinuxやWindowsにおけるバイナリが大体どんな感じのものかをわかって頂ければなーと思います。

2. 理論

 今回、基本的なアセンブリの解説はしません。非常にわかりやすく解説してるサイト、書籍は腐るほど有りますので…
 逆アセンブルの結果を見ていく上で、あまりマニアックな命令を覚えても仕方ないので基本的によく見るアセンブリ命令(10個程度)がわかっていれば簡単なCTFの問題を解く上では問題ないと思います。(個人的なオススメは某ハリネズミ本ですが、これにもう少し詳しいバイナリに関する本が一冊あれば良いんじゃないかと思います)

3. 実際にやってみよう

 それではまず簡単に、バイナリファイルを直接書き換えることでプログラムに自分の思い通りの動作をさせてみましょう。
 ~環境~
ubuntu 16.04 x86

bin.c
#include<stdio.h>
#include<string.h>

int main(){

    char input[50];
    char answer[50] = "binbinbin";
    char flag[50] = "FLAG{CTF_binary}";


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

        if(strcmp(input,answer)==0){
            printf("%s\n", flag);
            break;
        }
        else
            printf("Wrong answer...\n");
    }while(1);

    return 0;
}

 はい。内容は簡単で「秘密のパスワード『binbinbin』を入力するとflagが手に入るよ」というものです。
・パスワード平文で書くなよ…
・これ頑張ってデバッガとかコマンド使えばバイナリいじらなくてもパスかflag手に入らない?
 というツッコミは無しで…今回はあくまで簡略化のために雑コードで御座います。まあいつも雑なんですけど…

 というわけでこいつをコンパイルして(名前をbeforeとします)、objdumpで中身を見てみましょう。

gcc -o before bin.c

 CTFではこのようにバイナリファイルだけをポンと渡されて「これ解析してflag奪取してね~」と言われる事が多いです。勿論そのファイルにデバッグ情報などは付与されておらず、元のソースコードは不明なままです。
非常に簡単な問題ならstringsで答えがわかってしまうのですが、今回はどうもそうは行かなさそうです。

shir0@shir0:~/src$ objdump -d -M intel before

~省略~

0804851b <main>:
 804851b:   8d 4c 24 04             lea    ecx,[esp+0x4]
 804851f:   83 e4 f0                and    esp,0xfffffff0
 8048522:   ff 71 fc                push   DWORD PTR [ecx-0x4]
 8048525:   55                      push   ebp
 8048526:   89 e5                   mov    ebp,esp
 8048528:   57                      push   edi
 8048529:   53                      push   ebx
 804852a:   51                      push   ecx
 804852b:   81 ec ac 00 00 00       sub    esp,0xac
 8048531:   65 a1 14 00 00 00       mov    eax,gs:0x14
 8048537:   89 45 e4                mov    DWORD PTR [ebp-0x1c],eax
 804853a:   31 c0                   xor    eax,eax
 804853c:   c7 45 80 62 69 6e 62    mov    DWORD PTR [ebp-0x80],0x626e6962
 8048543:   c7 45 84 69 6e 62 69    mov    DWORD PTR [ebp-0x7c],0x69626e69
 804854a:   c7 45 88 6e 00 00 00    mov    DWORD PTR [ebp-0x78],0x6e
 8048551:   8d 55 8c                lea    edx,[ebp-0x74]
 8048554:   b8 00 00 00 00          mov    eax,0x0
 8048559:   b9 09 00 00 00          mov    ecx,0x9
 804855e:   89 d7                   mov    edi,edx
 8048560:   f3 ab                   rep stos DWORD PTR es:[edi],eax
 8048562:   89 fa                   mov    edx,edi
 8048564:   66 89 02                mov    WORD PTR [edx],ax
 8048567:   83 c2 02                add    edx,0x2
 804856a:   c7 45 b2 46 4c 41 47    mov    DWORD PTR [ebp-0x4e],0x47414c46
 8048571:   c7 45 b6 7b 43 54 46    mov    DWORD PTR [ebp-0x4a],0x4654437b
 8048578:   c7 45 ba 5f 62 69 6e    mov    DWORD PTR [ebp-0x46],0x6e69625f
 804857f:   c7 45 be 61 72 79 7d    mov    DWORD PTR [ebp-0x42],0x7d797261
 8048586:   c7 45 c2 00 00 00 00    mov    DWORD PTR [ebp-0x3e],0x0
 804858d:   8d 45 c6                lea    eax,[ebp-0x3a]
 8048590:   b9 1e 00 00 00          mov    ecx,0x1e
 8048595:   bb 00 00 00 00          mov    ebx,0x0
 804859a:   89 18                   mov    DWORD PTR [eax],ebx
 804859c:   89 5c 08 fc             mov    DWORD PTR [eax+ecx*1-0x4],ebx
 80485a0:   8d 50 04                lea    edx,[eax+0x4]
 80485a3:   83 e2 fc                and    edx,0xfffffffc
 80485a6:   29 d0                   sub    eax,edx
 80485a8:   01 c1                   add    ecx,eax
 80485aa:   83 e1 fc                and    ecx,0xfffffffc
 80485ad:   83 e1 fc                and    ecx,0xfffffffc
 80485b0:   b8 00 00 00 00          mov    eax,0x0
 80485b5:   89 1c 02                mov    DWORD PTR [edx+eax*1],ebx
 80485b8:   83 c0 04                add    eax,0x4
 80485bb:   39 c8                   cmp    eax,ecx
 80485bd:   72 f6                   jb     80485b5 <main+0x9a>
 80485bf:   01 c2                   add    edx,eax
 80485c1:   83 ec 0c                sub    esp,0xc
 80485c4:   68 d0 86 04 08          push   0x80486d0
 80485c9:   e8 f2 fd ff ff          call   80483c0 <printf@plt>
 80485ce:   83 c4 10                add    esp,0x10
 80485d1:   83 ec 08                sub    esp,0x8
 80485d4:   8d 85 4e ff ff ff       lea    eax,[ebp-0xb2]
 80485da:   50                      push   eax
 80485db:   68 d7 86 04 08          push   0x80486d7
 80485e0:   e8 1b fe ff ff          call   8048400 <__isoc99_scanf@plt>
 80485e5:   83 c4 10                add    esp,0x10
 80485e8:   83 ec 08                sub    esp,0x8
 80485eb:   8d 45 80                lea    eax,[ebp-0x80]
 80485ee:   50                      push   eax
 80485ef:   8d 85 4e ff ff ff       lea    eax,[ebp-0xb2]
 80485f5:   50                      push   eax
 80485f6:   e8 b5 fd ff ff          call   80483b0 <strcmp@plt> ///strcmpの呼び出し///
 80485fb:   83 c4 10                add    esp,0x10
 80485fe:   85 c0                   test   eax,eax
 8048600:   75 23                   jne    8048625 <main+0x10a> ///怪しいところ///
 8048602:   83 ec 0c                sub    esp,0xc
 8048605:   8d 45 b2                lea    eax,[ebp-0x4e]
 8048608:   50                      push   eax
 8048609:   e8 d2 fd ff ff          call   80483e0 <puts@plt>
 804860e:   83 c4 10                add    esp,0x10
 8048611:   90                      nop
 8048612:   b8 00 00 00 00          mov    eax,0x0
 8048617:   8b 7d e4                mov    edi,DWORD PTR [ebp-0x1c]
 804861a:   65 33 3d 14 00 00 00    xor    edi,DWORD PTR gs:0x14
 8048621:   74 19                   je     804863c <main+0x121>
 8048623:   eb 12                   jmp    8048637 <main+0x11c>
 8048625:   83 ec 0c                sub    esp,0xc
 8048628:   68 da 86 04 08          push   0x80486da
 804862d:   e8 ae fd ff ff          call   80483e0 <puts@plt>
 8048632:   83 c4 10                add    esp,0x10
 8048635:   eb 8a                   jmp    80485c1 <main+0xa6>
 8048637:   e8 94 fd ff ff          call   80483d0 <__stack_chk_fail@plt>
 804863c:   8d 65 f4                lea    esp,[ebp-0xc]
 804863f:   59                      pop    ecx
 8048640:   5b                      pop    ebx
 8048641:   5f                      pop    edi
 8048642:   5d                      pop    ebp
 8048643:   8d 61 fc                lea    esp,[ecx-0x4]
 8048646:   c3                      ret    
 8048647:   66 90                   xchg   ax,ax
 8048649:   66 90                   xchg   ax,ax
 804864b:   66 90                   xchg   ax,ax
 804864d:   66 90                   xchg   ax,ax
 804864f:   90                      nop

~省略~

 objdumpを使ってmain関数の逆アセンブル結果を表示しています。
この結果から、怪しい分岐命令に当たりをつけていきます。
80485f6でstrcmpを呼び出していますね。その後80485feでtest eax eaxを経て、8048600でstrcmpを呼び出してから初めてのjne(分岐命令)が登場します。そこで、jne命令のオペコード75 23をnop命令(何もしない、という命令)のオペコード2つ、90 90で上書きしてみましょう。すると、処理は次の8048602に移るわけですが、このまま流れを追っていくとどうやらプログラムは正常に終了しそうですね。このプログラムの場合、正解のパスを入力するかCtrl-Cで終了するかをしない限り、whileループによって半永久的にパスの入力を求めます。なので、正常に終了している⇢パスが正解の場合に分岐している と考えられます。

さて、それでは実際にバイナリエディタを使ってファイルを編集してみましょう。(編集後のバイナリファイルの名前をafterとします)今回はGHexを利用していますが、他に使い慣れたバイナリエディタがあればそちらでも構いません。
x86-LinuxにおけるPT_LOAD Segmentはデフォルトで0x0804000から開始されるので(リンカ自作とかをすれば変更は可能ですが)、jne命令のあるアドレス0x08048600との差を取って、GHexでオフセットが600のあたりを調べると…

Screenshot from 2017-03-03 20-12-36.png
 編集前のファイル(before)

 jne命令のオペコード 75 23がありましたね。こいつをnop命令×2に書き換えてあげましょう。

Screenshot from 2017-03-03 20-13-12.png
 編集後のファイル(after)

 それでは編集前後のバイナリファイルを実行してみましょう。

shir0@shir0:~/src$ ./before
input:aa
Wrong answer...
input:bb
Wrong answer...
input:binbinbin
FLAG{CTF_binary}
shir0@shir0:~/src$ ./after
input:aa
FLAG{CTF_binary}
shir0@shir0:~/src$ ./after
input:bb
FLAG{CTF_binary}
shir0@shir0:~/src$ ./after
input:binbinbin
FLAG{CTF_binary}

 beforeが正解を入力しないとflagを表示しないのに対して、afterではどんなパスを入力してもflagが獲得できている様子が確認できますね。

 非常に適当な解説でしたが、バイナリ編集による制御の変更って大体こんな流れなんだなあ 程度に思って頂ければ幸いです。通常レベルになると、まずsshで指定されたサーバーに接続⇢あるディレクトリにある編集等ができないバイナリの脆弱性を調査・攻撃する⇢flagを手に入れる という流れのpwn問題も出てきたりするので、自分的にもまだまだ先の道のりは長い…と思わざるを得ませんね。

4. 実践

 
 それでは、実際にCTFで出題された問題を解いてみましょう。

 Anti-Debugging

Reverse it.
bin   (←バイナリファイルのダウンロードリンク セキュリティソフトに引っかかるので今回はリンク先を貼っていません)
may some AV will alert,but no problem.

~SECCON CTF 2016 オンライン予選より "Anti-Debugging"~

 少々わかりづらいですが、謎のバイナリファイルが渡され、こいつを解析しろ!という問題みたいですね。bin問題というよりrev問題のような気もしますが…

 とりあえず先程の環境で、このファイルの正体を探ってみましょう。

shir0@shir0:~/ctf$ file bin
bin: PE32 executable (console) Intel 80386, for MS Windows

 32bit-Windowsのバイナリみたいですね。ここから環境をWindows 10 Homeに移します。
とりあえず名前に.exeを付け加えて端末で実行してみましょう。

Input password>

 うーん パスワードの入力を求められて、適当に入力してみてもプログラムが終了してしまいますね。

 そこで今回はIDAと呼ばれる逆アセンブラのデモ版を利用してみましょう。
ida.png

 画像はIDAで問題のバイナリファイルを開いたところです。
 このツールはプログラムの流れや分岐命令をツリー表示でわかりやすく示してくれる優れものなので、バイナリ解析の際に手元にあると大分心強いですね。

 それでは、各命令群の終わりにある分岐命令にブレークポイントを置きながら、流れを追っていきましょう。eipが分岐命令のブレークポイントに達すると、画像では分かりづらいですが矢印が点滅して次の分岐先を示してくれるので、それが正解の流れかどうかを検証しながら先に進んでいきます。

まず上の画像では、最初のパスワードの入力受付とその合否を判断して、分岐しています。分岐命令をクリックして、Hex-view(バイナリの16進数ダンプ)タブに切り替えると、該当命令に当たる部分をハイライトしてくれます。(黄色くハイライトされているjne命令が、Hex-viewタブでは濃い緑色でハイライトされています)

bef.png

jj.png

 IDA Demo版にはバイナリの編集機能がついてないので、IDAで該当部分のアドレスをチェックしながら、適宜stirlings等のバイナリエディタで編集を行います。

 次の命令群との整合性を考えると、ここでパスワードを間違えると16進数ダンプしたときの順番通りの流れから外れ、プログラムは終了してしまいます。なので、先程と同様に分岐命令(jnz)をnop命令に上書きしてしまいましょう。そうすれば、パスワードの合否に関わらず、プログラムは正解の場合の分岐に向かうはずです。

 しかし、ここからが問題です。
DEBUG.png

 タイトルからだいたい予想はできていましたが、IsDebuggerPresent()等のデバッガ検知が見受けられます。これらは、プログラムがデバッガ上で動いているかどうかを検出してくれるapi関数です。今回の場合、プログラムがデバッガ上で動いていることが検知されると、その時点で終了する命令群に分岐する仕組みになっているようですね。

 もう一つ厄介な点は、今度は16進数ダンプした時の順番通りではなく、分岐命令でジャンプしている先が正解のルートだということです。つまり、これらの命令群の最後の分岐命令をnop命令に書き換えても、無条件に不正解のルートに送られるだけ、ということです。そしてやはりどの分岐でも不正解ルートに行くことが実際に動かしてみるとわかります。(ちなみに、このプログラムはデバッガ外で最初のパスワードを正しく入力しても、「Your password is correct!」と表示され、終了してしまいます。)

そこで、もう少し分岐命令について踏み込んでみましょう。
今回分岐に使われている命令はjz命令、又はjnz命令ですね。前半での説明は省いていましたがこれらの命令は

jz ... (Jump if Zero命令:ゼロフラグ(ZF)が1のときに、指定先に分岐する)
jnz ... (Jump if Not Zero命令:ゼロフラグ(ZF)が0のときに、指定先に分岐する)

という意味の命令なのです。しかし、このゼロフラグとはどういうものなのでしょうか?

 今までのアセンブリコードを見返してみると、分岐命令の前に、よくtest,cmpといった命令があるのがお分かりでしょうか?これらの命令は2つの引数をとり、それら2つを比較し、結果によってフラグレジスタという部分の値を変化させます。(厳密に言うとtestはオペランド2つの論理積を取り、cmpはsub命令と同じように減算を行います。両方共結果はオペランドには格納されず、フラグレジスタの値を変更させるに留まります)そのフラグレジスタの一種として、ゼロフラグと呼ばれるものが存在し、後置されたjz、jnzの各分岐命令はその値を読み取り分岐することで、アセンブリコード上で条件分岐を成り立たせている、ということです。

 お気づきかとは思いますが、これらの分岐命令は全く逆の意味の命令ですね。そして画像から、当分は分岐命令でジャンプしている先が正解ルートの連続だと考えられるため、「jz命令はjnz命令に、jnz命令はjz命令に」書き換えてあげれば、デバッガ検知による条件分岐は全て裏目に出て、書き換える前とは常に逆の分岐をしていくことになります。(もちろんjmp命令のような無条件分岐命令に書き換えるのも有りかもしれませんが)

このように、この先も命令群の最後の分岐命令にブレークポイントを置きつつ、分岐の方向を確かめ、それぞれの場合によって「nopによる命令の上書き」「jnz jz命令の入れ替え」をIDA(+stirlings)で地道に行っていくと…

success.png

flagが表示されましたね。というわけで今回のflagは
「SECCON{check_Ascii85}」
でした。

 実は、この問題。抜け道があります。高機能なGUIデバッガの使い方を知っていることで、手間を省略できるのです。
使用するのは「Immunity Debugger」です。

 まずは先程と同じように、IDAで分岐の流れを追っていき、大量のデバッガ検知の網の先にある、ある命令群に目をつけます。

seisei.png

 他の命令群と比べて、比較的長いですね。それにIDAがコメントで謎の文字列を扱っていることを示しています。ここでflagの生成をしてるのかな?と当たりを付けてこの命令群の開始アドレス(画像だと見えていませんが、00401663)を記録しておきます。次に、それまでの流れとはちょっと変わったものを呼び出している箇所をその分岐先に見つけます。

messa.png

 赤文字の部分、MessageBoxA を呼び出していますね。これは一体何でしょうか?(すっとぼけ)
 はい。これが先程flagを表示するために使われたダイアログボックスの正体ですね。こいつのアドレス(00401737)も控えておき、Immunity Debuggerでファイルを開いてみましょう。

immu.png

 そして先程のflag生成箇所と思われる部分の先頭アドレスを探し、右クリックして「New Origin here」を選択します。これでプログラムをこのアドレスから実行することができます。

 後はもう一つのMessageBoxの命令の近辺のアドレスにブレークポイントを置いて、実行してみると

mesim.png

 flagが表示されましたね。(2回目)

 flag生成とそれを表示するための処理の間にデバッガ検知が存在しない、ということに気づくと、このようにflagを得るために必要な部分を切り抜いて実行して、案外あっさりと解いてしまうことが可能というわけです。

5.終わりに

 如何でしたでしょうか?
 今回はbin編ということで、ローカルな環境でバイナリファイルを編集・解析することでflagを奪取しました。もう少しアセンブリやツールの使い方について掘り下げたかったのですが、如何せんキリがないので大分雑な紹介になってしまいました。そんな中でも、大体の流れから、バイナリの編集・解析によってプログラムに自分の思い通りの動作をさせる技術とその楽しさを知って頂ければ幸いです。

 また、悪い人たちは今回扱ったような知識(ここまで簡単ではありませんが)を悪用してゲームのチートや海賊版などを作成したりしているわけです。(勿論今回の内容は悪用厳禁です)いくら優秀なパッカーや難読化を施しても、IDAのような高機能な逆アセンブラや、優秀な人達の前では絶対安全な対策をするのは難しいのが現実です。しかし、そういった人達の目線に立った時、「~をされたら解析しづらいなあ」「こういうことをされると面倒だなあ」ということを知っているのと知っていないのとでは大きな違いがあります。そういったことを学ぶのがCTFの目的である、と私はどっかで読んだ記憶があります。(適当)実際私の周りにもゲームを作っている方が多いので、今度はそういった解析からの防衛技術なんかについても何処かで取り上げたいなあと思っています。全ては私のこれからの勉強の進捗によりますが…

 まあいつも通り雑な解説でしたが、ここまでお付き合い頂き、有難うございました。間違っている点・意見等御座いましたらコメント等でご指摘頂けると幸いです。

65
57
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
65
57