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

こんにちは、あし です。

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

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

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

 

さて、今回は、BOF(バッファオーバフロー)編 第三弾。「2016 年度(平成 28 年度)秋期 情報セキュリティスペシャリスト 午後 1 問 2」を解いていきます。

 

《問題》

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

《凡例》

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

 

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

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

f:id:higher_tomorrow:20200217093742p:plain

ざっと、こんなことがつかめれば、十分だと思います。

  • 選択式の穴埋め a ~ d → 選択肢が 4 つしかない → サービス問題?
  • 設問 2 とは、ガチで BOF っぽい
  • 設問 3 は、変な文字列選択するようになっているし、修正用のコードもある、もっとガチかも
  • 設問 4 は、BOF の対策っぽい

 

頭から、設問 1 a からいきましょう。6 ページの真ん中らへんです。

f:id:higher_tomorrow:20200217093746p:plain

速攻で、CVE-ID → CVE 識別子です。出血、大サービス問題です

  このあたりをおさえている人が多いと思います

  • 脆弱性情報のデータベースの 1 つ
  • 各ベンダーが公表する脆弱性情報がバラバラだったので、それらをまとめて、共通したユニークな識別番号をつけた
  • アメリカの MITRE(マイター)社が運営している

 

設問 1 b と c です。

f:id:higher_tomorrow:20200217093751p:plain

これらは、もしや 「マグネット売場」 なのでは?

もとい。

ここに挙げられた、「ゼロデイ攻撃 + 標的型攻撃」 には、高い確率でヤラレると思います。出題の裏には、相当な危機感があると思われます。

 

本問を選択したほぼ全ての受験生が正解したでしょうね。

 

設問 1 d です。

f:id:higher_tomorrow:20200217093755p:plain

 ファジング(Fuzzing)とは、ファズ(fuzz)と呼ばれる、問題を引き起こしそうなデータを大量に送り込み、その応答や挙動を監視することで、脆弱性を検出する検査手法になります。

ちょっと脱線しますが、ここに出てくる 「静的解析ツール」 とは、ソースコードの段階で、脆弱性を含む不具合を検知してくれるツールです。

特に、セキュリティ面を重視した静的解析を、SAST(Static Application Security Testing)と呼んだりします。これは、DevSecOps(セキュリティを維持したまま、リリースの頻度を高めること)を実現するための、重要な要素となってきています。このあたり、SE っぽいでしょ?

 

戻ります。

設問 2 に入ります。

f:id:higher_tomorrow:20200217093758p:plain

また、この問題ですか。何度でも、きいてきますね。

『関数呼び出し時にスタックに必ず積まれる』 は、スタックフレームです。

『何の値を書き換える』 は、スタックフレームに入っている値のいずれかです。

別に、怒っていませんよ。

 

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

念のため、スタックフレームのレイアウトを確認させてください。

またこの絵です。

f:id:higher_tomorrow:20200217093802p:plain

 

  • 左の絵は、メモリ空間の全体
    上のアドレスが 「低」 く、下のアドレスが 「高」 い
    ことに注意してください(スタック領域は低い方に、ヒープ領域は高い方に成長します
  • 真ん中は、スタック領域(スタックが積み重なっている)
  • 右の絵は、スタックフレームのレイアウト(ローカル変数、スタックフレームポインタ、リターンアドレス、引数)

ここで、スタックフレームポインタ(SFP)と、リターンアドレス(RET)が何だったかというと、

  • スタックフレームポインタ →(呼び出し元関数の)スタックフレームの基準となるアドレス。1 つ下のスタックフレームがどこにあるのか
  • リターンアドレス → (呼び出し元関数の)命令があるアドレス。この関数が終わったとき、どこに戻ればよいのか

です。

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

これを踏まえて、設問に戻ります。

f:id:higher_tomorrow:20200217093807p:plain

「リターンアドレス」 には、今の関数の処理が終わったとき、どこに戻ればよいのか、そのアドレスが格納されています。

 

リターンアドレスの領域が書き換えられるということは、次に実行する命令を、好きな場所に指定できるということです。

 

スタックベースの BOF 脆弱性がある場合には、ローカル変数に確保した領域より大きなデータが投入されると、データがリターンアドレスの領域にあふれます。

 

リターンアドレスを都合のよい値に書き換えれば、プログラムの制御を奪える = 好きなコードを実行できるのです。

 

過去には、① shell コードの実行、② 共有ライブラリ内の関数であって、かつ、任意のプログラムを実行できる関数(e.g. libc 共有ライブラリ)の実行なんかが、出題されました。

 

次に移ります。

f:id:higher_tomorrow:20200217093811p:plain

いわゆる、setuid や SUID(Set User ID)と呼ばれるものです。問題文中に、丁寧な説明がありますので、知らなくても解けます。

ただ、setuid、setgid を悪用し、権限昇格を図る手法は、とても有名なので、ここで覚えてしまいましょう。再び問われる可能性があります。

 

ls コマンドの結果です。

f:id:higher_tomorrow:20200217093816p:plain

上の 2 行は、ディレクトリなのでムシしましょう。「.」 が、現在のディレクトリを表し、「..」 が、一つ上のディレクトリを表します。

 

本問では、所有ユーザに注目します。

まず、4 つのファイルとも、ユーザ suzuki に、読み取り権限、実行権限があります(書き込み権限はありません)。

パーミッションが、「rwx(リード / ライト / 実行)」 や 「r-x(リード / 実行)」 であれば、すべて、suzuki 権限で動作します。

さて、『実行権限の属性に "s" が表示されているファイルは、ファイルの所有権限で実行される』 とあるので、suzuki で起動しても、root 権限で実行されるケースは

  • 所有ユーザの実行権限 s になっている
  • 所有ユーザが root である

場合になります。それって、sample2 ファイルのみですね。

まとめます。

f:id:higher_tomorrow:20200217093820p:plain

この 3 つの条件を満たすファイルを選べばよいです。

  • 実行権限の属性が "s" である
  • 一般利用者 suzuki によって起動できる
  • ファイルの所有者が root である

ア の、sample2 ですね。

 

設問 3 です。まいります。

f:id:higher_tomorrow:20200217093825p:plain

まずは、プログラムを見ましょう。スライドには、「図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 引数で指定した文字列と一致したときに、認証が成功します

 

プログラムがやっていること(左)と、メモリのレイアウト(右)をまとめてみました。↓ ココまで整理できていれば、正解は、すぐそこです。

f:id:higher_tomorrow:20200217093829p:plain

なお、右のメモリのレイアウトは、一例です。ヒープ BOF の脆弱性があるということは、uid の領域があふれ、pass の領域を侵食するだろうことだけ意識して書けばよいと思います。

この問題は、けっこう悩みました。正解以外の選択肢が、あまりにも意味不明だったからです。

 

整理できたら、あとは具体的に値を入れてみればよいです。

f:id:higher_tomorrow:20200217093834p:plain

第 1 引数をメモリ領域にコピー(strcpy)し、パスワードの領域まで侵食します。

その後、パスワードの領域にある値と、第 2 引数とを比較します。

つまり、第 1 引数の下 8 桁が、第 2 引数の 8 桁と同じになる場合には、認証が成功します。

ふぅ。

 

設問 3 の (2) は、もうよいでしょう。

f:id:higher_tomorrow:20200217093838p:plain

次いきます。

ぱっと見、「ムリ」 と思ったかもしれませんが、ご安心あれ。難易度は、かなり低いです。

f:id:higher_tomorrow:20200217093841p:plain

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 バイト以上の値はコピーできません。これらが正解となります。

 

 

ここでまた、少しだけ、知識の確認をさせてください。

f:id:higher_tomorrow:20200217093845p:plain

  • strcpy は、文字列 str をコピー cpy する関数として、これまで何度も問われてきました。対策として、str"n"cpy を使って、バッファの上限を指定することを、知っていなければなりません。
    ちなみに、"n" なしの、単なる strcpy は、バッファの上限を指定できませんでした。
  • memcpy も、有名な関数です。メモリ mem の内容を、コピー cpy します。見聞きしたことがあるのではないでしょうか。なければ、ココで覚えておきましょう

今回のケースでは、argv[1] の指す領域を、UID_SIZE+1 バイト分、uid が指す領域へ、コピーします。

memcpy、strncpy、どちらも、BOF に脆弱となりうる 「ど典型」 の関数なので、覚えておきたいです。

以上です。

 

設問 4 に突入です。

f:id:higher_tomorrow:20200217093849p:plain

設問で、『引数が同でも』 とあります。当然、プログラム Y の書き換えはないため、実行する 「処理も同じ」 となります。

となると、「メモリの配置が異なる」 しかありません。

「その実行環境」 では、メモリの配置が異なるということです。

とはいえ、新たなメモリの配置が固定であれば、その配置が悪用される可能性があります。なので、「メモリ配置が毎回異なる」 となればよいです。

全くわからなくても、ココまでは、書きたいです。

 

あとは知識があるかどうかです。基礎知識である 「ASLR」 を知っているかどうかです。

f:id:higher_tomorrow:20200217093853p:plain

ASLR は、2014 年度(平成 26 年度)秋 の試験で、出題されました。

なので...

あら?

IPA の解答が、ASLR に限定していない。なんで、この解答になるのか、わかりません。ごめんなさい。

後ろ髪を引かれつつも、スルーして、(2) に移ります。

f:id:higher_tomorrow:20200217093858p:plain

これも、知識問題と言わざるをえないです。DEP のざっくりとした仕組みがわかっていれば十分ですが(過去問をやれば、どの程度の深さで技術をおさえればよいか、わかります)。

 

スライドには、DEP を全く知らない場合の思考を書いてみました。いけなくはないですが、、かなり厳しいです

ただ、何か書かなければならない場合には、こんな感じで推測するしかありません

f:id:higher_tomorrow:20200217093902p:plain

DEP とは、テキスト領域以外、つまり、ヒープ領域やスタック領域にあるコードに印をつけ、そこからコードが実行されると、プログラムを異常終了させる仕組みです。

私は、「ヒープ領域にあるコードが実行されるわけではないから」 とか、

「ヒープ領域の操作が問題で、テキスト領域のコードを実行するから」 と解答を作成しました。

 

30 字でまとめるのは、まぢで厳しいです。

 

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

f:id:higher_tomorrow:20200217093911p:plain

f:id:higher_tomorrow:20200217093915p:plain

な感じですかね。

 

総括です。

f:id:higher_tomorrow:20200217093920p:plain

総括です

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

  • "A" は、絶対にとりたいです。これだけでも、合格ラインを超えるかもしれません
  • "B" が、いずれか取れていれば、十分合格ラインを超えると思います
  • "C" は、時間かけてもできませんでした。この道の専門の人ならできるのでしょうが

 

今回も、長文になってしまいました。だらだら書きすぎですね。慣れてきたら、だんだん「コンパクトに」まとめていこうと思います。

 

最後まて見ていただき、ありがとうございました。