GnuTLSの脆弱性を分析したとき、私はTLSスタックに見られる深刻なバグはこれが最後にはならないだろうと言いました。
OpenSSLの「Heartbleed」脆弱性は非常に深刻な問題です。この脆弱性により、攻撃者はメモリから最大64KBのデータを読み取ることができます。一部のセキュリティ研究者は
特権情報や認証なしで、X.509証明書の秘密鍵、ユーザー名やパスワード、チャットツールのメッセージ、電子メール、重要なビジネス文書や通信などのデータを盗み出すことが可能です。
どのような仕組みになっているのでしょうか?コードを見てみましょう。
0x01 バグ
ssl/dl_both.cを参照してください。脆弱性のパッチはこの行から始まります:
int
dtls1_process_heartbeat(SSL *s)
{
unsigned char *p = &s->s3->rrec.data[0], *pl;
unsigned short hbtype;
unsigned int payload;
unsigned int padding = 16; /* Use minimum padding */
すぐに、SSLv3レコードのデータへのポインタを取得しました。構造体SSL3_RECORDは以下のように定義されています:
typedef struct ssl3_record_st
{
int type; /* type of record */
unsigned int length; /* How many bytes available */
unsigned int off; /* read/write offset into 'buf' */
unsigned char *data; /* pointer to the record data */
unsigned char *input; /* where the decode bytes are */
unsigned char *comp; /* only used with decompression - malloc()ed */
unsigned long epoch; /* epoch number, needed by DTLS1 */
unsigned char seq_num[8]; /* sequence number, needed by DTLS1 */
} SSL3_RECORD;
各 SSLv3 レコードはタイプフィールド、長さフィールド、 レコードデータへのポインタを含んでいます。dtls1_process_heartbeat に戻りましょう:
/* Read type and payload length first */
hbtype = *p++;
n2s(p, payload);
pl = p;
SSLv3 レコードの最初のバイトはハートビートパケットのタイプを識別します。マクロ n2s はポインタ p が指す配列から最初の 2 バイトを取り出し、変数 payload に格納します。このプログラムは SSLv3 レコードの実際の長さをチェックしないことに注意してください。変数 pl は訪問者によって提供されたハートビートパケットのデータを指します。
この機能の後に続くのは
unsigned char *buffer, *bp;
int r;
/* Allocate memory for the response, size is 1 byte
* message type, plus 2 bytes payload length, plus
* payload, plus padding
*/
buffer = OPENSSL_malloc(1 + 2 + payload + padding);
bp = buffer;
そのため、プログラムはアクセサで指定されたサイズのメモリ領域を、最大1バイトまで割り当てます。変数bpはこのメモリ領域にアクセスするためのポインタです。
/* Enter response type, length and copy payload */
*bp++ = TLS1_HB_RESPONSE;
s2n(payload, bp);
memcpy(bp, pl, payload);
マクロs2nはマクロn2sと逆の動作をします。s2nは16ビット長の値を読み込んで2バイトの値として格納するため、s2nは要求されたハートビートパケットのロードと同じ長さの値を変数payloadに格納します。プログラムは次に、pl -plで始まる新しく割り当てられたbp配列にペイロードバイトをコピーします。-plは、ユーザーが提供したハートビートパケットデータを指します。最後に、プログラムはすべてのデータをユーザーに送り返します。バグはどこに?
0x01a ユーザーは変数 payload と pl を制御できます。
ユーザがハートビートパケットで十分なデータを提供しない場合、何が問題を引き起こす可能性がありますか?例えば、plによって指されたデータが実際には1バイトしかない場合、memcpyはこのSSLv3レコードの後にあるデータをコピーします。
どうやらSSLv3レコードの周辺にはかなりのものがあるようです。
もちろん、他のプロセスからデータを読み取ることはできないので、「重要なビジネス文書」は現在のプロセスのメモリ領域にあり、64KB未満で、ポインタplが指すメモリブロックのすぐ近くになければなりません。
もしPoCを見つけたら、私に連絡してください。
0x01b 脆弱性パッチ
修正コードの最も重要な部分は以下の通り:
/* Read type and payload length first */
if (1 + 2 + 16 > s->s3->rrec.length)
return 0; /* silently discard */
hbtype = *p++;
n2s(p, payload);
if (1 + 2 + payload + 16 > s->s3->rrec.length)
return 0; /* silently discard per RFC 6520 sec. 4 */
pl = p;
このコードでは2つのことを行っています。まず1行目のステートメントで長さ0のハートビートパケットを破棄し、2行目のチェックでハートビートパケットの長さが十分であることを確認します。とても簡単です。
0x02 予兆
この抜け穴から何が学べるでしょうか?
私はC言語のファンです。C言語は、私が最初に出会ったプログラミング言語であり、仕事でも使いやすい言語でした。でも、今と比べると、C言語の限界がよりはっきりと見えてきました。
GnuTLSの脆弱性と今回のエクスプロイトから、私は以下の3つのことを行うべきだと思います:
OpenSSLのような重要なセキュリティー・インフラストラクチャのセキュリティー監査を誰かにお金を払ってやってもらうこと。
これらのライブラリに対して、広範なユニットテストと包括的なテストを記述します。
より安全な言語で代替案を書き始めましょう。
C言語でセキュアにプログラミングすることの難しさを考えると、他に解決策はないと思います。私もやってみます。
テストバージョンと検出ツールの結果:
OpenSSL 1.0.1 から 1.0.1f に脆弱性あり
OpenSSL 1.0.1g には脆弱性はありません。
OpenSSL 1.0.0 ブランチは脆弱ではありません。
OpenSSL 0.9.8 ブランチは脆弱ではありません。
http://./ed/。