なんと2年ほど脆弱な状態が続いていたという事ですが、その内容は脆弱性をついたコピーができたというものらしい。
ソースじたいは簡単に手に入るし、ソースはC言語で書かれているし、となりますと今ここで触れておかない理由がないっす。ってことで、兎にも角にもそーすに触れておきたいと思います。
使ったバージョンはこちら。ですが、安心してほしいのは1.0.1eを使っているRH系クローンを使ってる人はちゃんとアップデートして最新版のものだったらOKです。
ただし脆弱性のあるときに作ってた証明書はダメ(な可能性が高い)から作り直そう。
% ls -l /home/takeken/openssl-1.0.1f/ssl/d1_both.c
-rw-r--r-- 1 takeken takeken 44390 Jan 6 22:47 /home/takeken/openssl-1.0.1f/ssl/d1_both.c
% ls -l /home/takeken/openssl-1.0.1g/ssl/d1_both.c
-rw-r--r-- 1 takeken takeken 44715 Apr 8 01:54 /home/takeken/openssl-1.0.1g/ssl/d1_both.c
fが脆弱なものでgが修正されたものでございです。
時刻が新しいなあ。
解説サイトをじっくり読みながら調べて書いてとしているので、いつもだけどつたない文章でごめんなさい、頭の中ではそこそこまとまったものを書きたいと思います。
解説サイトで取り上げている関数は、tls1_process_heartbeat(SSL *s)というやつでした。
まずは、どういうもんかを自分なりに調べたことをざっくりと書くと、まず基本的なことはHeartBeat messageというのをサーバ、クライアントでやりとりするらしい。
リクエストが来たら返す。という基本的なもの。ゆえに脆弱だったという事のようです。
そーすを片手に読んでほしいのだけど
これが
unsigned char *p = &s->s3->rrec.data[0], *pl;
unsigned int payload;
1464 n2s(p, payload);
1465 pl = p;
これで、これなもんで。
1481 buffer = OPENSSL_malloc(1 + 2 + payload + padding);
1482 bp = buffer;
1486 s2n(payload, bp);
1487 memcpy(bp, pl, payload);
コピーされちゃいます。(memcpyのことは後の方に書いてます。)
payloadのチェックがない状態でした。
そして修正されたものには、payloadをチェックするためのunsigned int write_lengthができたようです。
これが
1471 n2s(p, payload);
1472 if (1 + 2 + payload + 16 > s->s3->rrec.length)
1473 return 0; /* silently discard per RFC 6520 sec. 4 */
1474 pl = p;
これで、これなもんで。
1479 unsigned int write_length = 1 /* heartbeat type */ +
1480 2 /* heartbeat length */ +
1481 payload + padding;
1491 buffer = OPENSSL_malloc(write_length);
1492 bp = buffer;
1486 s2n(payload, bp);
1487 memcpy(bp, pl, payload);
ここだけ抜粋したプログラムなら同じ初級者でも分かると思う。
n2sならびにs2nは関数形式マクロでコピーの定義がされています。
全体で見ると訳がわかりませんな。でもそこは今は重要じゃないんだ!と言い聞かせる。
んで、さっきのpayloadを悪用すると、メモリ領域の64kバイトをコピーできちゃうという事らしい。
ようは盗まれるという事だね。
ていっても64k~(笑)、って思うかもしれないけど、1回で64kなんで、何回もやればもっと多いのだ。
64kバイトというと、ファミコンのスーパーマリオブラザーズが40kバイトってのを基準にするとわりとでかい(ように感じる)ね。
memcpyを忘れてたね。
名前
memcpy - メモリ領域をコピーする。
書式
#include <string.h>
void *memcpy(void *dest, const void *src, size_t n);
説明
memcpy() はメモリ領域 src の先頭 n バイトを メモリ領域 dest にコピーする。
コピー元の領域と コピー先の領域が重なってはならない。重なっている場合は memmove(3)
を使うこと。
返り値
memcpy() は dest へのポインタを返す。
ということです。
ネットユーザーについては問題ないかと思いきや、悪意を持ってサーバーを用意しておいて、逆にクライアントからのデータもコピーできちゃうらしい。
という事で、次回もこのネタでいきたいと思います。