情報処理安全確保支援士 2018年 春期 午後1 問01
ソフトウェアの脆弱性に関する次の記述を読んで、設問1~9に答えよ。
V社は、従業員数100名のソフトウェア開発会社である。V社では、開発に関わる全員が情報セキュリティを意識した実装を行えるよう、開発経験が浅い従業員にセキュリティ教育を行っている。次は、任意の攻撃コードが実行され得る脆弱性について、開発チームのT主任が部下のUさんに教えていた時の会話である。
T主任:任意の攻撃コードが実行され得る脆弱性は幾つかある。確保済みメモリ領域を超えてデータを書き込んでしまうaと呼ばれる脆弱性の報告が以前から多かった。最近は解放したメモリ領域を後から使用してしまうbと呼ばれる脆弱性の報告も多くなってきている。
Uさん:bという脆弱性は具体的にはどのようなものなのですか。
T主任:例えば、図1のC++ソースコードからなるプログラム(以下、例示プログラムという)があったとする。例示プログラムは、図2に示すシステム構成の中で動作し、ノートと呼ぶメモ書き機能を実現するものであり、クライアントから利用者が自身の名前とメッセージを登録したり、それを他の利用者が参照したりする。例示プログラムでは、ノートはNote構造体で表現され、利用者の操作に応じて、NoteManagerクラスの各メンバ関数が個別に呼び出される。各メンバ関数では、ノートの生成(CreateNote)、利用者の名前の登録(RegisterName)、メッセージの登録(RegisterMsg)、ノートの登録内容表示(DisplayNote)、ノートの破棄(DeleteNote)を行う機能を実装している。例示プログラムにおいて、DeleteNoteメンバ関数内でm_noteの指すメモリ領域を解放しているが、仮にDeleteNoteメンバ関数が呼び出された直後にRegisterNameメンバ関数が呼び出されると、解放したm_noteの指すメモリ領域にアクセスできてしまう。これがbという脆弱性だ。例示プログラムでは、悪意をもつ利用者(以下、攻撃者という)の操作によって、任意の攻撃コードを実行され、サーバを乗っ取られてしまうおそれがある。


Uさん:解放したメモリ領域にアクセスされると、どのように攻撃コードが実行されるのですか。
T主任:例示プログラムにおいて、図3に示す(1)~(3)の順でメンバ関数の呼出しが行われたとしよう。その場合、図3の(1)で確保されていたNote構造体用のメモリ領域と、図3の(3)で確保されたchar[8]用のメモリ領域が同じアドレスに割り当てられる可能性がある。その場合、①RegisterNameメンバ関数内で読み込まれる攻撃者からの入力値によって、元々Note構造体用であったメモリ領域が上書きされる。このときの攻撃者からの入力値がうまく細工されていると、②次にRegisterNameメンバ関数が呼ばれた際、その際に読み込まれる攻撃者からの入力値が、攻撃者の指定したアドレスに書き込まれることになる。このように、攻撃者からの入力値が攻撃者の指定したアドレスに書き込まれる場合には攻撃コードが実行され得る。

Uさん:もう少し具体的に説明してください。
T主任:関数テーブルの例で説明しよう。ここでいう関数テーブルとは、プログラム中で呼び出している共有ライブラリに含まれる関数(以下、ライブラリ関数という)の実行コードの先頭アドレスが記録されたテーブルだ。ライブラリ関数の呼出し時には、図4に示すように関数テーブルに記録された実行コードの先頭アドレスの値を参照してライブラリ関数の実行コードに処理が遷移する。

T主任:攻撃者が既に、攻撃コードをメモリ上に書き込んでいるとしよう。この状態で、例えば、攻撃コードが存在するアドレスを関数テーブルに書き込まれた場合、関数の呼出し時に関数テーブルが参照されると、攻撃コードに処理が遷移してしまう。
Uさん:例示プログラムを攻撃する場合だと、関数テーブルに書き込むアドレスは具体的にどのような値になりますか。
T主任:例えば、RegisterMsgメンバ関数の呼出しによってm_note->msgが指し示すメモリ領域に攻撃コードが書き込まれていて、その先頭アドレスが0x0b123400と分かっていたとする。その場合、関数テーブルが表1に示すようになっていたとすると、アドレスc番地に値dを書き込むことによって、次にCreateNoteメンバ関数が呼び出された際、攻撃コードに処理が遷移することになる。
Uさん:RegisterMsgメンバ関数の呼出しによって入力された攻撃コードはe領域に書き込まれるので、データ実行防止と呼ばれる機能が有効化されていた場合、実行されませんよね。
T主任:確かにそのとおりだ。ただし、③関数テーブルに書き込むアドレスとして、例えば、共有ライブラリ内のメモリアドレスを選べば、データ実行防止が有効化されていた場合でも、攻撃者が任意の処理を実行できる可能性がある。例示プログラムにおいて、共有ライブラリ内のメモリアドレスが表2のようになっていたとすると、関数テーブルに書き込むアドレスをf番地にすることによって、データ実行防止が有効化されていた場合でも、/bin/shを起動して任意のシェルコマンドを実行できる可能性がある。
↩設問7
↩設問7 Uさん:共有ライブラリ内のメモリアドレスは、表2のように前もって知ることができるものなのですか。
T主任:共有ライブラリをメモリに読み込む際、それを配置するアドレスを毎回ランダムに選ぶASLR(Address Space Layout Randomization)と呼ばれるセキュリティ機能がある。この機能が有効な場合、共有ライブラリ内のメモリアドレスを前もって知ることは難しい。しかし、例示プログラムにおいて、図3の(3)の状態をつくり出せれば、RegisterNameメンバ関数とgメンバ関数を利用することによって、ASLRが有効化されていた場合でも、共有ライブラリ内のメモリアドレスを特定できる可能性がある。データ実行防止やASLRなどは効果的な機能ではあるが、根本的な対策にはならない。やはり脆弱性そのものを修正することが重要だ。
Uさん:では、bの脆弱性を修正するには、例示プログラムではどうすればよいのでしょうか。
T主任:図1の39行目の直後にhという1文を加えればよいだろう。
Uさんは、今回学んだことをコードレビューの観点として生かしていくことにした。
設問1:
問題文を見る本文中のa、bに入れる適切な脆弱性名を、解答群の中から選び、記号で答えよ。
解答群
ア:CSRF
イ:SQLインジェクション
ウ:Use-After-Free
エ:クロスサイトスクリプティング
オ:コマンドインジェクション
カ:バッファオーバフロー
キ:フォーマットストリングバグ
ク:レースコンディション
模範解答
a:カ
b:ウ
解説
解答の論理構成
- 【問題文】では
「確保済みメモリ領域を超えてデータを書き込んでしまうaと呼ばれる脆弱性」
と説明しています。確保領域を超える書込みは典型的なバッファオーバフローであり、解答群「カ:バッファオーバフロー」が一致します。 - 同じく【問題文】で
「最近は解放したメモリ領域を後から使用してしまうbと呼ばれる脆弱性」
と記載されています。解放後の領域を再利用してしまう欠陥はUse-After-Freeを指すため、解答群「ウ:Use-After-Free」が該当します。 - 以上より
a=「カ:バッファオーバフロー」
b=「ウ:Use-After-Free」
が妥当です。
誤りやすいポイント
- 「確保済みメモリ領域を超えてデータを書き込む」というキーワードから、他のインジェクション系(例:フォーマットストリングバグ)と混同する。
- 解放後に再利用される状況を「ダングリングポインタ」とだけ覚えており、正式名称「Use-After-Free」を選択できない。
- SQLインジェクションやクロスサイトスクリプティングといったWeb系攻撃を脆弱性全般のキーワードとして連想し、メモリ管理の問題であることを見落とす。
FAQ
Q: バッファオーバフローはスタックとヒープどちらも対象になりますか?
A: はい。確保した領域(スタック変数・ヒープ領域)を超えて書き込む行為はどちらでもバッファオーバフローと呼ばれます。
A: はい。確保した領域(スタック変数・ヒープ領域)を超えて書き込む行為はどちらでもバッファオーバフローと呼ばれます。
Q: Use-After-Freeは二重解放(Double Free)と同じですか?
A: 異なります。Double Freeは同じポインタを2回解放する操作ミス、Use-After-Freeは「解放後そのメモリを再利用してアクセス」する点が問題です。
A: 異なります。Double Freeは同じポインタを2回解放する操作ミス、Use-After-Freeは「解放後そのメモリを再利用してアクセス」する点が問題です。
Q: 脆弱性修正の基本方針は?
A: バッファオーバフローでは境界チェックや安全なAPIの使用、Use-After-FreeではポインタをNULLにする・スマートポインタを使うなど、根本原因を除去する実装改善が必須です。
A: バッファオーバフローでは境界チェックや安全なAPIの使用、Use-After-FreeではポインタをNULLにする・スマートポインタを使うなど、根本原因を除去する実装改善が必須です。
関連キーワード: バッファオーバフロー、Use-After-Free, メモリ管理、ASLR, データ実行防止
設問2:
問題文を見る模範解答
785634120a
解説
解答の論理構成
-
前提整理
-
書き換える標的
- 「Note構造体」の先頭4バイトはnameへのポインタです(T主任の説明より)。
- Deleteした領域に再確保したchar[8] が重なり、RegisterNameの %7sで 最大7バイト が書き込まれます。
- 従って、最初の4バイトでポインタを書き換えれば、次回のRegisterNameで入力バッファが0x12345678を指すようになります。
-
リトルエンディアンでの並び
- 0x12345678 → 下位バイトから78 56 34 12の順にメモリへ格納されます。
-
ストリング終端と改行
- %7sはトークン終端を示す改行0x0aに遭遇すると読取を終了し、 直後に自動でNUL(0x00) を追加します。
- したがって攻撃者が送るバイト列は
「78 56 34 12 0a」
(4バイトのアドレス値+改行)の5バイトで十分です。
-
16進数文字列での回答
- 大文字小文字は問われていません。
- 連続した10文字で「785634120a」となります。
誤りやすいポイント
- エンディアンを逆にして「12345678」で答えてしまう。
- 改行を忘れ、終端コードを 0x0a ではなく 0x00 と誤解する。
- %7sの制限から8バイト全てを入力しようとしてしまう。
- 16進数表記に0xを付けてしまい、問題指示「16進数文字列」に反する。
FAQ
Q: なぜNUL 0x00を含めなくて良いのですか?
A: NULはscanf("%s") が自動で付与するため、攻撃者が送信する必要はありません。
A: NULはscanf("%s") が自動で付与するため、攻撃者が送信する必要はありません。
Q: 5バイトではchar[8] の残り3バイトが未使用になりますが問題ありませんか?
A: 目的はポインタ先頭4バイトを書き換えることなので、残りは0x00で初期化されても攻撃に支障はありません。
A: 目的はポインタ先頭4バイトを書き換えることなので、残りは0x00で初期化されても攻撃に支障はありません。
Q: %7sで非表示文字を入力しても受け付けられるのですか?
A: %sは空白・改行・タブでトークンを区切るだけなので、0x01〜0x7Fの制御文字を含むバイト列も読み込まれます。
A: %sは空白・改行・タブでトークンを区切るだけなので、0x01〜0x7Fの制御文字を含むバイト列も読み込まれます。
関連キーワード: Use‐after‐free, リトルエンディアン、関数テーブル、ASLR, データ実行防止
設問3:
問題文を見る本文中のcに入れる適切なアドレスを表1中の(ア)〜(シ)から選び、記号で答えよ。
模範解答
c:(エ)
解説
解答の論理構成
-
攻撃者の目的は「次にCreateNoteメンバ関数が呼び出された際、攻撃コードに処理が遷移」させることです。
引用:【問題文】「その場合、関数テーブルが表1に示すようになっていたとすると、アドレスc番地に値dを書き込むことによって、次にCreateNoteメンバ関数が呼び出された際、攻撃コードに処理が遷移することになる。」 -
したがって c には「CreateNoteメンバ関数が実行時に参照する関数テーブルの格納場所」を選ぶ必要があります。CreateNoteはnew Note() を実行するので、newの実行コード先頭アドレスを格納している表1のエントリが対象です。
-
表1を確認するとnewの実行コード先頭アドレスを保持している行は
(エ) 0x08049e40です。
引用:【表1】「(エ) 0x08049e40 … newの実行コードの先頭アドレス」。 -
よって c に入るのは (エ)。模範解答「c:(エ)」と合致します。
誤りやすいポイント
- new[] とnewを取り違え、(オ) を選んでしまう。CreateNoteが呼ぶのは配列確保ではなく単一オブジェクト確保のnew。
- アドレスではなく値列(例えば (コ) 0xf7cff150)を選択しようとするミス。設問は「アドレスc番地」を問うているので、左欄のアドレスを選ぶ。
- delete系を選択する早とちり。CreateNote内では解放ではなく確保が行われる。
FAQ
Q: なぜscanfやprintfのエントリではないのですか?
A: CreateNote内で呼ばれているのはメモリ確保処理だけで入出力処理は行われていません。そのため攻撃対象となる関数テーブルのエントリもnewのものになります。
A: CreateNote内で呼ばれているのはメモリ確保処理だけで入出力処理は行われていません。そのため攻撃対象となる関数テーブルのエントリもnewのものになります。
Q: (エ) を書き換えると必ず攻撃に成功しますか?
A: ASLRやDEPなど環境依存の防御機構が働くと成功率は下がります。問題文でも後段でASLRやデータ実行防止について解説しているとおり、脆弱性修正が根本対策です。
A: ASLRやDEPなど環境依存の防御機構が働くと成功率は下がります。問題文でも後段でASLRやデータ実行防止について解説しているとおり、脆弱性修正が根本対策です。
Q: newではなくnew[] が呼ばれるケースはありますか?
A: 例示プログラムではRegisterName・RegisterMsg内でnew char[ ] を使いますが、CreateNoteでは配列確保を行っていないため該当しません。
A: 例示プログラムではRegisterName・RegisterMsg内でnew char[ ] を使いますが、CreateNoteでは配列確保を行っていないため該当しません。
関連キーワード: use-after-free, 関数テーブル改ざん、動的メモリ確保、ASLR, データ実行防止
設問4:
問題文を見る本文中のdに入れる適切な値を16進数で答えよ。
模範解答
d:0x0b123400
解説
解答の導き方
設問は表1の値「d」に入れる16進数を問うています。問題文の記述を順にたどると、書き込むべき値は攻撃コードの先頭アドレスであることが明らかになります。以下で段階的に示します。
-
攻撃コードの所在と先頭アドレスが与えられていること
問題文に「RegisterMsgメンバ関数の呼出しによってm_note->msgが指し示すメモリ領域に攻撃コードが書き込まれていて、その先頭アドレスが0x0b123400と分かっていたとする。」とあります。つまり攻撃者が狙う実行コードの先頭アドレスは0x0b123400であると前提されています。 -
関数呼び出しが関数テーブルの値を参照して遷移する仕組み
図4や本文で説明されているとおり、ライブラリ関数の呼出し時には「関数テーブルに記録された実行コードの先頭アドレスの値」を参照して実行先に遷移します。したがって関数テーブルの該当エントリに攻撃コードのアドレスを書き込めば、その関数呼び出し時に処理が攻撃コードへ移ります。 -
どの関数テーブルエントリを書き換えるかの特定
CreateNoteの実装を見ると、図1の13行目に「if(m_note = new Note()){」とあり、CreateNoteはnewを使っています。したがってCreateNoteの呼出し時に参照される関数テーブルのエントリはnewに対応するエントリです。表1を見るとnewに対応するエントリは左端アドレス「(エ) 0x08049e40」で、現在の値は「(コ) 0xf7cff150」となっています。 -
書き込む値(d)の決定
CreateNoteが呼ばれたときに攻撃コードへ遷移させるには、newに対応する関数テーブルエントリの値を攻撃コードの先頭アドレスに置き換えればよいことになります。攻撃コードの先頭アドレスは既に「0x0b123400」と与えられているため、表1の該当エントリに書き込む値(d)は0x0b123400です。
以上より、設問の求める値は0x0b123400となります。
誤りやすいポイント
-
関数テーブルの「アドレス」(左列)と「値」(右列)を取り違える
問題は「アドレス[c]番地に値[d]を書き込む」と書かれており、求められるのは書き込む「値[d]」です。アドレス(例: 0x08049e40)と混同して解答を誤らないようにしてください。 -
どの関数を狙うかの取り違え
RegisterName/RegisterMsgでscanfが使われているためscanfやprintfを選びがちですが、攻撃が発動するのは「次にCreateNoteが呼び出された際」です。CreateNote内の呼び出し(図1行13のif(m_note = new Note()){)を確認し、newに対応するエントリを狙う必要があります。 -
16進数表記の写し間違い
問題文中の16進数は一字一句正確に答える必要があります。特に先頭の0などを省略しないよう注意してください(ここでは0x0b123400が正解です)。
FAQ
Q: 「攻撃コードの先頭アドレスが0x0b123400と分かっていた」という設定は現実的ですか?
A: 試験問題ではその前提が明示されていますので、設問解答ではその値を与件として扱います。実務ではASLRやヒープの振る舞いにより不確定なことが多く、情報漏えい(アドレスリーク)などで先にアドレスを特定する工夫が必要になりますが、本問では攻撃者が既に0x0b123400を知っているという前提です。
A: 試験問題ではその前提が明示されていますので、設問解答ではその値を与件として扱います。実務ではASLRやヒープの振る舞いにより不確定なことが多く、情報漏えい(アドレスリーク)などで先にアドレスを特定する工夫が必要になりますが、本問では攻撃者が既に0x0b123400を知っているという前提です。
Q: データ実行防止(DEP)が有効ならm_note->msgに書いた攻撃コードは実行できないのでは?
A: DEPは書込み可能なデータ領域(通常ヒープやスタック)上のコード実行を防ぎます。しかし本問の攻撃手法は関数テーブルを書き換えて「既に実行可能な領域(例: 共有ライブラリ内の関数)や攻撃者が確保した実行可能領域」に制御を移すことを想定しています。したがってDEPがあっても、実行可能領域へ制御を移す方法(例: 共有ライブラリ内の既存関数の利用やROP)があれば攻撃が成立する可能性があります。
A: DEPは書込み可能なデータ領域(通常ヒープやスタック)上のコード実行を防ぎます。しかし本問の攻撃手法は関数テーブルを書き換えて「既に実行可能な領域(例: 共有ライブラリ内の関数)や攻撃者が確保した実行可能領域」に制御を移すことを想定しています。したがってDEPがあっても、実行可能領域へ制御を移す方法(例: 共有ライブラリ内の既存関数の利用やROP)があれば攻撃が成立する可能性があります。
Q: ASLRが有効なら共有ライブラリのアドレスは分からないのでは?
A: 一般にASLRは共有ライブラリのベースアドレスをランダム化しますが、情報漏えいによってそのランダム化値を取得できる場合があります。問題文にも「RegisterNameメンバ関数と別のメンバ関数を利用することによって、ASLRが有効化されていた場合でも、共有ライブラリ内のメモリアドレスを特定できる可能性がある」と明示されています。したがって問題文の前提どおりアドレス特定が可能であると扱います。
A: 一般にASLRは共有ライブラリのベースアドレスをランダム化しますが、情報漏えいによってそのランダム化値を取得できる場合があります。問題文にも「RegisterNameメンバ関数と別のメンバ関数を利用することによって、ASLRが有効化されていた場合でも、共有ライブラリ内のメモリアドレスを特定できる可能性がある」と明示されています。したがって問題文の前提どおりアドレス特定が可能であると扱います。
関連キーワード: Use-after-free、関数テーブル、データ実行防止、ASLR、ヒープ
設問5:
問題文を見る本文中のeに入れるメモリ領域の名称を答えよ。
模範解答
e:ヒープ
解説
解答の論理構成
- 問題文では、攻撃コードが格納される場所を
「Uさん:RegisterMsgメンバ関数の呼出しによって入力された攻撃コードはe領域に書き込まれるので」と説明しています。 - RegisterMsgメンバ関数のコードは
m_note->msg = new char[100];
でバッファを確保しています。new演算子が返すアドレスはC++ において ヒープ領域 です。 - したがって、入力された攻撃コードが入るのはヒープであり、e に入る適切な用語は「ヒープ」です。
誤りやすいポイント
- スタックと混同する
char buf[100] のような自動変数と違い、newで確保したメモリはスタックではなくヒープに配置されます。 - グローバル領域と誤認する
Note *m_note; はクラスのメンバであり、実際の文字列バッファは実行時にnewで動的確保されます。 - 「データ実行防止が効く=実行不可領域」と短絡する
DEPが有効でもヒープに配置されたデータは実行禁止属性となるだけで、関数テーブル書換えなど制御フローhijackによってコード実行は可能です。
FAQ
Q: なぜnewを使うとヒープに配置されるのですか?
A: C/C++ のメモリモデルでは、new(またはmalloc)が呼ばれるとOSのヒープ管理領域から要求バイト数分を確保し、その先頭アドレスを返します。スタックやグローバル領域は使用しません。
A: C/C++ のメモリモデルでは、new(またはmalloc)が呼ばれるとOSのヒープ管理領域から要求バイト数分を確保し、その先頭アドレスを返します。スタックやグローバル領域は使用しません。
Q: delete m_note; 後にポインタをNULLにしないと何が問題になりますか?
A: 解放済みアドレスを保持したままなので再利用時に未定義動作を引き起こし、b として示された「解放したメモリ領域を後から使用してしまう」ユースアフタフリー脆弱性になります。
A: 解放済みアドレスを保持したままなので再利用時に未定義動作を引き起こし、b として示された「解放したメモリ領域を後から使用してしまう」ユースアフタフリー脆弱性になります。
Q: DEPを回避する方法として共有ライブラリの関数アドレスを書き込む手口は一般に何と呼ばれますか?
A: 既存コードへのジャンプを利用するROP(Return Oriented Programming)やPLT/GOTオーバライドなど、コードリユース攻撃に分類されます。
A: 既存コードへのジャンプを利用するROP(Return Oriented Programming)やPLT/GOTオーバライドなど、コードリユース攻撃に分類されます。
関連キーワード: ヒープ、ユースアフタフリー、メモリ破壊、データ実行防止、ASLR
設問6:
問題文を見る本文中の下線③の理由を、45字以内で述べよ。
模範解答
ライブラリ関数はデータ実行防止の対象ではないメモリ領域に配置されているから
解説
解答の論理構成
- 問題文は、データ実行防止(DEP)が有効でも攻撃が成立する状況として、
「③関数テーブルに書き込むアドレスとして、例えば、共有ライブラリ内のメモリアドレスを選べば、データ実行防止が有効化されていた場合でも、攻撃者が任意の処理を実行できる可能性がある」
と述べています。 - DEPは “実行可能でないはずの領域” を実行不可にしてコード実行を防ぐ仕組みです。逆に言えば 既に実行可能フラグが立っている領域、すなわちプログラムや共有ライブラリのコード領域は対象外です。
- 共有ライブラリのコードはOSローダが 実行属性付き でメモリへ配置します。したがって、そのアドレスを関数テーブルへ上書きすれば処理が遷移してもDEPに阻まれません。
- よって、「ライブラリ関数はデータ実行防止の対象ではないメモリ領域に配置されているから」という結論になります。
誤りやすいポイント
- DEPが「すべての実行を禁止する」と誤解し、ライブラリ領域も保護対象と考えてしまう。
- ASLRとDEPを混同し、アドレスさえ分かればDEPも回避できると思わない、という逆の思い込み。
- 「関数テーブルを書き換える=コードインジェクション」と短絡し、既存コード流用(Return-to-libc型)という視点を見落とす。
FAQ
Q: DEPが有効なら攻撃者が注入したシェルコードは実行できないのでは?
A: そのとおりですが、本問は「共有ライブラリ内のコードを呼び出す」方式です。ライブラリ領域は実行可能なのでDEPを回避できます。
A: そのとおりですが、本問は「共有ライブラリ内のコードを呼び出す」方式です。ライブラリ領域は実行可能なのでDEPを回避できます。
Q: ライブラリ関数を呼び出すだけで任意コード実行になるのですか?
A: system("/bin/sh") のようにOSコマンドを呼ぶ関数へ制御を渡せば、攻撃者はシェルを取得し自由に操作できます。
A: system("/bin/sh") のようにOSコマンドを呼ぶ関数へ制御を渡せば、攻撃者はシェルを取得し自由に操作できます。
Q: ASLRが有効でも共有ライブラリのアドレスは分かるのですか?
A: 本文中にあるようにRegisterNameなどを使ってリークを行えば、実行時に割り振られたアドレスを得られる場合があります。
A: 本文中にあるようにRegisterNameなどを使ってリークを行えば、実行時に割り振られたアドレスを得られる場合があります。
関連キーワード: データ実行防止、共有ライブラリ、ASLR, 関数テーブル、メモリ保護
設問7:
問題文を見る本文中のfに入れる適切なアドレスを表2中の(ア)〜(ク)から選び、記号で答えよ。
模範解答
f:(ア)
解説
解答の論理構成
- 攻撃者の最終目標は「/bin/shを起動して任意のシェルコマンドを実行」することです。これはOSにコマンドを渡すライブラリ関数system() を悪用するのが定番手法です。
- 本文には「共有ライブラリ内のメモリアドレスが表2のようになっていたとすると、関数テーブルに書き込むアドレスをf番地にすることによって、データ実行防止が有効化されていた場合でも、/bin/shを起動して任意のシェルコマンドを実行できる可能性がある。」とあります。
- 表2を確認すると、systemの実行コードは「(ア) 0xf7cc8da0」と明示されています。
- したがって、「/bin/shを起動」させるために関数テーブルへ書き込むべきアドレス f はsystemの実行コード先頭アドレスである (ア) 0xf7cc8da0です。
- 他の候補(printfやnewなど)はOSコマンド実行能力を持たないため要件を満たしません。以上より解答は (ア) となります。
誤りやすいポイント
- /bin/shの文字列アドレス「(ク) 0xf7de99ab」を選ぶ誤答
- 文字列は実行可能コードではなく、これを書き込んでも制御は遷移しません。
- printfやscanfを選ぶ誤答
- どちらもシェルを起動する機能がなく、意図した「任意コマンド実行」には直結しません。
- 表1と表2を混同する
- 表1は「現在の関数テーブル」、表2は「ライブラリ内のアドレス一覧」です。書き込む先(表1)と書き込む値(表2)を区別しましょう。
FAQ
Q: なぜsystem() に直接ジャンプするだけでシェルが起動するのですか?
A: 例示プログラムでは既に "/bin/sh" という引数がメモリ上に配置されている前提で説明しています。system() が呼び出される際、この文字列が第一引数として渡されるよう細工されているため、シェルが起動します。
A: 例示プログラムでは既に "/bin/sh" という引数がメモリ上に配置されている前提で説明しています。system() が呼び出される際、この文字列が第一引数として渡されるよう細工されているため、シェルが起動します。
Q: データ実行防止が有効なら攻撃コードを実行できないのでは?
A: 本問は「実行可能な共有ライブラリ上のコードを再利用する」Return-to-libc型の攻撃です。実行権限付き領域にあるsystem() を呼び出すため、データ実行防止を回避できます。
A: 本問は「実行可能な共有ライブラリ上のコードを再利用する」Return-to-libc型の攻撃です。実行権限付き領域にあるsystem() を呼び出すため、データ実行防止を回避できます。
Q: ASLRでアドレスが毎回変わるのに、どうやってsystem() のアドレスを知るのですか?
A: 本文にあるように「RegisterNameメンバ関数とgメンバ関数を利用」して情報漏えいを起こし、実行時に実際のアドレスを取得する方法が示唆されています。
A: 本文にあるように「RegisterNameメンバ関数とgメンバ関数を利用」して情報漏えいを起こし、実行時に実際のアドレスを取得する方法が示唆されています。
関連キーワード: ユースアフターフリー、関数テーブル書き換え、データ実行防止、ASLR, Return-to-libc
設問8:
問題文を見る本文中のgに入れるメンバ関数の名前を答えよ。
模範解答
g:DisplayNote
解説
解答の論理構成
- 【問題文】には
「例示プログラムにおいて、図3の(3)の状態をつくり出せれば、RegisterNameメンバ関数とgメンバ関数を利用することによって、ASLRが有効化されていた場合でも、共有ライブラリ内のメモリアドレスを特定できる可能性がある。」
とあります。ここでRegisterNameは“入力”を受け付ける機能です。 - 共有ライブラリ内のアドレスを“特定”するには、ユーザ入力で細工した値をプログラムに表示させ、画面上から読み取れる必要があります。
- C++ ソースコード(図1)を見ると、表示を行うメンバ関数は
「void DisplayNote(){
if(m_note && m_note->name) printf("Name: %s\n"、m_note->name);
if(m_note && m_note->msg) printf("Message: %s\n"、m_note->msg);
}」
だけです。 - RegisterNameで細工した名前やポインタを格納し、DisplayNoteでその値をprintfさせれば、ASLR下でも実際に配置されたアドレスが漏えいします。
- よって、g に入るメンバ関数名は「DisplayNote」となります。
誤りやすいポイント
- CreateNoteを選んでしまう
→ CreateNoteはメモリ確保のみで表示処理がないため、アドレスを観測できません。 - RegisterMsgと誤答する
→ RegisterMsgも入力専用であり、アドレスを“漏えい”させる動作はありません。 - 「DisplayNote」の大文字・小文字を混同する
→ クラスメソッド名は【問題文】で示されたとおり「DisplayNote」です。
FAQ
Q: なぜDisplayNoteのprintfでアドレスが漏れるのですか?
A: RegisterNameでm_note->nameに細工したポインタ値を書き込むと、DisplayNoteがprintf("Name: %s") を実行した際、そのポインタを文字列アドレスとして参照しようとします。この時にクラッシュせず読める領域であれば実アドレスが画面に現れるため、ASLR下でも基底アドレス推定が可能になります。
A: RegisterNameでm_note->nameに細工したポインタ値を書き込むと、DisplayNoteがprintf("Name: %s") を実行した際、そのポインタを文字列アドレスとして参照しようとします。この時にクラッシュせず読める領域であれば実アドレスが画面に現れるため、ASLR下でも基底アドレス推定が可能になります。
Q: DisplayNoteだけでは任意コード実行に至りませんか?
A: DisplayNoteは情報漏えい用途で使われ、得たアドレスを基に関数テーブル書換えなど次の段階の攻撃を行います。任意コード実行はRegisterName等を再度呼び出して関数テーブルへ細工が行われた時点で発生します。
A: DisplayNoteは情報漏えい用途で使われ、得たアドレスを基に関数テーブル書換えなど次の段階の攻撃を行います。任意コード実行はRegisterName等を再度呼び出して関数テーブルへ細工が行われた時点で発生します。
Q: 対策としては何を修正すべきですか?
A: 【問題文】で示されたとおり、「図1の39行目の直後にhという1文を加えればよい」とされており、具体的には解放後にポインタをNULLに設定してUse-After-Freeを防止します。
A: 【問題文】で示されたとおり、「図1の39行目の直後にhという1文を加えればよい」とされており、具体的には解放後にポインタをNULLに設定してUse-After-Freeを防止します。
関連キーワード: Use-after-Free, ASLR, 情報漏えい、関数ポインタ書換、データ実行防止
設問9:
問題文を見る本文中のhに入れる適切なソースコードを答えよ。
模範解答
h:m_note = NULL;
解説
解答の論理構成
- 脆弱性の原因を確認
【問題文】では「解放したメモリ領域を後から使用してしまうbと呼ばれる脆弱性の報告も多くなってきている。」とあり、これは “use-after-free” です。さらに「DeleteNoteメンバ関数内でm_noteの指すメモリ領域を解放しているが、仮にDeleteNoteメンバ関数が呼び出された直後にRegisterNameメンバ関数が呼び出されると、解放したm_noteの指すメモリ領域にアクセスできてしまう。」と具体的な再利用シナリオが示されています。 - 何が欠けているかを特定
DeleteNoteは37~39行目で
• delete[] m_note->name;
• delete[] m_note->msg;
• delete m_note;
を実行していますが、ポインタを無効化していません。このためm_noteは“解放済みメモリを指すダングリングポインタ”になり、次の呼び出しで誤って再利用されます。 - 有効な修正策
使い終わったポインタをただちにNULLに設定しておけば、後続処理でif(m_note && …) の判定が偽になりアクセスは行われません。問題文も「図1の39行目の直後にhという1文を加えればよいだろう。」と指摘しています。 - 挿入すべきコード
ダングリングポインタを防ぐ標準的手法は「ポインタをNULLにする」。よって h には
m_note=NULL;
を追加するのが最適解です。
誤りやすいポイント
- 「delete m_note; だけで十分」と思い込み、NULL化を忘れる。
- m_note->nameやm_note->msgを個別にNULLにしても、根本のm_noteを放置するケース。
- nullptr(C++11以降)と混同し、問題文の「32ビット環境」「NULL」を変えてしまう。原文どおり「m_note=NULL;」が必要です。
FAQ
Q: delete後に必ずNULL化しなければいけませんか。
A: 条件分岐でポインタの生存を判定している設計では必須です。本問題のようにif(m_note && …) が多用されている場合、NULL化しないとuse-after-freeになります。
A: 条件分岐でポインタの生存を判定している設計では必須です。本問題のようにif(m_note && …) が多用されている場合、NULL化しないとuse-after-freeになります。
Q: m_noteをNULLにしただけでm_note->nameなどは放置して良いのですか。
A: 39行目以前にdelete[] m_note->name; とdelete[] m_note->msg; で解放済みです。m_noteがNULLになればそれらのメンバへの経路も断たれます。
A: 39行目以前にdelete[] m_note->name; とdelete[] m_note->msg; で解放済みです。m_noteがNULLになればそれらのメンバへの経路も断たれます。
Q: nullptrを使う方が安全ですか。
A: 近代的C++ ではそうですが、試験は原文「NULL」を指定しているため答案も「m_note=NULL;」が正しいです。
A: 近代的C++ ではそうですが、試験は原文「NULL」を指定しているため答案も「m_note=NULL;」が正しいです。
関連キーワード: use-after-free, ダングリングポインタ、NULL初期化、メモリ解放、ポインタ安全




