このエントリでは、安全確保支援士試験の対策として、情報処理技術者試験の過去問を解説します。
過去問を解きながら、知識のインプットができるようにしたつもりです。問題に取り組んでいただき、その後、本エントリを見ていただければと思います。
時間がなければ、問題を見ながら、さらっと読んでいただければよいと思います。さらに時間がなければ、絵だけ見ながら、流せばよいと思います。
さて、今回は、BOF(バッファオーバフロー)編 第二弾。「2014 年度(平成 26 年度)秋期 情報セキュリティスペシャリスト 午後 1 問 1」を解いていきます。2007 年度に比べると、難易度が上がっているように感じました。
難易度が上がれば、他の受験生の出来も悪くなるので、過度に心配する必要はないです。
《問題》
情報処理技術者試験のページからダウンロードしておいてください
《凡例》
|
それでは、午後 1 の 問 1 を、解き進めましょう。
前回同様、まずは、設問をながめます。
選択式の穴埋め、フリーの穴埋め。
記述問題 20 字 ~ 40 字(これだと、1 ~ 2 トピックしか入りません。フリーの穴埋めプラスアルファくらいのイメージです)。
問題も眺めます。四角 a ~ d の前後、下線部 ① ~ ⑤ を確認します。
テーマは BOF で、設問 2 はガチ。設問 3 には、IPS と WAF が登場 → なので、純粋な BOF の話ではなさそう。設問 4 は、BOF ではなさそうです。
頭から、設問 1 a からいきましょう。3 ページの上の方です。
すぐに、「パッチ」 とか 「修正プログラム」 などが思いつかないと、ヤバいです。ほぼすべての受験生がとってきます。
「[ a ] の適用」 が、「ソフトウェアの更新」 と並列になっているので、解答に 「セキュリティ」 という文言は入れたいです。
なので、答えは 「セキュリティパッチ」。これしかないんじゃないかなと思います。
設問 1 の b です。3 ページ真ん中らへんです。
スタックバッファオーバフロー攻撃ときたら、だいたい、「ヒープ」 バッファオーバフロー攻撃ですw
これも、他の受験生は、間違いなくとってきます。
設問 1 の d です。5 ページの真ん中よりやや上です。
で、私は、知らんかったです(チーン)。
ちょいと調べたところ、かなり有名な攻撃名であるため、名前、覚えておきます。
どのような攻撃なのかわからない、という方は、設問 2 の解説を見た後に、戻ってくるとよいです。
図1 のコードは、問題を解く際には、あまり関係ないのですが、一応、触れておきます。
この Vuln は、スタック BOF に脆弱な、ド典型のコードです。 後で見ますが、スタック領域には、関数の引数とローカル変数が格納されています。もちろん b も c も、スタック領域上にあります。 そして b は、巡り巡って、プログラム Vuln の第一引数の値を指し示します(オレンジ色の箇所です)。 しかし、この 11 行目、strcpy 関数は、バッファの上限のバイト数を指定できません。さらに、Vuln では、コピー元のデータが、確保したバッファ char c[24]; 内におさまるかどうかのチェックもしていません。 まとめますと、このスタック領域上の b に、巡り巡ってやってきたプログラムの引数 b に、スタック領域に確保した char c[24]; より長いデータを与えると、char c[24]; がオーバフローします。 ちなみに、strcpy 関数以外にも、strcat、sprint などの関数があります。表に示した関数くらいは、把握しておきたいです。 |
設問 2 の (1) です。図 2 を見ると、実質 5 択であることがわかります。
スタック BOF では、攻撃者がスタック領域に 「注入した」 shell コードが実行されます。この後、説明します。
通常のコードでは、事前に、テキスト領域に展開された命令が実行されます。ですので、この場合は、「スタック領域にあるコードは実行しない」とすればよいです。
解答は、そのまま、「スタック領域に適用すればよい」 となります。まんまなので、少し勇気いります?
いよいよ(?)、設問 2 (2) です。
「リトルエンディアンってなんだっけ?」 という人は、すぐにとばしましょう。この 1 問で合否が決まるわけではありません。
「リトルエンディアン」 の説明の前に、まずは、「レジスタ」 について復習します。
レジスタとは、CPU の記憶回路のことです。CPU 内にあるメモリのイメージです。
左側の表を見てください。これは、Intel が作った CPU である、x86(今や、32 bit アーキテクチャを指すことの方が多い?)から、ピックアップしてきたものです。レジスタは、色々な用途に使われています。ちなみに、x86 のレジスタは、全部で 16 あるそうです。
で、「リトルエンディアン」 ですが、これは、バイトの順序を表すタームです。
右の絵を見てください。今、レジスタ上に、C8, 04, 80, 26 というデータが格納されているとします。これを、メモリ上に持ってきたときにはどうなるかというと、逆さまに格納されます。つまり、26, 80, 04, C8 となります。
このように、バイトの順序が入れ替わる方式が、リトルエンディアンです。逆に、バイトの順序が入れ替わらない方式を、ビッグエンディアンと呼びます。
どちらがよいというわけではありません。ちなみに、x86 は、リトルエンディアン方式です。
ここから、しばらく、基本知識の整理をさせてください。関数を呼び出したときの、メモリ、レジスタの変化について、確認します。ガチで書いたので、結構、長いです。なんとなく知っている方は、「# インプット ここまで ---」まで、すっ飛ばしてください。
# インプット ここから ---
① 各種レジスタの名前、② リターンアドレス(ret)、③ ebp レジスタ保存値(sfp)などの用語になじみがないと、説明が難しく感じるかもしれません。が、気のせいです。単に慣れていないだけです。過去問を解く中で、慣れていってください。
こんな順で説明します。
- スタック領域 ~「スタック」のおさらい
- レジスタとメモリ ~ プログラム実行時のレジスタとメモリの様子
- スタックフレーム ~ スタックに PUSH / POP するデータのレイアウト
- 具体例 ~ プログラム実行時のレジスタとメモリの様子
main 関数実行時 --> foo 関数呼び出し時 --> foo 関数終了時
まずは、「スタック領域」から。
スタックというデータ構造は、みなさんご存じでしょう。「本」 が積まれているイメージです。新たに本を積むことを 「PUSH」 と呼び、本をとることを 「POP」 と呼びました。最初に積んだ本は、最後までとることができません(FILO)。最後に積んだ本が、最初にとれます(LIFO)。こんな特徴がありました。
「てか、このスタックの絵、逆さまぢゃね?」
はい。いつも見ているスタックのイメージと逆さまになっているので、少しわかりにくいです。問題中の、図 2 にあわせました。
続けます。「スタック領域」 は、「スタックフレーム」 という 「本」 を PUSH したり POP したりする領域になります。
ここで大事なことは、スタック領域は、高位のアドレスから、低位のアドレスに成長するということです。
逆さまになっていない図を使っている記事や書籍もあります(むしろ、そっちの方が多いと思います)。どうして逆さまになっていないかというと、低位のアドレスを上に、高位のアドレスを下に書いているからです。それだけです。
このスタック領域が、いつ使われるかというと、関数の呼び出し時です。関数を呼び出すときには、その関数が使う 「スタックフレーム」 なる 「本」 を作成し、スタックに PUSH します。関数を終えるとき、関数を呼び出した前の状態を復元して、「スタックフレーム」 なる 「本」 を、スタックから POP します。
次に、レジスタとメモリを使って、どのようにプログラムが実行されるのか、おさえましょう。
今回、用途をおさえておいてほしいレジスタは、「スタックポインタ(esp)」、「ベースポインタ(ebp)」、それから、「インストラクションポインタ(eip)」 の 3 つです。
左の絵を見てください。esp と ebp は、スタック領域を指し示します。
- esp には、スタックのトップのアドレスが格納されています。この値により、メモリを確保したり、開放したりしています
- ebp には、スタックフレームの 「ベース(基準)」 となる、アドレスが格納されています
右の絵を見てください。eip は、これらとは少し毛色が異なります。ここには、実行する命令のアドレスが格納されています。
eip が指す命令をとってきて(フェッチ)、解釈して(デコード)、実行する。すると、eip がカウントアップし、そこから、フェッチ、デコード、実行します。このようにして、プログラムが実行されていきます。
次に、スタック領域に積まれる 「本」 である 「スタックフレーム」 が、どのようなデータなのか、説明します。
スタックフレームのレイアウトは、問題文の図 2 に書いてあります。ちょっと見にくいので、簡潔に書きました。例えば、右側の絵のようなレイアウトになっています。
このスライドの、ちっちゃく描かれた 「スタック領域」 には、main 関数のスタックフレームがあり、その上に foo 関数のスタックフレームが積みあがっています。
スタックフレーム中の「リターンアドレス(ret)」と「ebp レジスタ保存値(sfp)」は、foo 関数が終了する際、foo 関数を 「呼び出す前の状態」 に復元するために使います。
それでは、関数が実行されるときのレジスタとメモリの様子を、具体的に見ていきましょう。丁寧に追っていきます。
この絵は、main 関数が実行されているときの、レジスタとメモリの様子です。
左側が、メモリのスタック領域、右側が、メモリのテキスト領域(プログラム Vuln の命令がある領域です)です。
まずは、簡単に、状況を把握しましょう。
- 左側にある、スタック領域を見てください。赤字の箇所がアドレスです。
main 関数のスタックフレームは、B 番地にあります。もちろん、ebp に格納されているアドレスは、B 番地です - 右側にある、テキスト領域を見てください。こちらも、赤字の箇所がアドレスです
eip は、X 番地を指しています。
この状態で、foo 関数が呼ばれると、どうなるかというと、 こうなります。
スタック領域に、foo 関数のスタックフレームが積まれました。つまり、esp が、新しいスタックの先頭を指しました。
ebp は、新しいスタックフレーム、C 番地を指し示しています。このとき、main 関数のスタックフレーム B 番地を見失わないように、sfp に退避しておきます。
eip は、カウントアップではなく、foo 関数の命令があるアドレスまでジャンプします。
このとき、呼び出し元である main 関数に戻ってこられないとまずいので、eip の値 X 番地(の次の番地)を、foo 関数のスタックフレームの ret に退避します。
そして、foo 関数の処理が終了しました。foo 関数のすべての命令が実行されました。
となると、foo 関数を呼び出す前に、戻らなければなりません。ebp が main 関数のスタックフレームを指すように、eip が main 関数の続きの命令を指すように、復元します。
これは、「foo 関数のスタックフレーム」に退避していましたよね。退避したリターンアドレス(X の次の番地)を eip に、sfp(B 番地)を ebp に戻します。
そして、foo 関数のスタックフレームを POP します。イコール、esp からスタックフレーム分のバイトを引いて、メモリ領域を開放します。
これで、foo 関数を呼び出す前の状態に戻り、X の次の番地から、フェッチ、デコード、実行です。
これで、インプットはお終いです。お疲れさまでした。
設問に戻りましょう。
# インプット ここまで ---
設問 2 の (2) です。
右の図は、図 2 を簡略化して書いたものです。図 2 って何だったかというと、関数 foo が呼び出された後のメモリ配置でした。
となると、このスタックフレームは、foo 関数のものです。では、foo 関数が終わったときに、どのような挙動になるかというと。
リターンアドレスを eip に入れます。そうすると、eip は、main 関数の続きの命令を指すようになります。そして、foo 関数のスタックフレームが POP されます。
ここで、eip が shell コードの先頭を指せば、shell コードの命令が、フェッチ、デコード、実行されてしまいます。
eip が shell コードの先頭を指すには、"リターンアドレスを" shell コードの先頭に指定すればよいでしょう。EIP の値が、C8 04 08 26 であればよいということです。
で、この CPU は、リトルエンディアン方式であるため、EIP の値を C8 04 08 26 にするには、メモリ上で、26 80 04 C8 とすればよいです。
ですので、答えは エ になります。
わかっている人は、「shell コードの先頭のアドレス C8 04 08 26 を EIP に入れるので、リターンアドレスが 26 80 04 C8 だったらいいよね。以上」 で、秒速で終了です。
次いきましょう。
設問 2、(3) です。5 ページの真ん中よりちょっと上、「アドレス空間配置ランダム化技術」 についてです。
いわゆる ASLR(アスラー)です。この名前は知っておきましょう。
設問 2 (2) がわかれば、セットでとれます。
この問題の事例では、攻撃者は、shell コードが、どのアドレスに展開されるかわかっていました。だからこそ、リターンアドレスを shell コードの先頭に指定できたのです。
『具体的に述べよ』 とあったので、私は、そのまま 「リターンアドレスを shell コードの先頭に指定する(行為をできないようにする)」 と解答しました。
これだと、「shell コードの先頭」 って何? となるかもしれません。正直、25 字でまとめるのは厳しかったです。
IPA の解答は、『攻撃を成功させるためのジャンプ先アドレスの特定』 です。特にコメントはありません。
設問 3 (1) です。3 ページの、下線部 ① です。
IPS とは、侵入防御システムのことです。4 層における不正なデータ(主に、OS への攻撃)を検知、破壊します。
WAF とは、Web アプリケーションファイアウォールの略です。7 層における不正なデータ(主に、アプリケーションへの攻撃)を検知、破壊します。
いずれも、あやしいと判断したデータについては、先に進めないようにします。
設問を見ると、また 『具体的に述べよ』 です。たった 25 字で、具体的に?
私は、「既知の攻撃を含むパケットを遮断する(処理)」 と解答しました。これは、ダメです。「アノマリ検知」 があることを忘れていました。あと、ぜんぜん具体的ではありません。
IPA の解答は、『インジェクションベクタを検知・破壊する』 となっています。
『インジェクションベクタ』、まんま使ってきました。本文中の言葉を使うと、2 つのメリットがあります。
- いちいち、その定義は何なのか、それが何を指すのか、説明しなくてよいため、コンパクトに収めることができる
- いっきに具体的になる
「そこまでわかっているなら、オマエ使えよ」、です。はい。ごめんなさい。
どんどんいきます。設問 3 (2) です。
私は、『あらゆる命令の実行が shell コードで可能となる』 = 「誰でも、どこからでも実行できる」 と、とらえました。
このうち、「どこからでも実行できる」 は、「shell コードの展開箇所によらない」 という意味です。これは、p.5 の冒頭にあります。なので、スルーしました。
というわけで、自分は、「権限によらず、あらゆる命令が実行できる(条件)」 としました。まずくはないと思います。
しかし、問題文中に、『ルート特権化』 という文言が、何度も繰り返し出てきています。そう、これを使えば、コンパクトにできるのです。そこででしょう。IPA の解答例は、『ルート特権があること』 となっています。
設問 4 の (1) です。
正直、何を答えてよいのか、わかりませんでした。仕方がないので、一般的なファイルシステムのアクセス制御について言及しました。こんな感じです。
「読出すファイルに、利用者やアプリからの参照権限がない場合、処理を中断する仕様」
めちゃめちゃ苦しいですね。でも、何も書かないと、100 %、0 点になりますから。。何か書きましょう。
スマホのアプリは、サンドボックス内で実行されています。サンドボックスとは、「砂場」 のことです。ある砂場から、他の砂場のリソースやデータには、アクセスできません。アクセスするには、明示的に許可が必要です。
これについて、答えてほしかったのでしょう。
設問 4 の (2) です。
この問題は、絵にある 2 箇所に着目できれば、それでおしまいです。多くの受験生がとってくると思います。
私は、本文中の言葉をできる限り使って、「データを盗み出すタイプのマルウェアに侵入された」(状況)としました。
ラスト、設問 4 の (3) です。
ポイントは、p.2 の最初の段落にあります。これは、少し見つけにくいです。国語力が試されます。
とはいえ、1 箇所見つければよいだけなので、多くの受験生がたどり着くと思います。書き負けないでください。
少なくとも、スマホの管理に M システムを使っていることは、たどり着いてください。
それでは、これまで見てきたこと、本問で得た知識を、簡単に復習しましょう。
な感じですかね。
総括です。
よく出題される、重要な技術、受験生の正解しやすさなどを鑑み、独断と偏見で、問題ごとに、ランクをつけてみました("A" ~ "C")
- "A" は、絶対にとりたいです
- "B" が半分くらい取れていれば、それで十分合格ラインを超えるという感覚です。全部とれれば、上位合格だと思います
- "C" は、時間かけてもできませんでした。設問 4 (1) なんて、ムリぢゃね?
めちゃめちゃ長文になってしまいました。最後まで読んでいただき、ありがとうございました。