黒ネコと学ぶ、Buffer Overflow 超基礎シリーズ
エクスプロイト開発を学び始めると、最初によく出てくる言葉があります。
それが「Buffer Overflow(バッファオーバーフロー)」です。
この言葉を初めて見ると、
くろちゃん
「難しそう」「危なそう」「自分にはまだ早そう」
と感じると思いませんか?
でも、最初の考え方だけなら、じつはとてもシンプルなのです。
ひとことで言うと、Buffer Overflowとは、「小さい箱に、大きすぎるデータを無理やり入れて、あふれさせてしまうこと」です。
そして、コンピュータの世界では、この「あふれたデータ」がただ消えるのではなく、となりの大事な場所まで壊してしまうことがあるため、脆弱性として非常に重要になります。
今回は、まず
・バッファとは何か。
・オーバーフローとは何か。
・なぜ危険なのか。
この3つを、やさしく理解することを目標にします。
※イメージです。
Buffer Overflowを身近なたとえで考えてみよう
まずはコンピュータの話を少し離れて、身近なたとえで考えてみます。
たとえば、小さなコップを思い浮かべてください。
そこに少しずつ水を入れるなら問題ありません。
ですが、コップの大きさをこえて水を入れ続けると、水はあふれます。
白猫先生
これが「オーバーフロー」のイメージです。
また、8冊しか入らない本棚に12冊の本を無理やり入れようとすると、本棚の外にはみ出します。
カバンの大きさを考えずに荷物を詰め込みすぎても、入りきりません。
コンピュータの中でも、これと同じようなことが起きます。
プログラムは、文字やデータを一時的に置いておくために、小さなメモリ領域を用意します。
その「一時的な入れ物」が「バッファ」です。
もし、そのバッファに入りきらない量のデータを書き込んでしまうと、コップの水のようにあふれます。
ただし、コンピュータでは机の上にこぼれるのではなく、その先にある別のメモリ領域へ書き込まれてしまうのです。
白猫先生
ここがとても大事です。ただの「あふれ」ではなく、本来さわってはいけない場所まで壊してしまうことが、Buffer Overflowの危険な点です。
バッファとは何か
では、バッファとは何でしょうか。
バッファとは、簡単に言うと、データを一時的に入れておく箱です。
プログラムは、ユーザーから受け取った文字列や、途中で使うデータを、いったんどこかに保存する必要があります。
そのためにメモリの中に小さな領域を用意します。
C言語では、たとえば次のように書くことがあります。
char buf[8];
これは、1文字ずつ入る小さな部屋を8個用意したという意味です。
つまり"buf"という名前の箱を作り、その中に最大で8文字ぶんのデータを入れられるイメージです。
こんなイメージになります。
buf[0]
buf[1]
buf[2]
buf[3]
buf[4]
buf[5]
buf[6]
buf[7]
ここで大切なのは、この箱には限界があるということです。
無限に文字が入るわけではありません。
8個しか部屋がないなら、それ以上のデータを入れようとすると問題が起きます。
白猫先生
つまり、バッファは便利な保存場所ですが、大きさを超えて使ってはいけないのです。
なぜオーバーフローが起きるのか
くろちゃん
では、なぜBuffer Overflowが起きるのですか。
理由はとてもシンプルで、
入れるデータの大きさを確認しないまま書き込んでしまうからです。
たとえば、8文字しか入らない箱に、11文字や12文字をそのまま入れたら、当然入りきりません。
現実でも、小さい箱に大きい荷物を押し込めば、あふれたり、ふたが閉まらなくなったりします。
C言語では、文字列をコピーするときに、使い方によっては長さを十分に確認しない関数があります。
そのため、プログラムを書く人が注意しないと、「何文字まで入る箱なのか」を考えないままコピーしてしまい、Buffer Overflowが起きます。
ここで大事なのは、
悪いのは長い入力そのものではないということです。
白猫先生
本当に危険なのは、「箱の大きさを確認しないままデータを入れること」です。
つまり、Buffer Overflowは、単なる「入れすぎ」ではなく、境界を守らずに書いてしまうことが本質です。
サンプルコードでやさしく理解する
ここで、学習用のシンプルなコードを見てみましょう。
-----
#include <stdio.h>
#include <string.h>
int main(void) {
char buf[8];
strcpy(buf, "ABCDEFGHIJK");
printf("%s\n", buf);
return 0;
}
-----
白猫先生
このコードは、安全な書き方の見本ではありません。むしろ、なぜ危険なのかを学ぶためのサンプルプログラムです。
まず、char buf[8];は、8文字ぶんの領域を用意しています。
つまり "buf" は小さな箱です。
次に、strcpy(buf, "ABCDEFGHIJK");
では、右側の文字列を左側の "buf" にコピーしています。
しかし、"ABCDEFGHIJK"は8文字より長い文字列です。
そのため、本来なら"buf"に入りきりません。
それなのに、このコードでは「本当に入る長さかどうか」を確認せず、そのままコピーしています。
すると、"buf"の中に収まりきらないぶんが、箱の外にはみ出して書き込まれる可能性があります。
これが「Buffer Overflow」です。
最後の、printf("%s\n", buf);は、"buf"の中身を表示しています。
ですが、すでにメモリの中身が壊れていると、変な文字が出たり、場合によってはプログラムが落ちたりすることがあります。
このコードをたとえで言うなら、
8人しか座れないベンチに、11人を無理やり座らせようとしている
ようなものです。
最初の8人は座れても、残りの3人は、はみ出します。
コンピュータでは、その「はみ出した部分」がとなりの大事な情報をこわしてしまうのです。
なぜBuffer Overflowは危険なのか
Buffer Overflowが危険なのは、単に「あふれる」からではありません。
あふれた先に、別の大事なデータがあるからです。
コンピュータのメモリには、いろいろな情報が並んで置かれています。
ある場所には文字列、別の場所には数字、さらに別の場所にはプログラムの動きに関わる重要な情報があります。
そのため、1つのバッファからデータがあふれると、
・別の変数の値が壊れる
・文字列の終わりがわからなくなる
・プログラムの動きがおかしくなる
・場合によってはクラッシュする
といった問題が起こります。
白猫先生
このように、Buffer Overflowはプログラムを不安定にするだけでなく、場合によっては脆弱性として悪用される可能性もあるため、エクスプロイト開発では最初の大事なテーマになります。
Buffer Overflowとは何か?黒ネコでもわかる最初の入口のまとめ
今回学んだ「Buffer Overflow」とは、小さなメモリ領域であるバッファに、入りきらない大きさのデータを書き込んでしまい、その領域をはみ出して周囲のメモリまで壊してしまう現象です。
バッファは、文字やデータを一時的に入れておく箱のようなもので、オーバーフローはその箱から中身があふれることを意味します。危険なのは、あふれたデータが消えるのではなく、となりにある別の変数や重要な情報まで上書きしてしまう点です。
つまり、Buffer Overflowは「入れすぎ」の問題であると同時に、「境界を守らずに書き込んでしまう」問題でもあります。まずは、バッファは小さな箱であり、長さを確認しないコピーが危険である、という基本イメージをしっかり持つことが大切ですね。