リンク
CVE-2014-9295の参考情報
http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-9295
https://bugzilla.redhat.com/show_bug.cgi?id=1176037
CVE-2014-9295は以下のバグを含んでいます
http://bugs.ntp.org/show_bug.cgi?id=2667
http://bugs.ntp.org/show_bug.cgi?id=2668
http://bugs.ntp.org/show_bug.cgi?id=2669
バグの利用コード
以下はバグの分析になります、ntpdに関してはあまり詳しくないのでちょっとしか読めません。
Buffer overflow in crypto_recv()
修正パッチ
解説
まずはRSA_public_encryptとRSA_private_decryptはどう動くかを見てください。
https://www.openssl.org/docs/crypto/RSA_public_encrypt.html
RSA_public_encryptは指定した平文をRSAに通して暗号化します
char buffer[128]; // RSA_size(1024)
RSA_public_encrypt(4, "\x00\x00\x00\x01", buffer, rsa, RSA_PKCS1_OAEP_PADDING);
次にRSA_private_decryptは暗号化したデータを復号化します
もし平文が4 bytesなら、cookie_bufは先頭の4 bytesしか書き込まれません。
char cookie_buf[128];
RSA_public_encrypt(128, buffer, &cookie, rsa, RSA_PKCS1_OAEP_PADDING);
問題のあるコードはcookieの平文を4 bytesしか想定していません、
temp32のサイズは4 bytesなのでもし平文が4 bytes以上ならばオーバーフローが発生します。
オーバーフローする最大のサイズはRSA_size(host_pkey->pkey.rsa) - 41 - 4
になります。
u_int32 temp32;
...
if (RSA_private_decrypt(vallen,
(u_char *)ep->pkt,
(u_char *)&temp32,
host_pkey->pkey.rsa,
RSA_PKCS1_OAEP_PADDING) <= 0) {
rval = XEVNT_CKY;
修正後のコードはtemp32を使わず代わりにcookie_bufを動的に確保します、サイズはRSA_private_decryptが書き込める最大のサイズRSA_size(host_pkey->pkey.rsa)
、これでオーバーフローが発生しなくなりました。
このあとは平文サイズもチェックされます、4 bytesでなければエラーを返すようになりました。
Buffer overflow in ctl_putdata()
修正パッチ
解説
普通のmemcpyバグです。
dataptは送信するパケットデータのバッファ、サイズはdataend - datapt
。
ctl_putdata(const char *dp, unsigned int dlen, int bin)
関数はdpの指すデータをdataptに書き込みます。
元の処理はサイズが足りない場合、ctl_flushpkt
で現存のデータを送信してdataptをrpkt.u.dataにリセットします。
しかしリセットしてもなお足りない場合(dlen + overhead > CTL_MAX_DATA_LEN
)はオーバーフローを引き起こします。
修正したコードではサイズが足りない場合は分割して送信するようになりました。
しかしバグはまだ残っています。
この一行はポインタオーバーフローを引き起こす可能性があります。
while (dlen + overhead + datapt > dataend)
正しくはこう書くべきです
while (dlen + overhead > dataend - datapt)
ポインタオーバーフローについてはこの記事を参考してください
http://lwn.net/Articles/278137/
buffer overflow: configure()
修正パッチ
解説
また普通のmemcpyバグです。
修正したコードではreqptからreqendまでのデータをremote_config.bufferにコピーする前にサイズをチェックするようになりました。
まとめ
バッファに対する操作のコードを書くとき、十二分の注意を払わなければいけません。
それができなければrustまたはjavaのようなメモリー安全な言語を使うべきです。
バグを検出するために以下のツールが使えます。
http://valgrind.org/
http://code.google.com/p/address-sanitizer/
http://cppcheck.sourceforge.net/
http://clang-analyzer.llvm.org/
しかしこれらのツールが2667みたいなバグを検出できるとは思えません、ツールだけを頼ってもだめです。