バッファオーバーフロー脆弱性 - プログラミング学習サイト

バッファオーバーフロー脆弱性

😎 クラッキング ハッキング CTF 学習サイト

https://minegishirei.hatenablog.com/entry/2024/08/17/110233

結論

入力値チェックは大事だね。

古事記にもそう書いてある。

バッファオーバーフロー脆弱性

バッファーオーバーフロー脆弱性は、コンピューターの黎明期から今に至るまで見かけるものです。 InternetExploreのVML脆弱性についても、バッファオーバーフローが用いられていました。

C言語バッファオーバーフロー

C言語は高水準プログラミング(?)と呼ばれますが、データの整合性確保についてはプログラマが責任を持ちます。 この責任をコンパイラに任せてしまうと、全ての変数に対して整合性チェックが行われ、実行プログラムの処理速度が低下してしまうのです。

C言語は簡潔さと速度を信条としますが、データの整合性は捨てています。

プログラマがコントロールできる部分を増やし、実行効率を高めてはいますがその代償として、プログラマのうっかりミスによってバッファオーバーフローやメモリりーくが発生してしまうのです。

8バイトしか割り当てられない領域に、10バイトのデータを割り当てようとすると、プログラムがクラッシュしてしまうこともあるのです。

具体的にコードを見てみます。

バッファオーバーフロー サンプルコード

環境構築

Note

この章ではバッファオーバーフローを実演します。 Dockerおよびgitをインストールしている方は、次のコードでC言語の実行環境を整えることができ、ソースコードも同封されています。

git clone https://github.com/minegishirei/hacking_lab
cd hacking_lab
cd c_language
./run.sh

bashが立ち上がった後は、次のコマンドでビルドと実行をしてみてください。

cd buffer_overflow
gcc -g -O0 main.c
./a.out aaa

サンプルコード

例えば、次のようなコードを実行してみる。

# include <stdio.h>
# include <string.h>

int main(int argc, char *argv[]){
    int value = 5;
    char buffer_one[8], buffer_two[8];

    strcpy(buffer_one, "one");
    strcpy(buffer_two, "one");
    
    printf("buffer_two ポインター : %p \n", buffer_two);
    printf("buffer_two 値 : %s \n", buffer_two);
    printf("buffer_one ポインター : %p \n", buffer_one);
    printf("buffer_one 値 : %s \n", buffer_one);
    printf("value ポインター : %p \n", value);
    printf("value 値 : %d \n", value);
    /*最初の引数をbuffer_twoにコピーする*/
    strcpy(buffer_two, argv[1]);
    
    printf("buffer_two ポインター : %p \n", buffer_two);
    printf("buffer_two 値 : %s \n", buffer_two);
    printf("buffer_one ポインター : %p \n", buffer_one);
    printf("buffer_one 値 : %s \n", buffer_one);
    printf("value ポインター : %p \n", value);
    printf("value 値 : %d \n", value);
}

後はこのコードを保存し、 gcc -g -O0 main.cを実行してみてください。 (Gitリポジトリの中にはすでに同封済み。)

a.outファイルが作成されたら完了です。

実験

メモリ内部に収める

まずは通常通り、 aaaaと入力してみる

./a.out aaaa

結果、特に何も起きなかった。

root@275f7b1d6b9e:/code/buffer_overflow# ./a.out aaaa
buffer_two ポインター : 0xffffca961a38 
buffer_two 値 : one 
buffer_one ポインター : 0xffffca961a40 
buffer_one 値 : one 
value ポインター : 0x5 
value 値 : 5 
buffer_two ポインター : 0xffffca961a38 
buffer_two 値 : aaaa 
buffer_one ポインター : 0xffffca961a40 
buffer_one 値 : one 
value ポインター : 0x5 
value 値 : 5 

オーバーフローさせる

次に、配列の確保分を超えた aaaaaaaaaと入力してみる

./a.out aaaaaaaaa

結果、buffer_twoから溢れた一文字のaが、buffer_oneに格納されてしまった。オーバーフロー発生!

root@275f7b1d6b9e:/code/buffer_overflow# ./a.out aaaaaaaaa
buffer_two ポインター : 0xffffdc6761e8 
buffer_two 値 : one 
buffer_one ポインター : 0xffffdc6761f0 
buffer_one 値 : one 
value ポインター : 0x5 
value 値 : 5 
buffer_two ポインター : 0xffffdc6761e8 
buffer_two 値 : aaaaaaaaa 
buffer_one ポインター : 0xffffdc6761f0 
buffer_one 値 : a 
value ポインター : 0x5 
value 値 : 5 

さらにオーバーフローさせる

buffer_twoをこえ、buffer_oneをこえ、16文字以上の引数を入れた場合... aaaaaaaaaaaaaaaaaaaaa

オーバーフローする文字に制限はないことがわかった

./a.out aaaaaaaaaaaaaaaaaaaaa
buffer_two ポインター : 0xffffd238f2e8 
buffer_two 値 : one 
buffer_one ポインター : 0xffffd238f2f0 
buffer_one 値 : one 
value ポインター : 0x5 
value 値 : 5 
buffer_two ポインター : 0xffffd238f2e8 
buffer_two 値 : aaaaaaaaaaaaaaaaaaaaa 
buffer_one ポインター : 0xffffd238f2f0 
buffer_one 値 : aaaaaaaaaaaaa 
value ポインター : 0x61 
value 値 : 97

予防方法

buffer_two のサイズは8バイトですが、argv[1] の長さがこれを超える場合、メモリが破壊される可能性があります。 コピーする際には、strncpy などの安全な関数を使ってバッファオーバーフローを防ぐべきです。

#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[]){
    int value = 5;
    char buffer_one[8], buffer_two[8];

    strcpy(buffer_one, "one");
    strcpy(buffer_two, "one");

    printf("buffer_two ポインター : %p , 値 %s\n", buffer_two, buffer_two);
    printf("buffer_one ポインター : %p , 値 %s\n", buffer_one, buffer_one);
    printf("value      ポインター : %p , 値 %d\n", &value, value);
    
    if (argc > 1) {
        strncpy(buffer_two, argv[1], sizeof(buffer_two) - 1);
        buffer_two[sizeof(buffer_two) - 1] = '\0'; // ヌル終端
    }

    printf("buffer_two ポインター : %p , 値 %s\n", buffer_two, buffer_two);
    printf("buffer_one ポインター : %p , 値 %s\n", buffer_one, buffer_one);
    printf("value      ポインター : %p , 値 %d\n", &value, value);

    return 0;
}

まとめ

入力値チェックは大事だね。

古事記にもそう書いてある。

参考

Hacking:美しき策謀 第2版 ―脆弱性攻撃の理論と実際

Jon Erickson 著、村上 雅章 訳

https://www.oreilly.co.jp/books/9784873115146/

page:https://minegishirei.hatenablog.com/entry/2024/10/01/214408