これはセキュアなソフトウェア開発に関するラボ演習です。 ラボの詳細については、概要をご覧ください。
バッファオーバーリードバグの一例として、OpenSSL の "Heartbleed" を単純化した以下のコードを変更してバグを修正してください
ほとんど全てのプログラミング言語では、もしプログラムがバッファの外側を読み書きしようとした場合にはバッファをリサイズするか、何等かのエラーを出力するかをデフォルトの挙動としています(例:例外を上げる)。 なぜなら誤ってバッファの外側を読み書きしようとするのは非常によくあることだからです。
しかし、C と C++ は事情が異なります。 C++ は進歩しており、いくらかは安全になっていますが(例:スマートポインタ)、 C と C++ においてバッファの外側を読み書きするような動作は多くの場合で 未定義の動作であり、未定義の動作に対しては あらゆることが なんの防御もなしに発生することになります。 実際によく起こるのは、なにか他のデータが読み書きされることです。 proposals to improve C++ memory safety という提案もありますが、現状では多くの C++ 組込みデータ構造(例えば配列)がメモリセーフではないため、ここでは二つの言語を同時に取り扱います。
2014年に明らかになった Heartbleed(CVE-2014-0160)は、バッファオーバーリード脆弱性の一例です。 Heartbleed は OpenSSL の脆弱性です。OpenSSL は暗号プロトコルである Secure Socket Layer (SSL) とその後継である Transport Layer Security (TLS) を C で実装しており、広く使用されているツールキットです。 Heartbleed は極めて多くの、例えば Google, YouTube, Yahoo!, Pinterest, Blogspot, Instagram, Tumblr, Reddit, Netflix, Stack Overflow, Slate, GitHub, Yelp, Etsy, the U.S. Postal Service (USPS), Blogger, Dropbox, Wikipedia, and the Washington Post といった有名なウェブサイトに影響を与えました。 より詳しくは こちら を参照してください。
ここでは OpenSSL の Heartbleed 脆弱性に対する修正を、dtls1_process_heartbeat という関数を修正することで再現します。
この時点のコードでは、 s->s3->rrec.length というデータ構造はバッファの何バイトが利用可能かを表します。 もし上限サイズを確認しなければ、バッファを超えた読み出しが簡単に発生してしまいます。 以下のコードでは2か所を変更することで、この問題を修正しましょう。
まず初めに、レスポンスの最短の長さである (1 + 2 + 16) が s->s3->rrec.length で取得できる長さを超えていればハートビートを送信せずに 0 を返すようにコードを変更します。 これは、ハートビートを生成するための領域が全くない場合にはハートビートを生成しないようにするための措置です。
続いて、ペイロードがある場合のレスポンスの最短の長さである (1 + 2 + payload + 16) が、 s->s3->rrec.length で示されるレスポンスを格納するための領域より長い場合にも、ハートビートを送信せずに 0 を返すようコードを変更します。
これにより、ハートビートそのものとペイロードがあるハートビートへの返信を格納するための領域がない場合にハートビートを作成しようとするのを防ぐことができます。
これはそれほど難しい修正ではありません。 ここではほんの短いコードを追加します。 問題は、バッファの読み書きという非常に一般的な動作に対して、C と C++ はデフォルトでは安全ではないということです。 実際には、全ての範囲の全ての可能性のあるケースを常にチェックするというのは困難です。これが、C または C++ で書かれたプログラムでメモリ安全性に関わる脆弱性がよく見られることの理由です。
必要に応じて「ヒント」と「諦める」のボタンを使用してください。