Exploiting実習 第2回


目次


ペイロードの作成について


ペイロード作成の流れ

ペイロードは次の手順に従って作成する.

  1. 行いたい動作の決定
  2. 動作対象システムを調べる
  3. アセンブリ言語でプログラムを記述
  4. 16進数エディタなどで開きバイトコードを取得
  5. 動作テスト

アセンブリの復習

命令(instruction)

主に次のような命令がある(instruction, x, y). これらを組み合わせて行いたい処理を記述する.

下位互換命令と下位互換レジスタ

GASでは8ビットコンピュータおよび16ビットコンピュータとの互換性を保つため,レジスタおよびレジスタを操作する命令にはそれぞれ8ビット用命令,16ビット用命令,32ビット用命令の3種類が用意されている.
8ビット用命令なら32ビットコンピュータで動作しても下位8ビットのみを操作する.レジスタを参照する場合でも8ビット互換レジスタを指定すれば32ビットレジスタであっても下位8ビットしか参照しない.

互換命令

mov %eax, %ebxのように互換命令部分を省略して記述することも可能.この場合は後ろのレジスタの種類によってl,w,bが補完される.

互換レジスタ

32ビットの領域を全て認識するeax、下位16ビットのみを認識するax、axの上位8ビットを認識するah、axの下位8ビットを認識するalの図
図1:互換レジスタの参照部分

実際には次のように記述する.


movl $0x1, %eax        ;1をeaxに代入    eax: 0000 0001
incl %eax              ;eaxに1加算      eax: 0000 0002
movb %ah, %al          ;eaxをゼロクリア eax: 0000 0000
movl $0xFFFF0F0F, %eax ;eaxに値を代入   eax: FFFF 0F0F
movw $0xF0F0, %bx      ;ebxにF0F0を代入 ebx: 0000 F0F0
xorw %ax, %bx          ;axとbxのxor      ax: 0F0F→0000111100001111
                       ;                 bx: F0F0→1111000011110000
                       ;eaxはFFFFFFFFになる

要求の確定について

要求から仕様を導く


動作対象システムを調べる

対象システムの詮索方法

ハッキングは他者の管理するシステムへ行うが,通常システム情報が公開されていることはない.ゆえになんらかの方法で,どのOSを使っているのか詮索する必要がある.


HelloWorldの作成と機械語への変換

メモをとっていない方,第0回その2に参加していない方は次のソースをご利用ください.


.code32
 
.data
msg: .ascii "Hello!"
 
.global main
main:

movl $4, %eax
movl $0, %ebx
movl $msg, %ecx
movl $6, %edx
int $0x80
ret

前回作ったプログラムはあまり行儀の良いプログラムとは言えない.プログラムがすべて終わったことを示し,OSに制御を移すべくexitシステムコールを呼ぶのが普通.

例題 2.1

前回作成したHelloWorldプログラムにexitシステムコールを追加し,まっとうなプログラムに更生せよ.

バイトコードへの変換

実行ファイルを2進数や16進数で表したものをバイトコードと呼ぶ.ペイロードとして実行するプログラムは最終的にバイトコードに変換して利用する必要がある.

バイトコード変換手順

  1. 実行ファイルを16進数エディタで開く
  2. 表示される16進数標記の部分をコピー

16進数エディタのobjdumpを使う.そのままでは不要な部分まで表示され見にくいため,次のようにタイプする.

defolos@glazheim:~$ objdump -d a.out|grep \<main\> -A 30

ペイロードとしての問題点

NULLバイト問題

NULLバイト(0x00)はstrcpy()などでは文字列の終端を意味するため,ペイロードの途中にNULLが含まれると正常に動作しない.

NULLバイト問題の実例


int main(int argc, char *argv[]){
    char str1[] = "Hello\0World";
    char str2[12];

    strcpy();
    printf("%s\n", str2);

    return 0;
}

アドレッシング問題


ペイロード化に伴うテクニック

前述の問題点を解決するために,ペイロードの作成スタイルには独自のテクニックを使用することが多い.

アドレッシング問題の解決

スタックセグメントのみを使用するようにする.特に問題になるのが文字列などのデータ.文字列を格納しているメモリの先頭アドレスを取得できれば良い.

jmp/callテクニック


main:
    jmp    ONE

TWO:
    popl %ecx
    ;codes...

ONE:
    call   TWO
    .string "STRINGS"

pushテクニック

文字列を4バイトごとに区切ってスタックにプッシュすることで文字列を生成する.


pushl   $0x0
pushl   $0x ;!gni
pushl   $0x ;nroM
movl    %esp,   %ecx

ただし,文字列が4の倍数でなければこのテクニックは利用できない.

NULLバイト問題

NULLバイトの原因はコード中に出現する「0」が影響している.つまり,0を使わずにコードを書けば良い.

xorの利用

4バイト命令の埋め草問題

「movl $0x5, %eax」は4バイト(32ビット)の転送命令であるが,0x5は101であり8ビットでも表現可能.残の24ビットには埋め草として「0」が挿入され,コンパイル時にmovl $0x00000005, %eaxと解釈される.


movb    $0x5,   $al

作成したペイロードのテスト

ペイロードのテストには,プログラム内で意図的にペイロードを実行する次のプログラムを利用する.


unsigned char payload[]="SHELLCODE";

int main(void){
    int *retadd;
    retadd = (int *)&retadd + 2;
    (*retadd) = (int)payload;
    return 0;
}

プログラムの流れを変えてスタックセグメント上のペイロードを実行する点で,バッファオーバフローなどのExpliotと動作は変わらない.

例題 2.2

これまで紹介したテクニックを参考にし,シェル(/bin/sh)を起動するペイロードを作成せよ


次回の予定


Copyleft (C) 2007. Defolos. All rights opened.