\n\n\n\n\n\n\n\n\n\n\n
flawopen.com/模拟沙箱/Heartbleed 内存越界模拟器
基于浏览器的交互式内存与数据包模拟器,直观展示未经验证的 TLS 心跳长度字段如何触发服务器堆内存越界越权读取。
想象你对一只受过训练的鹦鹉说了一个4个字母的单词“BIRD”,然后要求它:“复述这个词,但你的回答必须有64,000个字母长。”鹦鹉叫了声“BIRD”,随后立即开始喋喋不休地背诵它脑海记忆中的后63,996个字符——其中包括其他客人的机密密码、私钥和银行卡号。在 Heartbleed 漏洞中,OpenSSL 盲目信任客户端声称的消息长度,却未检查数据包的实际物理大小,从而将服务器私有堆内存直接拷贝回送给了攻击者。
攻击者与 TLS 服务器握手,发送仅含 1 字节有效负载('A')的心跳包,但将长度声明篡改为 65,535 字节(0xFFFF)。
OpenSSL 通过 n2s(p, payload) 解析长度,却未检验 1 + 2 + payload + 16 <= rrec.length 是否成立。
服务器完全依据客户端伪造的声明,在堆中分配整整 64KB 的回复内存空间。
memcpy() 越过 1 字节数据包边界,将相邻堆内存中的 RSA 私钥和用户凭据一同复制并通过 TLS 发送回攻击者。
/* 直接从客户端数据包读取 16 位长度,未作边界检查 */
n2s(p, payload);
pl = p;
/* 根据客户端声明的假长度分配回复缓冲区 */
buffer = OPENSSL_malloc(1 + 2 + payload + padding);
bp = buffer;
/* 致命错误:向后复制长达 64KB 的服务器堆内存! */
memcpy(bp, pl, payload);
r = ssl3_write_bytes(s, TLS1_RT_HEARTBEAT, buffer, 3 + payload + padding);
/* 从数据包中解析 16 位长度 */
n2s(p, payload);
pl = p;
/* 修复:验证声明的载荷长度未超出实际接收到的记录总长 */
if (1 + 2 + payload + 16 > s->s3->rrec.length)
return 0; /* 静默丢弃格式畸形的心跳数据包 */
buffer = OPENSSL_malloc(1 + 2 + payload + padding);
bp = buffer;
memcpy(bp, pl, payload);