ハッカーが悪用するスタックオーバーフロー。仕組み・防御・実際のエクスプロイト手法までわかりやすく解説
スタックオーバーフロー(Stack Overflow)は、プログラムの動きを自由に書きかえる代表的な脆弱性です。
このバグを使えば、プログラムを自分の思い通りに動かすことができてしまいます。
スタックとは
スタックは、プログラムが関数(命令のかたまり)を使うときに使う、メモリの入れ物です。
たとえば、
縦に細長いお弁当箱におかずをいれるときに
まず、玉子焼きをいれます。
次に、ミニトマトをいれます。
次に、ソーセージをいれます。
次に、トマトをいれます。
次に、イチゴをいれます。
すると、おかずを食べるときには、
デザートのイチゴを一番最初に食べて、
次に、トマト
次に、ソーセージ
次に、ミニトマト
最後に、玉子焼きを食べることになります。
後に入れたおかずから食べていく、これがLIFOです。
LIFO(Last In, First Out)とは、データ構造やメモリ管理の方式で、最後に追加されたデータが最初に取り出されるという特徴を持つものです。日本語では「後入れ先出し」とも呼ばれます。メモリ管理においては、スタック領域でLIFOの方式が用いられ、関数の呼び出しやローカル変数の管理に利用されます。
スタックオーバーフローが起きる仕組み
スタックには、「これくらいの大きさまでデータを入れてね」という決まり(バッファサイズ)があります。
でも、もしその決まりよりも大きなデータを無理やり入れると、隣の大事な情報(戻り先など)まで上書きしてしまうことがあります。
それが「スタックオーバーフロー」です。
たとえば
お弁当箱にごはんを入れすぎると、ふたが閉まらなくなって中身がはみ出しますよね。
同じように、プログラムの中でも、
決められた「入れ物(メモリ)」にデータを入れすぎると、となりの情報がこわれてしまうのです。
C言語でスタックオーバーフローの確認してみましょう
1.プログラム
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> // read() に必要
// スタックオーバーフローで無理やり呼び出す予定の処理
void secret() {
printf(“攻撃成功:/bin/shを起動 \n");
system(“/bin/sh");
}
void vuln() {
char buf[32];
// 読み込み最大64バイト → わざとオーバーフローを起こす
read(0, buf, 64);
}
int main() {
vuln();
return 0;
}
2.コンパイル
gcc -fno-stack-protector -z execstack -no-pie -o stack_overflow01 stack
上記のエラーは
表示された内容は「警告(warning)」であって、致命的なエラー(error)ではありません。
これは、「このコードは危険だよ。バッファがあふれるかもしれないよ」という、GCCからの注意になります。
これは、「このコードは危険だよ。バッファがあふれるかもしれないよ」という、GCCからの注意になります。
3.実行
./stack_overflow01
4.データ入力(aを64文字入力)
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
zsh: segmentation fault ./stack_overflow01
※バッファオーバーフロー(スタックオーバーフロー)が成功して、プログラムがクラッシュしました。
つまり、スタックオーバーフローの再現に成功した。ということです。
このプログラムでは、buf という小さな入れ物にたくさんの文字を入れることで、secret() という関数にジャンプしてしまうことができます。(実際にはジャンプしていません。)
スタックオーバーフローの悪用方法について
攻撃する人は、buf に長い文字列を入れて、戻る場所(リターンアドレス)を自分の好きな場所に書きかえて、自分の命令(シェルなど)を動かします。
これを「エクスプロイト(Exploit)」といいます。
スタックを守る防御と対策
今のコンピューターには、スタックオーバーフローを防ぐ工夫がされています。
1. Canary(カナリア)
スタックの中に「見張り番の数字」を入れておく仕組みです。
もしスタックオーバーフローで、バッファからデータがあふれても、この「見張り数字(Canary)」が書きかえられてしまうことで、「あっ、攻撃されたかも」と検知できます。
2.NXビット(No eXecute ビット)
「このメモリはデータだけで、プログラムとして実行しちゃダメ」と決める機能です。
攻撃者が、シェルコード(悪い命令)をスタックに入れても、NXビットがあると、それを「実行できない」ようにします。
3.ASLR(Address Space Layout Randomization)
プログラムが使うメモリの場所を毎回バラバラにする仕組みです。
攻撃者が「この場所にシェルコードがあるはず」と思っても、実行のたびに場所が変わるので、当てるのが難しくなるのです。
ハッカーが悪用するスタックオーバーフロー。仕組み・攻撃例・実際のエクスプロイト手法まで解説のまとめ
スタックオーバーフローは「入りすぎたデータ」がプログラムの動きを変えるバグで、プログラム内の一時的なメモリ領域(スタック)に、決められた以上のデータを無理に入れることで、隣の重要な情報(戻りアドレスなど)まで上書きしてしてしまいます。
これを悪用すると、攻撃者はプログラムの動きを変えて、自分の命令を実行させることができます。C言語のような低レベル言語では、このようなチェックが行われないため、バッファ(データの入れ物)に大きな入力を渡すだけで、スタックがあふれてしまうことがあります。
実際にコードを書いて動かすと、理解がぐんと深まると思います。