実行ファイルのセキュリティ機構についてまとめてみる
CTFのpwnについて調べているとよくセキュリティ機構の用語が出てきます。しかし、それぞれのセキュリティ機構がどんな役割を果たすのかをよく忘れてしまいます。なので、一度まとめてみました。
実行ファイルのセキュリティ機構について、こちらの書籍を参考にしました。
セキュリティ機構の調べ方
https://github.com/slimm609/checksec.sh
checksecというものがあります。checksecはセキュリティ機構をわかりやすい形式で表示してくれるシェルスクリプトです。
使い方
以下のコマンドを実行すると、ダウンロードすることができます
git clone https://github.com/slimm609/checksec.sh
ダウンロードが完了すると、コマンドを実行した階層にchecksec.sh
というディレクトリができます。
その中に移動すると、checksec
というファイルがあります。これをセキュリティ機構を調べたいファイルがある場所にコピーして、
./checksec --file=<file name>
を実行すると、セキュリティ機構が表示されます。
RELRO
RELRO(RELocation Read Only)はメモリ上のデータのどの部分に対してReadOnly属性を付けるか決定する役割を果たします。
RELROには以下の3種類が存在します。
- No RELRO
- Partial RELRO
- Full RELRO
No RELROの時とPartial RELROのときは、GOT領域が書き込み可能なためGOT overwrite攻撃が成功します。一方、Full RERLOのときはGOT領域を読み込み専用にしてしまうためGOT overwrite攻撃が成功しません。
RELROはコンパイル時に-Wl,-z,norelro
を指定すると無効にすることができます。以下に例を示します。
RELRO有効(デフォルト)
$ gcc -o target example.c $ checksec target [*] '/home/vagrant/hoge/target' Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled
RELRO無効
$ gcc -Wl,-z,norelro -o target example.c $ checksec target [*] '/home/vagrant/hoge/target' Arch: amd64-64-little RELRO: No RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled
SSP
SSP(Stack Smashing Protection)はバッファオーバーフローを防ぐための役割を果たします。
SSPでは、関数呼び出し時にリターンアドレスとローカル変数の間にCanary(カナリア)と呼ばれる値をスタックに挿入し、関数の終了時に値が書き換えられているかどうか判定することで、スタックオーバーフローを検出します。
SSPが有効になっている時と無効になっている時の関数呼び出しを例に見てみましょう。
下の図は、SSPが無効になっている時のスタックの様子です。
+--------------------+ ↑ | ... | Low Address +--------------------+ | buffer | +--------------------+ | ... | +--------------------+ | saved rbp | +--------------------+ | ret addr | +--------------------+ | arg 1 | +--------------------+ | ... | High Address +--------------------+ ↓
このスタックでは、bufferがバッファオーバーフローを起こしても検出することができません。
一方、下の図はSSPが有効になっている時のスタックの様子です。
+--------------------+ ↑ | ... | Low Address +--------------------+ | buffer | +--------------------+ | ... | +--------------------+ | canary | +--------------------+ | saved rbp | +--------------------+ | ret addr | +--------------------+ | arg 1 | +--------------------+ | ... | High Address +--------------------+ ↓
SSPが有効になっているときは、Canaryがsaved rbp
とbuffer
の間にあることがわかります。この状態でbuffer
という変数がバッファオーバーフローを起こし、Canaryの値を変えてしまうと、プログラムが強制終了します。
gccではSSPはデフォルトで有効になっています。-fno-stack-protector
オプションをコンパイル時に指定すると、SSPを無効にすることができます。
NX bit
NX bit(No eXecute bit)はメモリ領域に置かれたデータをプログラムとして実行できなくする役割を果たします。WindowsではDEP(Data Execution Prevention)と呼ばれます。
NX bitが有効になっているとシェルコードを使って攻撃するのが困難になります。
gccではデフォルトでNX bitが有効になっています。無効にするときは、gccでは-z execstack
というオプションを付けてコンパイルすると、NX bitを無効にすることができます。
ASLR
ASLR(Address Space Layout Randomize)は日本語に訳すと「アドレス空間のランダム配置」という意味になります。
ASLRが有効になっていると、実行ファイルのスタックやヒープ、ライブラリをメモリに配置するときに、アドレスの一部をランダムに配置するようになります。これによって、攻撃者がアドレスを推測するのを困難にする役割を果たします。
スタック領域やヒープ領域、共有ライブラリのアドレスは、
cat /proc/<process ID>/maps
で確認することができます。なので、起動したら入力を受け付けて、それを表示するだけのプログラムを作成し、実際に起動するたびにアドレスが変わっているか確認してみました。
以下はASLRが無効になっているときのスタック領域とヒープ領域のアドレスです。ASLRが無効になっているときは常に以下のアドレスを出力しました。
555555559000-55555557a000 [heap] 7ffffffde000-7ffffffff000 [stack]
cat /proc/<process ID>/maps
を実行すると、本来はアドレスだけではなくパーミッションなども一緒に出力されるのですが、今回注目するのはアドレスだけなのでアドレスの部分だけ抜き出しています。
ASLRを有効にすると次のようになりました。
1回目 562ef2635000-562ef2656000 [heap] 7ffdd3881000-7ffdd38a2000 [stack] 2回目 55d1f63f6000-55d1f6417000 [heap] 7fff6e4e2000-7fff6e503000 [stack]
1回目と2回目で異なるアドレスが表示されていることが分かります。
Ubuntuでは以下のコマンドを入力するとASLRを無効にすることができます。
sudo sysctl kernel.randomize_va_space=0
ALSRを有効に戻すときは以下のコマンドを実行します。
sudo sysctl kernel.randomize_va_space=2
PIE
PIE(Position Independent Executable)は実行コード内のアドレス参照をすべて相対アドレスで行うことで、実行ファイルがメモリ上のどの位置に置かれても、正常に動作実行できるように コンパイルされた実行ファイルのことを指します。
PIEが有効になっていると、特定のアドレスを突く攻撃を成功させることが困難になります。
参考リンク
間違った情報があれば修正します。