Heartbleedのソースを見比べてみた

なんと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 へのポインタを返す。

ということです。

ネットユーザーについては問題ないかと思いきや、悪意を持ってサーバーを用意しておいて、逆にクライアントからのデータもコピーできちゃうらしい。

 

という事で、次回もこのネタでいきたいと思います。

 

Similar Posts:


Leave a Reply

Your email address will not be published. Required fields are marked *