Format String Attacks

7324 ワード

本文は原文に対して一字一句の翻訳を行いません.原文の基本原理部分だけを残して、自分の理解を少し加えました.もう一つのブログを書きました.文字列攻撃に関する小さな実験を記録してフォーマットしました.http://blog.csdn.net/aqifz/article/details/49704287.
原文情報:著者Tim Newsham,2000.9.ダウンロードアドレス:http://forum.ouah.org/FormatString.PDF
要約
文字列の脆弱性をフォーマットする原因と影響を議論し、実際の例と組み合わせて原理を説明する.
紹介する
本文は文字列の攻撃をフォーマットする方面を紹介して、知らない人に基本的な認識があるようにします.
文字列攻撃のフォーマットは何ですか?
    文字列の脆弱性をフォーマットするのは多くの他のセキュリティ・ホールと同じで、プログラミング者の怠けから来ます.例えば、プログラミング者はコードを書いています.その任務は文字列を印刷したり、バッファにコピーしたりします.本来はprintf("%s",str)と同じように書くべきです.彼は6文字を打つのがおっくうで、書きました.
printf(str)そうですよ.何を楽しみますか?なぜ余計なprintfパラメータに気を使って、これらの面倒なフォーマットを解析するのに時間がかかりますか?
とにかく
printfの最初のパラメータは印刷する文字列です.しかし、プログラミング者は、攻撃者がプログラムの実行を制御できるようにセキュリティ・ホールを開いたとは知らなかった.これが文字列の脆弱性をフォーマットする根本的な原因である.
プログラミング者は一体何を間違えましたか?彼はそのまま印刷したい文字列を伝えましたが、printfはこの文字列をフォーマット文字列に解析して、特殊な書式記号(例えば%dなど)をスキャンして、見つけたらスタックからパラメータを取得します.明らかな結果は、攻撃者が印刷スタックの値を通じてプログラムメモリを覗き込むことができ、攻撃者がプログラムのメモリに任意の値を書き込むことができます.
Printf-学校が教え忘れたもの
    printfのいくつかの基本的な特性を知っていると仮定して、ここで紹介します.
文字列攻撃の書式設定に関するいくつかの特性.
    フォーマットされた文字列のどの点でも現在の文字の出力数を取得できます.%nに解析すると、出力された文字の数は対応するパラメータに書きます.例えば、2つのフォーマット数の間の位置ずれを取得します.
int pos, x = 235, y = 93;
printf("%d %n%d
", x, &pos, y); printf("The offset was %d
", pos);
    なお、%nフォーマットの文字は、実際に出力される文字の個数ではなく、出力されるべき文字の個数を返します.文字列を固定サイズのバッファに書式設定すると、出力文字列は切断されますが、%nは文字列が切断されていない場合にオフセットを返します.例えば、次の例では20ではなく100が出力される.
char buf[20];
int pos, x = 0;
snprintf(buf, sizeof buf, "%.100d%n", x, &pos);
printf("position: %d
", pos);
簡単な例
    以下の具体例によって、文字列をフォーマットする原理を説明します.
/*
 * fmtme.c
 * Format a value into a fixed-size buffer
 */
#include 
int
main(int argc, char **argv)
{
 char buf[100];
 int x;
 if(argc != 2)
 exit(1);
 x = 1;
 snprintf(buf, sizeof buf, argv[1]);
 buf[sizeof buf - 1] = 0;
 printf("buffer (%d): %s
", strlen(buf), buf); printf("x is %d/%#x (@ %p)
", x, x, &x); return 0; }
    まず、このコードの目的は簡単です.コマンドラインが入ってきた値を一定の長さのバッファに書式設定し、オーバーフローしないように長さを制限し、書式設定された文字列をフォーマットして出力し、2番目の整数値を設定して出力します.この変数は後で攻撃の対象となりますが、現在の値はずっと1です.注意します
本明細書の全ての例はx 86 BSD/OS 4.1 boxで実行されており、異なるシステムは異なるかもしれない.
(snprinntfの関数のプロトタイプは、int snprint f(char*str、sizaut size、const char*format)、フォーマット文字列の後ろには具体的なパラメータがあるはずです.正常な呼び出しはsnprint(buf、size of buf、「%s」、argv[1]. )
FOREMAT ME
    いくつかの攻撃を試してみます.通常のパラメータ呼び出しプログラムを使用し始めました.これは特別なものではありません.プログラムフォーマット文字列はバッファに行ってから長さと値を印刷して、xの10進数/16進数の値と記憶されている住所0 x 804745 cを印刷します.

    次に、スタック内の整数を印刷するなど、いくつかの書式記号が入ってきます.

    プログラムを高速解析すると、main関数がsnprintf関数を呼び出した時のプログラムのスタックレイアウトが得られます.
Format String Attacks_第1张图片
(x 86のスタック構造によれば、スタックは高アドレスから低成長に向かって、先にアドレスに戻り、保存されているebpであり、保存されている他のレジスタやcanaryである可能性があります.そして、関数にローカル変数空間を割り当てて、サブ関数snprintfパラメータであり、参照時は右から左への順番でスタックを押します.テーブルは順次、bufアドレス、sizef buf、sizobuf、argv[1]ポインタ、x、bufバッファ.ここでは各フォーマットの%xに対応すべきパラメータは与えられていませんが、snprintfは知られていません.高いアドレスにスタックの内容を順番に読み込むだけがパラメータです.)
    前のテスト出力の4つの値はスタックの中で文字列の後をフォーマットする4つのパラメータです.変数x、未初期化のbuf変数から取り出した3つの整数です.
    ポイントとして、攻撃者としてバッファ内の値を制御しました.これらの値はsnprintfのパラメータとしても使用できます.迅速なテストで検証します.

    前の4つのa文字はバッファの先頭にコピーされ、snprintfによって整数パラメータに解析され、その値は0 x 616161(aのascii値)です.
前にすでに3つのパラメータがあります.この2つの%x書式子はそれぞれ対応します.
4番目、5番目のパラメータですので、xとbufバッファの前の4バイトをこの2つのパラメータとして読みました.このとき、bufの前の4バイトはすでに「aaaa」を充填しました.そして、1と「aaaaa」の16進数は「aaaa」の文字に接続してからbufに書き込みます.)
確実な点
    現在、受動的な検知から積極的にプログラム状態を変化させて変数xを覚えていますか?以下でその値を変更するには、1つのパラメータでそのアドレスに入る必要があります.ここでは最初のパラメータ:変数xをスキップして、%nフォーマット記号で指定されたアドレスを書きます.(ここではPERLでプログラムを実行し、コマンドラインパラメータに任意の文字を配置します.)

(ここのxアドレスは変更されました.アドレスのランダム化のためか、それとも作者がプログラムコードを修正したためか、レイアウトが少し変化しました.)
xの値が変化しました.いったい何が起こっていますか?snprintfのパラメータは実際には以下の通りです.
snprintf(buf, sizeof buf, "\x58\x74\x04\x08%d%n", x, 4 bytes from buf);
    開始snprintfは最初の4バイトをbufにコピーした(\x 58\x 74\x 04\x 08は4つの文字化けして印刷したはずです)、その後%dフォーマットをスキャンしてxの値をプリントしました.最後に%nフォーマット子になりました.スタックの中の次の値であるbufの前の4バイトをパラメータとして、この4バイトはすでに「x 58\x 74\x 74\04 x 74\748」に充填されました.snprintfは出力したバイト数をこの住所に書き込みます.この住所はxの住所です.これは偶然ではありません.事前検査プログラムで0 x 0807458の値を選択しました.このケースでは、プログラムは私達が知りたい住所を印刷するのに役立ちます.もっと典型的なのは、この値はデバッグプログラムで分かります.
    現在は任意のアドレスを選択して値を書き込むことができます.このアドレスにNUL文字が含まれていない限り、有効な値を書き込むことができますか?snprintfは現在文字の数しか書き込みできません.小さな値を書きたいなら(>4、少なくとも宛先アドレスは4バイトを占めます)解決方法は比較的簡単です.正しい値まで文字列を充填します.もっと大きな値なら?カットオフがない場合、%nは出力すべき文字の数を計算します.

    %n書き込みxの値は504で、実際の書き込みbufの99文字数よりもはるかに大きいので、大きなドメイン幅を指定することで、任意の大きな値を書き込むことができます.(これはいくつかのバージョンのglibc printfにおいて、大きなドメイン幅を指定すると、printfが内部バッファをオーバーフローさせてプログラムのcrassを引き起こす欠陥があります.したがって、あるバージョンのLinuxでは、攻撃プログラムは何千もの幅より大きいものは使えません.例えば、printf(「%.9999 d」、1)は、セグメントエラーを引き起こすことがあります.この小さい値は何回つづり合わせて書きますか?任意の値(または0)を作成します.バイトをオフセットとして4つの数を書いたら、4つの最も重要でないバイトを通して任意の整数を作成できます.状況を説明するために、4回書きます.
Format String Attacks_第2张图片
    4回の書き込みが完了した後、0 x 44332211はアドレスAに残り、4回の書き込みの最も重要でないバイトから構成されています.この技術は書き込みの値を柔軟に選択することができますが、欠点があります.4回の書き込みで値を設定し、目標アドレスの隣の4バイトをカバーし、3回の非整合書き込みが必要です.一部のアーキテクチャは非整列書き込みをサポートしていないので、この技術は広く適用できません..
それはどうですか
    それはどうですか?メモリに任意の値を書いてもいいです.
    プログラム格納されているUIDを書き換え、特権を低減し、向上させる.
    実行されたコマンドを書き換える.
    住所を書き換えて、shellcodeを含むバッファエリアを指します.
    簡単に言えば、あなたはプログラムを持っています.
では、今日は何を学びましたか?
    printfはあなたの予想より強いです.
    手抜きはいつまでもいい報いがありません.
    油断しているように見えますが、攻撃者に十分な攻撃手段を提供して、一日を台無しにします.
    暇な時間、精力、文字列を入力して、誰かの小さな間違いを全国の聯合ニュースの物語に変えることができます.