- 1972 年:バッファオーバーフローのバグを利用した攻撃について公に言及された
- 1988 年:Morris Worm がバッファオーバーフローバグを侵入のための手段として用いる事で,バッファオーバーフローのバグが実用的な攻撃として認知され始めた
- 1991 年:Linux 初リリース
- 1996 年:スタックにシェルコードを配置して任意コードを実行する方法がまとめられた
- 1998 年:
-f-stack-protectorが開発される - 2001 年:PaX 初リリース
データ用とコード用のページを分けて,データ用ページからの命令フェッチを禁止するというのが主な機能.
x86_64 アーキテクチャの PAE Paging と 4-Level Paging のためのページテーブルには XD ビット(63 ビット目)がある. このビットを 1 にすると,そのページからの命令フェッチができなくなる.
PaX は ASLR(Address Space Layout Randomization)も提供する.
仮想アドレス空間を固定長の領域に分割し,それぞれの領域を物理アドレス空間にマッピングする CPU の機能. 仮想アドレスと物理アドレスの変換は CPU の中の MMU(Memory Management Unit)が担当する.
参考 https://www.tutorialspoint.com/operating_system/os_virtual_memory.htm
カーネルをハックする方法は主に 2 つ
- カーネルモジュール等の正規の手段を悪用する
- カーネル自体の脆弱性を利用する
前者の方法として Linux on-the-fly kernel patching without LKM がある. これは /dev/kmem や SIDT 命令を用いる事でユーザーの権限でリアルタイムにカーネルのメモリを書き換える手法.
IDT はハードウェア,ソフトウェア割り込みに対応するハンドラ(関数)のアドレスが登録してある表.メモリ上に載っている.CPU が割り込みを受け付けると,割り込みベクタ番号に対応するハンドラを起動する.ハードウェア割り込みとしてはキーボード入力割込,タイマ割込,ネットワーク受信割込,などがある.
Linux のシステムコールは,レジスタに必要な値をセットした後 int 0x80 を発行する.これは割り込みベクタ 0x80 に対するソフトウェア割込を発生させる.すると CPU は IDT 0x80 に登録されたハンドラを起動する.このハンドラはシステムコールを受け付けるハンドラになっており,レジスタの値に基づいてシステムコールが実行される.
このハンドラ一つで全システムコールに対応するために,システムコール別の関数表が別途ある.例えば read や write システムコールの関数アドレスが登録された表だ.この表を書き変えることで,攻撃者はシステムコールを乗っ取ることができる.
IDTR(IDT のあるアドレスを格納してあるレジスタ)の内容を指定した変数にコピー(Store)する命令.この命令を実行すると,IDT の場所が分かる.IDT の 0x80 番目を読めば int 0x80 に対応するハンドラの場所が分かる.後は,そのハンドラの機械語をちょこっと変更し,細工されたシステムコール表を参照させれば攻撃できる.