安全確保支援士対策 BOF 編 [2]

このエントリでは、安全確保支援士試験の対策として、情報処理技術者試験の過去問を解説します。

過去問を解きながら、知識のインプットができるようにしたつもりです。問題に取り組んでいただき、その後、本エントリを見ていただければと思います。

時間がなければ、問題を見ながら、さらっと読んでいただければよいと思います。さらに時間がなければ、絵だけ見ながら、流せばよいと思います。

 

さて、今回は、BOF(バッファオーバフロー)編 第二弾。「2014 年度(平成 26 年度)秋期 情報セキュリティスペシャリスト 午後 1 問 1」を解いていきます。2007 年度に比べると、難易度が上がっているように感じました。

難易度が上がれば、他の受験生の出来も悪くなるので、過度に心配する必要はないです。

 

《問題》

    情報処理技術者試験のページからダウンロードしておいてください

《凡例》

  • 『・・・』は、問題文からの引用を表します
  • (解答)は、IPA が発表した解答例を表します

 

 

それでは、午後 1 の 問 1 を、解き進めましょう。

前回同様、まずは、設問をながめます。

f:id:higher_tomorrow:20200216061402p:plain

選択式の穴埋め、フリーの穴埋め。
記述問題 20 字 ~ 40 字(これだと、1 ~ 2 トピックしか入りません。フリーの穴埋めプラスアルファくらいのイメージです)。

問題も眺めます。四角 a ~ d の前後、下線部 ① ~ ⑤ を確認します。
テーマは BOF で、設問 2 はガチ。設問 3 には、IPS と WAF が登場 → なので、純粋な BOF の話ではなさそう。設問 4 は、BOF ではなさそうです。

 

頭から、設問 1 a からいきましょう。3 ページの上の方です。

f:id:higher_tomorrow:20200216061405p:plain

すぐに、「パッチ」 とか 「修正プログラム」 などが思いつかないと、ヤバいです。ほぼすべての受験生がとってきます。

「[  a  ] の適用」 が、「ソフトウェアの更新」 と並列になっているので、解答に 「セキュリティ」 という文言は入れたいです。
なので、答えは 「セキュリティパッチ」。これしかないんじゃないかなと思います。

 

設問 1 の b です。3 ページ真ん中らへんです。

f:id:higher_tomorrow:20200216061410p:plain

スタックバッファオーバフロー攻撃ときたら、だいたい、「ヒープ」 バッファオーバフロー攻撃ですw

これも、他の受験生は、間違いなくとってきます。

 

設問 1 の d です。5 ページの真ん中よりやや上です。

f:id:higher_tomorrow:20200216061413p:plain

で、私は、知らんかったです(チーン)。

ちょいと調べたところ、かなり有名な攻撃名であるため、名前、覚えておきます。

 

どのような攻撃なのかわからない、という方は、設問 2 の解説を見た後に、戻ってくるとよいです。

 

図1 のコードは、問題を解く際には、あまり関係ないのですが、一応、触れておきます。

f:id:higher_tomorrow:20200216061418p:plain

この 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 択であることがわかります。

f:id:higher_tomorrow:20200216061422p:plain

スタック BOF では、攻撃者がスタック領域に 「注入した」 shell コードが実行されます。この後、説明します。

通常のコードでは、事前に、テキスト領域に展開された命令が実行されます。ですので、この場合は、「スタック領域にあるコードは実行しない」とすればよいです。

解答は、そのまま、「スタック領域に適用すればよい」 となります。まんまなので、少し勇気いります?

 

いよいよ(?)、設問 2 (2) です。

f:id:higher_tomorrow:20200216061425p:plain

「リトルエンディアンってなんだっけ?」 という人は、すぐにとばしましょう。この 1 問で合否が決まるわけではありません。

 

「リトルエンディアン」 の説明の前に、まずは、「レジスタ」 について復習します。

レジスタとは、CPU の記憶回路のことです。CPU 内にあるメモリのイメージです。

左側の表を見てください。これは、Intel が作った CPU である、x86(今や、32 bit アーキテクチャを指すことの方が多い?)から、ピックアップしてきたものです。レジスタは、色々な用途に使われています。ちなみに、x86 のレジスタは、全部で 16 あるそうです。

で、「リトルエンディアン」 ですが、これは、バイトの順序を表すタームです。

右の絵を見てください。今、レジスタ上に、C8, 04, 80, 26 というデータが格納されているとします。これを、メモリ上に持ってきたときにはどうなるかというと、逆さまに格納されます。つまり、26, 80, 04, C8 となります。

このように、バイトの順序が入れ替わる方式が、リトルエンディアンです。逆に、バイトの順序が入れ替わらない方式を、ビッグエンディアンと呼びます。

 

どちらがよいというわけではありません。ちなみに、x86 は、リトルエンディアン方式です。

 

ここから、しばらく、基本知識の整理をさせてください。関数を呼び出したときの、メモリ、レジスタの変化について、確認します。ガチで書いたので、結構、長いです。なんとなく知っている方は、「# インプット ここまで ---」まで、すっ飛ばしてください

# インプット ここから ---

① 各種レジスタの名前、② リターンアドレス(ret)、③ ebp レジスタ保存値(sfp)などの用語になじみがないと、説明が難しく感じるかもしれません。が、気のせいです。単に慣れていないだけです。過去問を解く中で、慣れていってください。

こんな順で説明します。

  1. スタック領域 ~「スタック」のおさらい
  2. レジスタとメモリ ~ プログラム実行時のレジスタとメモリの様子
  3. スタックフレーム ~ スタックに PUSH / POP するデータのレイアウト
  4. 具体例 ~ プログラム実行時のレジスタとメモリの様子
    main 関数実行時 --> foo 関数呼び出し時 --> foo 関数終了時

 

まずは、「スタック領域」から。

f:id:higher_tomorrow:20200216061430p:plain

スタックというデータ構造は、みなさんご存じでしょう。「本」 が積まれているイメージです。新たに本を積むことを 「PUSH」 と呼び、本をとることを 「POP」 と呼びました。最初に積んだ本は、最後までとることができません(FILO)。最後に積んだ本が、最初にとれます(LIFO)。こんな特徴がありました。

 

「てか、このスタックの絵、逆さまぢゃね?」

 

はい。いつも見ているスタックのイメージと逆さまになっているので、少しわかりにくいです。問題中の、図 2 にあわせました。

続けます。「スタック領域」 は、「スタックフレーム」 という 「本」 を PUSH したり POP したりする領域になります。

ここで大事なことは、スタック領域は、高位のアドレスから、低位のアドレスに成長するということです。

逆さまになっていない図を使っている記事や書籍もあります(むしろ、そっちの方が多いと思います)。どうして逆さまになっていないかというと、低位のアドレスを上に、高位のアドレスを下に書いているからです。それだけです。

 

このスタック領域が、いつ使われるかというと、関数の呼び出し時です。関数を呼び出すときには、その関数が使う 「スタックフレーム」 なる 「本」 を作成し、スタックに PUSH します。関数を終えるとき、関数を呼び出した前の状態を復元して、「スタックフレーム」 なる 「本」 を、スタックから POP します。

 

次に、レジスタとメモリを使って、どのようにプログラムが実行されるのか、おさえましょう。

f:id:higher_tomorrow:20200216061434p:plain

今回、用途をおさえておいてほしいレジスタは、「スタックポインタ(esp)」、「ベースポインタ(ebp)」、それから、「インストラクションポインタ(eip)」 の 3 つです。

 

左の絵を見てください。esp と ebp は、スタック領域を指し示します。

  • esp には、スタックのトップのアドレスが格納されています。この値により、メモリを確保したり、開放したりしています
  • ebp には、スタックフレームの 「ベース(基準)」 となる、アドレスが格納されています

 

右の絵を見てください。eip は、これらとは少し毛色が異なります。ここには、実行する命令のアドレスが格納されています。

 

eip が指す命令をとってきて(フェッチ)、解釈して(デコード)、実行する。すると、eip がカウントアップし、そこから、フェッチ、デコード、実行します。このようにして、プログラムが実行されていきます。

 

次に、スタック領域に積まれる 「本」 である 「スタックフレーム」 が、どのようなデータなのか、説明します。

f:id:higher_tomorrow:20200216061438p:plain

スタックフレームのレイアウトは、問題文の図 2 に書いてあります。ちょっと見にくいので、簡潔に書きました。例えば、右側の絵のようなレイアウトになっています。

 

このスライドの、ちっちゃく描かれた 「スタック領域」 には、main 関数のスタックフレームがあり、その上に foo 関数のスタックフレームが積みあがっています。

 

 

スタックフレーム中の「リターンアドレス(ret)」と「ebp レジスタ保存値(sfp)」は、foo 関数が終了する際、foo 関数を 「呼び出す前の状態」 に復元するために使います。

 

それでは、関数が実行されるときのレジスタとメモリの様子を、具体的に見ていきましょう。丁寧に追っていきます。

f:id:higher_tomorrow:20200216061442p:plain

この絵は、main 関数が実行されているときの、レジスタとメモリの様子です。

左側が、メモリのスタック領域、右側が、メモリのテキスト領域(プログラム Vuln の命令がある領域です)です。

まずは、簡単に、状況を把握しましょう。

  • 左側にある、スタック領域を見てください。赤字の箇所がアドレスです。
    main 関数のスタックフレームは、B 番地にあります。もちろん、ebp に格納されているアドレスは、B 番地です
  • 右側にある、テキスト領域を見てください。こちらも、赤字の箇所がアドレスです

eip は、X 番地を指しています。

 

この状態で、foo 関数が呼ばれると、どうなるかというと、 こうなります。

f:id:higher_tomorrow:20200216061447p:plain

スタック領域に、foo 関数のスタックフレームが積まれました。つまり、esp が、新しいスタックの先頭を指しました。

 

ebp は、新しいスタックフレーム、C 番地を指し示しています。このとき、main 関数のスタックフレーム B 番地を見失わないように、sfp に退避しておきます。

 

eip は、カウントアップではなく、foo 関数の命令があるアドレスまでジャンプします。

このとき、呼び出し元である main 関数に戻ってこられないとまずいので、eip の値 X 番地(の次の番地)を、foo 関数のスタックフレームの ret に退避します。

 

そして、foo 関数の処理が終了しました。foo 関数のすべての命令が実行されました。

f:id:higher_tomorrow:20200216061451p:plain

となると、foo 関数を呼び出す前に、戻らなければなりません。ebp が main 関数のスタックフレームを指すように、eip が main 関数の続きの命令を指すように、復元します。

 

これは、「foo 関数のスタックフレーム」に退避していましたよね。退避したリターンアドレス(X の次の番地)を eip に、sfp(B 番地)を ebp に戻します。

そして、foo 関数のスタックフレームを POP します。イコール、esp からスタックフレーム分のバイトを引いて、メモリ領域を開放します。

 

これで、foo 関数を呼び出す前の状態に戻り、X の次の番地から、フェッチ、デコード、実行です。

 

これで、インプットはお終いです。お疲れさまでした。

設問に戻りましょう。

# インプット ここまで ---

 

設問 2 の (2) です。

f:id:higher_tomorrow:20200216061456p:plain

右の図は、図 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 ページの真ん中よりちょっと上、「アドレス空間配置ランダム化技術」 についてです。

f:id:higher_tomorrow:20200216061501p:plain

いわゆる ASLR(アスラー)です。この名前は知っておきましょう。

設問 2 (2) がわかれば、セットでとれます。

この問題の事例では、攻撃者は、shell コードが、どのアドレスに展開されるかわかっていました。だからこそ、リターンアドレスを shell コードの先頭に指定できたのです。

 

『具体的に述べよ』 とあったので、私は、そのまま 「リターンアドレスを shell コードの先頭に指定する(行為をできないようにする)」 と解答しました。

これだと、「shell コードの先頭」 って何? となるかもしれません。正直、25 字でまとめるのは厳しかったです。

 

IPA の解答は、『攻撃を成功させるためのジャンプ先アドレスの特定』 です。特にコメントはありません。

 

設問 3 (1) です。3 ページの、下線部 ① です。

f:id:higher_tomorrow:20200216061505p:plain

IPS とは、侵入防御システムのことです。4 層における不正なデータ(主に、OS への攻撃)を検知、破壊します。

WAF とは、Web アプリケーションファイアウォールの略です。7 層における不正なデータ(主に、アプリケーションへの攻撃)を検知、破壊します。

 

いずれも、あやしいと判断したデータについては、先に進めないようにします。

 

設問を見ると、また 『具体的に述べよ』 です。たった 25 字で、具体的に?

私は、「既知の攻撃を含むパケットを遮断する(処理)」 と解答しました。これは、ダメです。「アノマリ検知」 があることを忘れていました。あと、ぜんぜん具体的ではありません。

IPA の解答は、『インジェクションベクタを検知・破壊する』 となっています。

『インジェクションベクタ』、まんま使ってきました。本文中の言葉を使うと、2 つのメリットがあります。

  • いちいち、その定義は何なのか、それが何を指すのか、説明しなくてよいため、コンパクトに収めることができる
  • いっきに具体的になる

 

「そこまでわかっているなら、オマエ使えよ」、です。はい。ごめんなさい。

 

どんどんいきます。設問 3 (2) です。

f:id:higher_tomorrow:20200216061510p:plain

私は、『あらゆる命令の実行が shell コードで可能となる』 = 「誰でも、どこからでも実行できる」 と、とらえました。

このうち、「どこからでも実行できる」 は、「shell コードの展開箇所によらない」 という意味です。これは、p.5 の冒頭にあります。なので、スルーしました。

というわけで、自分は、「権限によらず、あらゆる命令が実行できる(条件)」 としました。まずくはないと思います。

 

しかし、問題文中に、『ルート特権化』 という文言が、何度も繰り返し出てきています。そう、これを使えば、コンパクトにできるのです。そこででしょう。IPA の解答例は、『ルート特権があること』 となっています。

 

設問 4 の (1) です。 

f:id:higher_tomorrow:20200216061514p:plain

正直、何を答えてよいのか、わかりませんでした。仕方がないので、一般的なファイルシステムのアクセス制御について言及しました。こんな感じです。

「読出すファイルに、利用者やアプリからの参照権限がない場合、処理を中断する仕様」

めちゃめちゃ苦しいですね。でも、何も書かないと、100 %、0 点になりますから。。何か書きましょう。

 

スマホのアプリは、サンドボックス内で実行されています。サンドボックスとは、「砂場」 のことです。ある砂場から、他の砂場のリソースやデータには、アクセスできません。アクセスするには、明示的に許可が必要です。

これについて、答えてほしかったのでしょう。

 

設問 4 の (2) です。

f:id:higher_tomorrow:20200216061519p:plain

この問題は、絵にある 2 箇所に着目できれば、それでおしまいです。多くの受験生がとってくると思います。

私は、本文中の言葉をできる限り使って、「データを盗み出すタイプのマルウェアに侵入された」(状況)としました。

 

ラスト、設問 4 の (3) です。

f:id:higher_tomorrow:20200216061523p:plain

ポイントは、p.2 の最初の段落にあります。これは、少し見つけにくいです。国語力が試されます。

とはいえ、1 箇所見つければよいだけなので、多くの受験生がたどり着くと思います。書き負けないでください。

少なくとも、スマホの管理に M システムを使っていることは、たどり着いてください。

 

 

それでは、これまで見てきたこと、本問で得た知識を、簡単に復習しましょう。

f:id:higher_tomorrow:20200216061530p:plain

f:id:higher_tomorrow:20200216061534p:plain

な感じですかね。

 

総括です。

f:id:higher_tomorrow:20200216061539p:plain

よく出題される、重要な技術、受験生の正解しやすさなどを鑑み、独断と偏見で、問題ごとに、ランクをつけてみました("A" ~ "C")

  • "A" は、絶対にとりたいです
  • "B" が半分くらい取れていれば、それで十分合格ラインを超えるという感覚です。全部とれれば、上位合格だと思います
  • "C" は、時間かけてもできませんでした。設問 4 (1) なんて、ムリぢゃね?

 

めちゃめちゃ長文になってしまいました。最後まで読んでいただき、ありがとうございました。