ClamAV 1.5.x 三个零日漏洞:双free、远程代码执行、地址泄露

导语:又一个被忽视的老牌安全软件出了事。8月4日,研究团队Zer0SumGam3在GitHub上公开了ClamAV 1.5.x系列的三个内存破坏漏洞——其中之一涉及远程代码执行。这三个漏洞全部由AI工具”Clanker”发现,发现者本人分文不取,称”协调披露已经没有意义,有Clanker的人都能发现同样的问题”。Cisco已收到通知,但CVE编号尚未分配。


一、事件概述

这次披露包含三个独立的ClamAV漏洞,均影响1.5.0至1.5.3版本以及测试版开发分支:

编号漏洞类型最高危害
01ZIP目录浅拷贝双重释放(double free)进程崩溃 + 间接分支控制
02ZIP索引容量反同步(desync)远程代码执行(RCE)
03STATS命令泄露栈地址6字节内存地址暴露

目标环境为ClamAV 1.5.3(commit b970812e9b6427e361c362762d94f7356597efe4),使用官方Debian arm64镜像:

clamav/clamav@sha256:9ea1c2bc1955c58d094bb3d168718d0173c7899096012523278ad76e06cc75a2

关于AI发现这件事:研究者特别强调,这三个漏洞”100%由Clanker生成”,自己只是”加了点人类评论来收尾”。他甚至直言:”协调披露适用于有人真的会在披露期间发现同样漏洞的场景——但现在谁有个Clanker都能发现。所以:代码公开或者发了二进制?那唯一负责任的披露就是全公开。”


二、漏洞一:ZIP目录双重释放(double free)

2.1 漏洞原理

libclamav的ZIP解析代码中,index_local_file_headers()函数将struct zip_record条目浅拷贝到合并目录中。问题在于结构体包含指向original_filename的指针,当重叠验证拒绝合并时,清理逻辑会先通过源目录释放文件名指针,再通过合并目录对同一指针再次释放——同一块内存被free两次。

引入漏洞的提交是aadf25df6a738544eb8d99b2b66e6cb42f9db385。ASan(地址 sanitizer)追踪显示:分配发生在parse_local_file_header()libclamav/unzip.c:731,第一次和重复释放均发生在index_local_file_headers()的第1664和1676行。

关键代码模式是直接的结构体赋值:

combined_catalogue[i] = temp_catalogue[temp_catalogue_offset];

由于复制的是指针而非转移所有权,错误路径下同一内存被释放两次。

2.2 危害程度

748字节的畸形ZIP文件就能确定性终止ClamAV 1.5.3守护进程。在arm64架构上,配合精心构造的堆布局,可进一步将缺陷升级为受控的cl_fmap.need回调 corruption,甚至选择间接分支目标(研究者选择了无效地址0x0000ffff00011000作为”惰性控制流完整性”证明,不涉及命令执行)。

2.3 检测与修复

检测脚本(PoC生成器):

// poc/gen_zip_catalogue_double_free.js
// 生成748字节的触发文件

官方修复补丁patches/0001-unzip-move-filename-ownership.patch

核心思路:在结构体拷贝后立即将源指针置空,使源目录失去文件名所有权,合并目录独占。这样错误路径下的清理逻辑就能正确地只释放一次。


三、漏洞二:ZIP索引容量反同步导致RCE

3.1 漏洞原理

index_local_file_headers_within_bounds()函数在重用zip_record数组时,从逻辑元素计数重建容量。受限扫描可能留下99条记录,但实际分配已扩展到200个槽位。重新进入时,函数将数组视为一个100记录块处理,下一条被接受的记录使索引达到100,后续写入测试100 == 101失败,恢复点被跳过,结构化的32字节记录开始顺序写入超过分配边界的内存。

漏洞引入提交:8d8433b5ef3a84710c39cd0778b90e71fc91a0d9,ClamAV 1.4.5不受影响。

完整失败序列:

  1. 首次受限扫描分配100条记录
  2. 第99条被接受的记录将实际分配扩展到200槽
  3. 重新进入时从计数99重建一个100记录块
  4. 解析下一条记录使索引升至100,写后测试100 == 101失败
  5. 块计数仅在错过的分支内改变,槽200及之后为越界写入

3.2 RCE利用链

在固定的Debian arm64 1.5.3环境中,研究者完成了完整的利用链:结合ClamAV正常的DMG、HFS+、RTF、OLE10和ZIP解析器,corrupt活动RTF回调状态,通过glibc tcache回收stale对象,调用ClamAV自带的cli_memcpy包装函数,最终将后续回调改为libcsystemexecvp

关键数据:

  • 确定性实验:使用26字节测试数据库 + 单worker配置,稳定复现RCE
  • 默认配置实验:使用完整官方数据库(版本28079/28080),需要oracle控制
  • 盲打实验:固定字节campaign,19038次尝试后在第19038次收到回调成功,过程中未读取目标内存映射

3.3 重要限制

这个RCE高度依赖签名数据库字节。数据库字节影响匹配器分配和堆排序,完整官方数据库快照未被包含在公开package中(太大)。后续freshclam更新可能改变堆几何图形,使保存的exploit artifact失效——但这不代表底层解析器缺陷被修复。

触发命令nc a 9(在campaign拥有的Docker网络内的监听别名)

检测脚本

# poc/gen_zip_index_desync.js — 小型根因生成器
# poc/clamav_153_rtf_rce.py — 标准库完整artifact构造器
# poc/full_stock_instream_bruteforce.py — 共享隔离Docker campaign实现

官方修复补丁patches/0001-unzip-track-catalogue-capacity.patch

核心思路:跨受限重新进入跟踪分配容量,在向解析器传递下一槽位之前检查/增长数组;在不可能的计数/容量关系时关闭失败,并保护大小算术。


四、漏洞三:STATS命令栈地址泄露

4.1 漏洞原理

ClamAV 1.5.3的clamd在共享任务描述符中发布指向扫描worker栈上文件名缓冲区的指针。不同的worker遍历这些描述符以生成STATS响应,并使用%s格式化借来的指针,但未与其生命周期同步。

根因代码(clamd/scanner.c):

char fdstr[32];  // 在worker栈上创建
thrmgr_setactivetask(fdstr, NULL);  // 发布该地址,无锁

thrmgr_printstats()持有全局池锁的同时格式化并发送响应:

mdprintf(f, "t%s %f %sn", ..., task->filename ? task->filename : "");

这是一个数据竞争加作用域后使用(use-after-scope),有协议响应泄露出口。清除描述符不能撤销已被STATS worker加载的指针。

4.2 泄露实证

三次Fresh ASLR启用运行中,六字节线序重建了栈指针,并推导出了独立记录的libclamav和libc基址:

运行线序指针推导/实际 libclamav推导/实际 libc
10xffff6a21c3c00xffffa87e00000xffffa8620000
20xffff4af3c3c00xffff895000000xffff89340000
30xffff4e9ac3c00xffff8cf700000xffff8cdb0000

固定关系(精确镜像/worker顺序/配置的属性,非ClamAV ABI):

libclamav_base = stack_pointer + 0x3e5c3c40
libc_base      = stack_pointer + 0x3e403c40

在测试布局中,泄露还提供了单独披露的RTF/ZIP利用链所需的代码地址输入和相关arena字节。但消耗该泄露仍需要该链匹配的、依赖数据库的堆几何——泄露本身不能让保存的RCE artifact数据库无关化。

4.3 限制与检测

15秒普通客户端压力测试完成6,986次STATS回复而无泄露,所以自然无调试器场景下的可靠性未建立。未声称单daemon泄露-然后构建-然后回调运行。

验证脚本

# poc/verify_stats_stack_leak.py
python3 ./poc/verify_stats_stack_leak.py 
  ./evidence/run-aslr-01 
  ./evidence/run-aslr-02 
  ./evidence/run-aslr-03

官方修复补丁patches/0001-thrmgr-own-and-lock-task-filenames.patch

核心思路:将文件名复制到task自有的存储中,在STATS读者使用的同一锁下交换它,并在读者不再能观察它之后释放旧有值。同时同步读者使用的相邻描述符字段。


五、影响范围与修复建议

受影响版本

  • ClamAV 1.5.0、1.5.1、1.5.2、1.5.3
  • 测试版开发快照

不受影响

  • ClamAV 1.4.5(漏洞引入在1.5.x分支)

修复建议

  1. 等待官方补丁:Cisco已收到PSIRT通知,预计将在未来版本中修复。关注ClamAV官方博客获取更新。
  2. 应用社区补丁:项目仓库的patches/目录提供了针对commit b970812e9b6427e361c362762d94f7356597efe4的临时补丁,可手动应用。
  3. 缓解措施
  • 限制不可信文件提交到clamd扫描服务
  • 监控clamd进程异常终止
  • 网络隔离部署,不暴露clamd socket给未授权方
  1. 临时规避:如果不需要ZIP扫描功能,可考虑在邮件网关层面阻止ZIP附件(但这会影响正常业务)。

六、相关资源

GitHub仓库Zer0SumGam3/clamav-zero-days

PoC文件列表

漏洞PoC文件用途
01-双重释放gen_zip_catalogue_double_free.js生成748字节触发文件
01-双重释放gen_zip_predecessor_bridge_interior.js生成CFI验证artifact
01-双重释放reproduce-stock.shloopback stock镜像驱动
02-RCEgen_zip_index_desync.js小型根因生成器
02-RCEclamav_153_rtf_rce.py完整RCE artifact构造器
02-RCEfull_stock_instream_bruteforce.py盲打campaign实现
03-地址泄露verify_stats_stack_leak.py离线泄露验证

LAB环境

  • 01-zip-catalogue-double-free/LAB.md — Docker复现环境
  • 02-zip-index-desync-rce/LAB.md — 确定性callback lab
  • 03-clamd-stats-address-disclosure/LAB.md — GDB调度复现

七、总结

ClamAV作为最广泛使用的开源杀毒引擎之一,其任何漏洞都值得关注。这次披露的三个漏洞——尤其是第二个ZIP索引反同步导致的RCE——证明了即使是成熟的开源项目,在AI辅助发现时代也难以避免被”挖穿”。

研究者的态度也值得玩味:既然AI能在数小时内发现漏洞,协调披露等待90天甚至更久已经没有意义。全公开让下游用户能立即用AI修复,而不是被动等待厂商响应。

对于安全运维人员来说,与其期待Cisco尽快发布补丁,不如先检查自己的ClamAV部署是否暴露了clamd socket,同时关注仓库的补丁更新。毕竟——用AI发现的问题,用AI修也不是不行。

版权声明:本文由华盟网原创发布,保留所有权利。配图由华盟网授权使用。

© 版权声明
THE END
喜欢就支持一下吧
点赞15 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容