HEARTBLEED
Created by Dennis / @cdepillabout
- 今月話題になったHeartBleedの説明をしたいと思います
- もともとセキュリティに興味があって
- 最初の仕事がLinuxのセキュリティに関係している仕事でした
- HeartBleedを簡単に説明しているネットコミックがあって共有したいと思います。
- (read the comic...)
- リクエストのサイズについて嘘を言っています。
- 結果としては、クライアントはメモリ上の任意のデータを読み取れるんです。
問題のコード
int dtls1_process_heartbeat(SSL *s)
{
unsigned char *p = &s->s3->rrec.data[0], *pl;
unsigned short hbtype;
unsigned int payload;
hbtype = *p++;
n2s(p, payload);
pl = p;
...
buffer = OPENSSL_malloc(1 + 2 + payload);
bp = buffer;
*bp++ = TLS1_HB_RESPONSE;
s2n(payload, bp);
memcpy(bp, pl, payload);
bp += payload;
r = dtls1_write_bytes(s, TLS1_RT_HEARTBEAT, buffer,
1 + 2 + payload);
...
}
- C言語がわからない人?
- (explain C's memory layout: stack and heap)
- これはクライアントからのリクエスト
- クライアントが渡しているリクエストのサイズをチェックしないで信じて使っちゃうところです
- レスポンスのメモリを確保しているところです
- 任意のメモリをユーザに渡している
リクエストってどうなっている?
int dtls1_process_heartbeat(SSL *s)
{
unsigned char *p = &s->s3->rrec.data[0], *pl;
unsigned short hbtype;
unsigned int payload;
}
リクエストの構成
Created with Raphaël 2.1.2requesttype1 byteheartbeatlength2 bytesheartbeat text...4 bytes
- リクエストがヒープ領域で作られている
- サイズのところに、リクエストサイズを書きます
- 通常だと、サイズが小さい
チェック無し
unsigned int payload;
hbtype = *p++;
n2s(p, payload);
pl = p;
リクエストの構成
Created with Raphaël 2.1.2requesttype1 byteheartbeatlength2 bytesheartbeat text...4 bytes↑65536
- チェックしないでユーザーが指定したサイズを信じてしまいます
- 悪いユーザーだと、小さいリクエスト送ってきたのに嘘ついてサイズをとても大きく指定します
大きすぎるmalloc
buffer = OPENSSL_malloc(1 + 2 + payload);
bp = buffer;
リクエストの構成
Created with Raphaël 2.1.2requesttype1 byteheartbeatlength2 bytesheartbeat text...4 bytes↑65536
- (draw on board)
- heap領域でレスポンスのメモリを確保
memcpyあるある
unsigned char *p = &s->s3->rrec.data[0], *pl;
...
hbtype = *p++;
n2s(p, payload);
pl = p;
...
*bp++ = TLS1_HB_RESPONSE;
s2n(payload, bp);
memcpy(bp, pl, payload);
bp += payload;
リクエストの構成
Created with Raphaël 2.1.2requesttype1 byteheartbeatlength2 bytesheartbeat text...4 bytes↑65536
- (draw on board)
- 本当のリクエストのサイズが小さいのにそれを超えてメモリをコピーしてユーザーに返却します
- ユーザーは、超えた分のメモリが読めます
- サーバの秘密鍵を読めることもあります