こんにちは、あし です。
このエントリでは、安全確保支援士試験の対策として、情報処理技術者試験の過去問を解説します。
過去問を解きながら、知識のインプットができるようにしたつもりです。問題に取り組んでいただき、その後、本エントリを見ていただければと思います。
時間がなければ、問題を見ながら、さらっと読んでいただければよいと思います。さらに時間がなければ、絵だけ見ながら、流せばよいと思います。
さて、今回は、BOF(バッファオーバフロー)編 第三弾。「2016 年度(平成 28 年度)秋期 情報セキュリティスペシャリスト 午後 1 問 2」を解いていきます。
《問題》
情報処理技術者試験のページからダウンロードしておいてください
《凡例》
|
それでは、午後 1 の 問 2 を、解き進めましょう。
前回同様、まずは、設問をながめます。
ざっと、こんなことがつかめれば、十分だと思います。
- 選択式の穴埋め a ~ d → 選択肢が 4 つしかない → サービス問題?
- 設問 2 とは、ガチで BOF っぽい
- 設問 3 は、変な文字列選択するようになっているし、修正用のコードもある、もっとガチかも
- 設問 4 は、BOF の対策っぽい
頭から、設問 1 a からいきましょう。6 ページの真ん中らへんです。
速攻で、CVE-ID → CVE 識別子です。出血、大サービス問題です
↓ このあたりをおさえている人が多いと思います
- 脆弱性情報のデータベースの 1 つ
- 各ベンダーが公表する脆弱性情報がバラバラだったので、それらをまとめて、共通したユニークな識別番号をつけた
- アメリカの MITRE(マイター)社が運営している
設問 1 b と c です。
これらは、もしや 「マグネット売場」 なのでは?
もとい。
ここに挙げられた、「ゼロデイ攻撃 + 標的型攻撃」 には、高い確率でヤラレると思います。出題の裏には、相当な危機感があると思われます。
本問を選択したほぼ全ての受験生が正解したでしょうね。
設問 1 d です。
ファジング(Fuzzing)とは、ファズ(fuzz)と呼ばれる、問題を引き起こしそうなデータを大量に送り込み、その応答や挙動を監視することで、脆弱性を検出する検査手法になります。
ちょっと脱線しますが、ここに出てくる 「静的解析ツール」 とは、ソースコードの段階で、脆弱性を含む不具合を検知してくれるツールです。
特に、セキュリティ面を重視した静的解析を、SAST(Static Application Security Testing)と呼んだりします。これは、DevSecOps(セキュリティを維持したまま、リリースの頻度を高めること)を実現するための、重要な要素となってきています。このあたり、SE っぽいでしょ?
戻ります。
設問 2 に入ります。
また、この問題ですか。何度でも、きいてきますね。
『関数呼び出し時にスタックに必ず積まれる』 は、スタックフレームです。
『何の値を書き換える』 は、スタックフレームに入っている値のいずれかです。
別に、怒っていませんよ。
# インプット ここから ---
念のため、スタックフレームのレイアウトを確認させてください。
またこの絵です。
- 左の絵は、メモリ空間の全体
上のアドレスが 「低」 く、下のアドレスが 「高」 いことに注意してください(スタック領域は低い方に、ヒープ領域は高い方に成長します) - 真ん中は、スタック領域(スタックが積み重なっている)
- 右の絵は、スタックフレームのレイアウト(ローカル変数、スタックフレームポインタ、リターンアドレス、引数)
ここで、スタックフレームポインタ(SFP)と、リターンアドレス(RET)が何だったかというと、
- スタックフレームポインタ →(呼び出し元関数の)スタックフレームの基準となるアドレス。1 つ下のスタックフレームがどこにあるのか
- リターンアドレス → (呼び出し元関数の)命令があるアドレス。この関数が終わったとき、どこに戻ればよいのか
です。
# インプット ここまで ---
これを踏まえて、設問に戻ります。
「リターンアドレス」 には、今の関数の処理が終わったとき、どこに戻ればよいのか、そのアドレスが格納されています。
リターンアドレスの領域が書き換えられるということは、次に実行する命令を、好きな場所に指定できるということです。
スタックベースの BOF 脆弱性がある場合には、ローカル変数に確保した領域より大きなデータが投入されると、データがリターンアドレスの領域にあふれます。
リターンアドレスを都合のよい値に書き換えれば、プログラムの制御を奪える = 好きなコードを実行できるのです。
過去には、① shell コードの実行、② 共有ライブラリ内の関数であって、かつ、任意のプログラムを実行できる関数(e.g. libc 共有ライブラリ)の実行なんかが、出題されました。
次に移ります。
いわゆる、setuid や SUID(Set User ID)と呼ばれるものです。問題文中に、丁寧な説明がありますので、知らなくても解けます。
ただ、setuid、setgid を悪用し、権限昇格を図る手法は、とても有名なので、ここで覚えてしまいましょう。再び問われる可能性があります。
ls コマンドの結果です。
上の 2 行は、ディレクトリなのでムシしましょう。「.」 が、現在のディレクトリを表し、「..」 が、一つ上のディレクトリを表します。
本問では、所有ユーザに注目します。
まず、4 つのファイルとも、ユーザ suzuki に、読み取り権限、実行権限があります(書き込み権限はありません)。
パーミッションが、「rwx(リード / ライト / 実行)」 や 「r-x(リード / 実行)」 であれば、すべて、suzuki 権限で動作します。
さて、『実行権限の属性に "s" が表示されているファイルは、ファイルの所有権限で実行される』 とあるので、suzuki で起動しても、root 権限で実行されるケースは
- 所有ユーザの実行権限 s になっている
- 所有ユーザが root である
場合になります。それって、sample2 ファイルのみですね。
まとめます。
この 3 つの条件を満たすファイルを選べばよいです。
- 実行権限の属性が "s" である
- 一般利用者 suzuki によって起動できる
- ファイルの所有者が root である
ア の、sample2 ですね。
設問 3 です。まいります。
まずは、プログラムを見ましょう。スライドには、「図2 ヒープベース BOF の脆弱性のあるプログラム Y」 の、main 関数の中身だけ抜き出しました。
その中で、さらに骨格だけ残してみました。行番号を入れていないところが、思い切って省略した箇所です。
このプログラムは、C++ で書かれていますが、C++ をよく知らない人でも、① 単純な ID パスワード認証の処理であること、② コメントが充実していることから、各行がどのような処理をしているのかについては、比較的容易に把握できる、はず。です (少なくとも、私は Java の人で、C++ は、3 ヵ月程度さわったに過ぎません) |
一応、プログラムの内容を言葉にしておきます(コードの方が、簡潔でわかりやすいですが)。
- 20 行目、21 行目(青い吹き出しです) → プログラムの中で、領域を確保しています。"new" とあるので、察しはつくと思います。9 バイトずつの領域が、「ヒープ領域に」 確保されます
- 22 行目 → 第 1 引数(ユーザ ID)から、パスワードを取得し、pass 領域に格納しています
- 23 行目(赤い吹き出しです)→ また strcpy です。まぁ、こうなりますよね。第 1 引数(ユーザ ID)を、uid 領域にコピーします
- 25 行目(灰色の吹き出しです)→ pass 領域に文字列があり、この値が、第 2 引数で指定した文字列と一致したときに、認証が成功します
プログラムがやっていること(左)と、メモリのレイアウト(右)をまとめてみました。↓ ココまで整理できていれば、正解は、すぐそこです。
なお、右のメモリのレイアウトは、一例です。ヒープ BOF の脆弱性があるということは、uid の領域があふれ、pass の領域を侵食するだろうことだけ意識して書けばよいと思います。
この問題は、けっこう悩みました。正解以外の選択肢が、あまりにも意味不明だったからです。
整理できたら、あとは具体的に値を入れてみればよいです。
第 1 引数をメモリ領域にコピー(strcpy)し、パスワードの領域まで侵食します。
その後、パスワードの領域にある値と、第 2 引数とを比較します。
つまり、第 1 引数の下 8 桁が、第 2 引数の 8 桁と同じになる場合には、認証が成功します。
ふぅ。
設問 3 の (2) は、もうよいでしょう。
次いきます。
ぱっと見、「ムリ」 と思ったかもしれませんが、ご安心あれ。難易度は、かなり低いです。
C++ をよく知らなくても、正解にたどり着けます。C や C++ の関数名そのものを問いたいわけでは、なさそうです。
まず、『ウ』 です。
これまでより少し多く(PASS_SIZE+8)、パスワード用のバッファを確保しているだけです。PASS_SIZE+8 より大きなパスワードが指定されると、当然、想定していたパスワード用の領域からあふれます。
速攻きります。
次に、『ア』 と 『エ』 についです。どちらも、コピー cpy 関数なので、第 1 引数と第 2 引数が、コピー元、コピー先と考えるのが普通です。第 3 引数は何だったかと言うと、確保するバッファの大きさ(バッファの上限)でした。過去問で、何度も strcpy が出てきたので、知っていた受験生が多かったと思います。これについては、次のスライドで簡単に触れます。
これを踏まえると、『ア』 と 『エ』 では、バッファの上限として strlen(argv[1])+1 が指定されています。ということは、argv[1] の値が大きくなれば、バッファの上限も大きくなります。ユーザ ID として、256 バイトを指定すると、257 バイトまでコピーできるので、当然、ユーザ ID 用の領域からあふれます。
最後に、『イ』 と 『オ』 です。これらは、バッファの上限として、固定値 "9" を指定しています。9 バイト以上の値はコピーできません。これらが正解となります。
ここでまた、少しだけ、知識の確認をさせてください。
- strcpy は、文字列 str をコピー cpy する関数として、これまで何度も問われてきました。対策として、str"n"cpy を使って、バッファの上限を指定することを、知っていなければなりません。
ちなみに、"n" なしの、単なる strcpy は、バッファの上限を指定できませんでした。
- memcpy も、有名な関数です。メモリ mem の内容を、コピー cpy します。見聞きしたことがあるのではないでしょうか。なければ、ココで覚えておきましょう
今回のケースでは、argv[1] の指す領域を、UID_SIZE+1 バイト分、uid が指す領域へ、コピーします。
memcpy、strncpy、どちらも、BOF に脆弱となりうる 「ど典型」 の関数なので、覚えておきたいです。
以上です。
設問 4 に突入です。
設問で、『引数が同でも』 とあります。当然、プログラム Y の書き換えはないため、実行する 「処理も同じ」 となります。
となると、「メモリの配置が異なる」 しかありません。
「その実行環境」 では、メモリの配置が異なるということです。
とはいえ、新たなメモリの配置が固定であれば、その配置が悪用される可能性があります。なので、「メモリ配置が毎回異なる」 となればよいです。
全くわからなくても、ココまでは、書きたいです。
あとは知識があるかどうかです。基礎知識である 「ASLR」 を知っているかどうかです。
ASLR は、2014 年度(平成 26 年度)秋 の試験で、出題されました。
なので...
あら?
IPA の解答が、ASLR に限定していない。なんで、この解答になるのか、わかりません。ごめんなさい。
後ろ髪を引かれつつも、スルーして、(2) に移ります。
これも、知識問題と言わざるをえないです。DEP のざっくりとした仕組みがわかっていれば十分ですが(過去問をやれば、どの程度の深さで技術をおさえればよいか、わかります)。
スライドには、DEP を全く知らない場合の思考を書いてみました。いけなくはないですが、、かなり厳しいです
ただ、何か書かなければならない場合には、こんな感じで推測するしかありません
DEP とは、テキスト領域以外、つまり、ヒープ領域やスタック領域にあるコードに印をつけ、そこからコードが実行されると、プログラムを異常終了させる仕組みです。
私は、「ヒープ領域にあるコードが実行されるわけではないから」 とか、
「ヒープ領域の操作が問題で、テキスト領域のコードを実行するから」 と解答を作成しました。
30 字でまとめるのは、まぢで厳しいです。
それでは、これまで見てきたこと、本問で得た知識を、簡単に復習しましょう。
な感じですかね。
総括です。
総括です
よく出題される、重要な技術、受験生の正解しやすさなどを鑑み、独断と偏見で、問題ごとに、ランクをつけてみました("A" ~ "C")。
- "A" は、絶対にとりたいです。これだけでも、合格ラインを超えるかもしれません
- "B" が、いずれか取れていれば、十分合格ラインを超えると思います
- "C" は、時間かけてもできませんでした。この道の専門の人ならできるのでしょうが
今回も、長文になってしまいました。だらだら書きすぎですね。慣れてきたら、だんだん「コンパクトに」まとめていこうと思います。
最後まて見ていただき、ありがとうございました。