セキュリティホール#
はじめに#
まず最初に十分理解していいただきたいのは,以下の実験内容は教育目的であって絶対に悪用しないでください.
インターネット上には,ポートスキャン方法やクラッキング方法など様々な情報が氾濫していますが,本実験環境のローカルネットワーク内における実験用サーバに対してのみ試してみて構いませんが,そのからくりを理解できていない人は周囲に迷惑をかける可能性もありますので十分注意して実施してください.外部サーバや他人のPCに対して試してみるということは絶対にしないでください.
学内のトラフィックは,情報基盤センターのツールで監視されています.皆さんはコンピュータネットワークの授業で,通信の仕組み(接続元IPアドレス・ポート番号,プロトコル種別,接続先IPアドレス・ポート番号の5つの情報で通信フローは識別可能)を学んでいるわけですので,どのような仕組みで監視できるのか認識しているものと思いますが,意図せぬウィルス感染や不正アクセスでも情報基盤センターから連絡は来ますので,十分注意して以下の課題に取り組んでください.皆さんのノートPCのMACアドレスは入学時に学内DBへ登録されているはずです.実験機材のMACアドレスもこちらで把握しています.また,学内DHCP(無線であろうが有線であろうが)でIPアドレスをもらっている場合,どのMACアドレスへいつからいつまでどのIPアドレスを割り振ったかログに記録されてます.学内にはネットワーク機器の運用規則がありますので,遵守するとともに敢えて抜け道を探すようなことは絶対にしないでください.もし抜け道に気づいてしまった場合は,真摯に教えてください.将来のネットワークエンジニアの卵として良い方向での成長を楽しみにしたいと思います.
また,もし何か意図せぬことが発生しているような気がする場合は,「学内ネットへ接続しているルータWAN側のLANケーブルを抜く」ということでお願いします. (自宅で実施している場合は,インターネットに接続せずに実施することを心がけてください)
よく,「ホームページが改竄(かいざん)された」というニュースや「セキュリティホールが見つかったのでソフトウェアアップデートしましょう」というような周知があるかと思いますが,これまでに実装したSimple HTTPサーバにもセキュリティホールがあります(というかあまり深く考えずにネットワークプログラムを開発すると,セキュリティホールだらけになりますので注意してください).
ここでは,セキュリティホールの基本であるバッファオーバフローを利用した攻撃方法について理解し,実際に以下のステップを実験用端末で試してみてセキュリティホールを実感してください.
[必須課題]セグメンテーションフォルト
[必須課題]脆弱性のあるプログラムの実行
[発展課題]バッファオーバーフロー
[発展課題]バッファオーバーランで任意のコマンドを実行
[発展課題]バッファオーバーランでリモートからシェルに接続
上から順番に少しずつ攻撃を高度化したものになります.つまり,一番下に示すリモートサーバのシェルに接続できれば,ホームページの改竄もできてしまいます.
ここで,昨今のOSはデフォルトでは以下の対策が施されていますので,今回は敢えて体験できるよう以下の設定を解除する場合があります.その他にも最新のセキュリティ対策が施されている場合がありますので,状況に応じて解除して実施してください.ただし,レポートにはその旨をしっかり記述し,読み手が同様の実験結果を再現するのに必要な情報をしっかり記述してください(レポート内容が正しいか(コピペや虚偽でないか),こちらでも試して確認する場合がありますので).
アドレス空間配置のランダム化(ASLR: Address Space Layout Randomization)を実験のために無効化:
$ sudo sysctl -w kernel.randomize\_va\_space=0
データ実行防止 (DEP: Data Execution Prevention)とスタック保護(SSP: stack-smashing protection)を実験のために無効化:
$ gcc -z execstack -fno-stack-protector xxxx.c
[必須課題] セグメンテーションフォルト#
下記プログラムにおいて,セグメンテーションフォルトが発生する理由について考察してください. また,セグメンテーションフォルトとは何か説明してください
#include <stdio.h>
#include <stdint.h>
#include <string.h>
void test_func(char *msg)
{
uint32_t y = 0x88776655;
char buf[16];
uint32_t z = 0xccbbaa99;
int i, j;
uint32_t base = 0;
printf("0x%016x: buf\n", buf);
printf("0x%016x: &y\n", &y);
printf("0x%016x: msg\n", msg);
printf("0x%016x: &z\n", &z);
printf("\n");
printf("start dump memory\n");
base = (uint32_t)&z;
for(i = 0; i < 18; i++){
printf("0x%016x: ", base);
for(j = 0; j < 8; j++){
uint8_t *p = (uint8_t*) base;
printf("%02x", *(p + j));
}
printf("\n");
base += 8;
}
printf("end dump memory\n");
printf("\n");
strcpy(buf, msg);
}
int main()
{
uint32_t x = 0x44332211;
char data[] = "abcd" "efgh" "ijkl" "mnop" "qrst" "uvwx"
"abcd" "efgh" "ijkl" "mnop" "qrst" "uvwx";
printf("0x%016x: data\n", data);
printf("0x%016x: &x\n", &x);
printf("0x%016x: main\n", main);
test_func(data);
}
余裕のある人は上記の条件下でもセグメンテーションフォルトが発生しないようにプログラムを変更してみましょう.
[必須課題] 脆弱性のあるプログラムの実行#
以下に示す上記プログラムの実行結果について,どのような動作の結果なのか説明してください.
0x00000000ffffe5d0: data
0x00000000ffffe60c: &x
0x00000000004006e8: main
0x00000000ffffe590: buf
0x00000000ffffe5a4: &y
0x00000000ffffe5d0: msg
0x00000000ffffe58c: &z
start dump memory
0x00000000ffffe58c: 99aabbccd0e5ffff
0x00000000ffffe594: ff7f0000d0e5ffff
0x00000000ffffe59c: ff7f00000f094000
0x00000000ffffe5a4: 5566778803000000
0x00000000ffffe5ac: 00000000ace5ffff
0x00000000ffffe5b4: ff7f0000b4e5ffff
0x00000000ffffe5bc: ff7f000010e6ffff
0x00000000ffffe5c4: ff7f0000a4074000
0x00000000ffffe5cc: 0000000061626364
0x00000000ffffe5d4: 65666768696a6b6c
0x00000000ffffe5dc: 6d6e6f7071727374
0x00000000ffffe5e4: 7576777861626364
0x00000000ffffe5ec: 65666768696a6b6c
0x00000000ffffe5f4: 6d6e6f7071727374
0x00000000ffffe5fc: 7576777800e6ffff
0x00000000ffffe604: ff7f000000000000
0x00000000ffffe60c: 1122334400000000
0x00000000ffffe614: 000000005ded2114
end dump memory
Segmentation fault (core dumped)
皆さんの実装したプログラムがどのようにメモリ上に展開され実行されているか理解できると様々な意図せぬ動作の解決に役立ちます.ちなみに,上記の出力は下記のようにしてASLRを解除した場合の出力となります.
$ sudo sysctl -w kernel.randomize_va_space=0
[発展課題]バッファオーバーフロー#
プログラムが管理しているメモリ上に確保するバッファ領域に対して,脆弱性のある実装をしているとバッファ領域の上限を超えた部分に情報を格納(バッファオーバーフロー)してしまうことがあります.これによりプログラムが意図した通りに動作しなくなってしまう場合があります.詳細は「バッファオーバーフロー」で検索して調べてみてください.
Simple HTTPサーバにはこの脆弱性がありますので,クライアント側プログラムを工夫することで,バッファオーバーフローを生じさせSimple HTTPサーバを停止させる攻撃ができます.Simple HTTPサーバプログラムのどこに脆弱性があり,その結果,どのようにクライアント側プログラムを工夫するとバッファオーバーフローが生じるのか検討し,実際に試してみてください.レポートには以下の項目を記述してください.
攻撃用のクライアント側ソースコード(該当部のみでよい)
Simple HTTPサーバが停止していることの分かるスクリーンショット
上記の解説
対策用のサーバ側ソースコード(該当部のみでよい)
Simple HTTPサーバが停止しなくなったことの分かるスクリーンショット
上記の解説
[発展課題]バッファオーバーランで任意のコマンドを実行#
バッファオーバーフローを利用して,サーバ側でシェルを起動できれば任意のコマンドを実行させることができるようになります.例えば以下のようなサイトもありますので,参考にして実現してみてください.要約すると,次のようなデータを送り込めばよいことになります.gdbを使用してメモリ上の値を分析しながら実施すると具体的なイメージがわくと思います(ちなみにSSP (Stack-Smashing Protection)も無効にしておきましょう).サーバ側でシェルを起動できると,サーバプログラムを実行した端末画面で任意のコマンドを入力し実行できるようになります(サーバ側の端末画面で入力できてもあまり意味ないですが).
シェルコード(シェルを起動するコード)を作成する
データ中にシェルコードを含める
バッファ長の適切な位置にシェルコードの先頭アドレスを入れておく
参考URL
[発展課題]バッファオーバーランでリモートからシェルに接続#
上記の任意のコマンド実行できるようになったらいよいよあと一息です.クライアント側で入力したコマンドをサーバへ送信&実行させ,そのサーバ側での実行結果の出力をクライアント側へ送信&表示させるように実装すれば,リモートからサーバ上の任意のコマンドを実行し好き放題できるようになります(もちろん通常は実行したコマンドがログとして残りますので,世の中のよくできたクラックツールは様々な工夫が施されておりますし,いたちごっこですのでサイバー攻防の発展は凄まじいですが).例えば,サーバ側で実行させるシェルコードで,ディスクリプタを複製できるdup2()システムコールを実行させてaccept()で受け付けたソケットのディスクリプタを標準入出力へ複製してからexecve()するようにしておくといった方法があります.
参考URL
おわりに#
アドレス空間のランダム化は実験が終了次第元の設定に戻しておきましょう.sysctl -w kernel.randomize\_va\_space=2
以上のようにセキュリティホールに対するサイバー攻防の発展は凄まじいですが,十分理解していいただきたいのは,この実験内容は教育目的ですので倫理観を持って絶対に悪用しないでください.
なお,静岡大学では,enPiT Basic SecCap 夏季特別講義として本内容を高度化した「サイバー攻防基礎演習」を行う予定です.余裕がある方は下記内容を先取りして取り組んでくれても構いません.また,今回の実験にも役にたつ情報が多く記載されていますので,ご参考にしてください.