ホーム » 2020 » 12月 » 18

日別アーカイブ: 2020年12月18日

2020年12月
 12345
6789101112
13141516171819
20212223242526
2728293031  

検索・リンク

CTF実験とobjdumpの使い方

CTF実験で、objdump を使う問題の解説。

機械語の知識が必要なCTF問題

use-the-strings の問題では、ダウンロードすると Linux 実行ファイルが得られる

$ file use-the-strings
use-the-strings: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV)...
$ sudo apt-get install binutils    # binutilsはインストール済みかも
$ objdump -d use-the-strings       # use-the-stringsを逆アセンブル

逆アセンブルにより出力されたプログラムの最も主要な部分を示す。

((2024/01/23追記))
サーバのOSのバージョンによっては、逆アセンブラ出力の関数の先頭に endbr64 という ”f3 0f 1e fa”の4byte命令が含まれているかもしれない。この命令が入っている場合は、この命令の分だけずれている。 endbr64 命令は、何らかの攻撃により不当な関数呼び出しがあった場合に強制的に終了させる命令らしい。

上記プログラムの機械語を、C言語っぽく書くならば、以下のようになるだろう。

    reg_eax = memory( 0x2fde+reg_rip ) ; // 大域変数flagを取り出す
    if ( reg_eax != 0 ) goto label_105d ;
    reg_eax = 0 ;
    return ;
label_105d:
    push( reg_rax ) ;
    reg_rdi = 0xf9f+reg_rip ; // 出力文字の保存場所
    puts() ;                  // 以下のputsを呼び出す。
    reg_eax = 0 ;
    pop( reg_rdx ) ;
    return ;

ちなみに、この use-the-strings の元プログラムは、以下のような内容である。
flag の変数に 0 以外の値が入っていれば、本来の出力処理が行われるはずだが、大域変数初期化にて通常であれば動かない。

このため printf を動かすには、以下の方法があるだろう。

  1. if 文(機械語ならば jne 命令の部分)を書き換えて、printf が実行されるようにする。
    • 0x1058番地の jne(0x75) を jmp(0xeb) に書き換える。
    • 0x1058番地からのjne,xor,retの5byteをNOP(0x90)に書き換える方法もある。
  2. if文を実行する前に、大域変数 flag を0以外の値に書き換える。
    • 今回は、この方法の説明として、Cのソースプログラムとそのデバッガ情報を持った use-the-strings-gdb を使った方法を説明する。(ただし、Cのソースが手に入ったのなら答えもバレているはずだが…)
    $ gdb use-the-strings-gdb 
    GNU gdb (Debian 10.1-1+b1) 10.1
    :
    (gdb) break main         # ブレークポイントを設定
    Breakpoint 1 at 0x1050: file use-the-strings.c, line 33.
    (gdb) run                # プログラムを動かす
    Starting program: /home/t-saitoh/public_html/ctf/use-the-strings-gdb 
    
    Breakpoint 1, main () at use-the-strings.c:33
    33	  if ( flag ) {
                             # main() の先頭で一時停止
    (gdb) set flag=1         # 変数 flag に 1 を代入
    (gdb) continue           # 続きを実行させる
    Continuing.
    FLAG{UseTheStrings}      # 答えが表示された。
    [Inferior 1 (process 767940) exited normally]
    (gdb) quit               # デバッガを抜ける
    

そもそももっと簡単な方法がある

機械語の知識に興味を持ってもらうために、あえて、上記のような説明をしたけど、本来はもっと簡単な手法がある。

strings コマンドは、ファイル中の英数字などで表示可能な文字部分を抽出して表示する。

$ strings use-the-strings
/lib64/ld-linux-x86-64.so.2
libc.so.6
puts
:
$ strings use-the-strings | grep FLAG  # FLAGという文字を含むのが解っていれば
FLAG{UseTheStrings}

strings コマンドを使えばこの問題は極めて簡単に解けるが、ではCTFの難易度を高めるにはどうすればいいだろうか?

一般的に、ウィルス対策ソフトは、プログラム中の特定の機械語が、既知のウィルスの機械語パターンと一致しているかどうかで、ウィルスが含まれているか検知する。つまり、strings でバレない対策を考える…ということは、ウィルス作者が「如何にウィルス対策ソフトに見つからないウィルスを作るのか」ということに繋がる。

システム

最新の投稿(電子情報)

アーカイブ

カテゴリー