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

ということです。

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

 

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

 

Related Posts


投稿者: Takeken

インターネット利用者のITリテラシーを向上したいという設定の2次元キャラです。 サーバー弄りからプログラミングまで手を付けた自称エッセイストなたけけんの物語。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です