本目录是一个独立的、授权实验室内使用的复现捆绑包,用于无账户 ZendTo 到 ClamAV 代码执行链,以及其独立的默认配置 Smarty/root-cron 后续阶段。它包含:
初始 PoC 以默认的 clamav 服务账户执行调用者选择的命令。可选变体将这一无账户立足点延续为以 root 身份执行调用者选择的命令。两者默认均执行 id,拒绝字面量 /root/flag 路径,并通过 HTTPS 返回绑定 nonce 的输出。
存在漏洞的组、目录、Smarty 和 root-cron 关系均为 ZendTo Debian 软件包/安装程序的默认设置。然而,root 可达性受环境条件制约:该精确的正向测试装置使用 ext4 后端的 /var/zendto 卷、功能正常的 PHP CLI FFI/POSIX/exec,以及不受限的 clamd 进程。在另一个原本为默认配置的安装上强制启用 AppArmor/SELinux 或不兼容的文件系统语义,可能会阻断该后续阶段。
请仅在随附的一次性测试装置或你明确获授权测试的其他系统上使用本捆绑包。
权威的精确目标是一个本地的、有意不纳入版本管理的工件,位于:
image/zendto-installer-systemd-debian12.tar.zst
Git 会忽略 Docker 镜像归档。在进行精确回归之前,请在该路径放置一份授权的本地副本;scripts/setup.sh 需要其记录的 SHA-256 和镜像 ID。source-recipe/README.md 记录了如何构建并捕获源自安装程序的镜像。从活跃软件包仓库进行全新构建对拓扑测试有用,但不能假定其与锁定目标逐字节一致。
设置脚本和容器入口点会拒绝与以下配置档案的任何不一致:
完整值见 PROFILE.json。
这比仅锁定 ClamAV 软件包版本更为严格。分配器构造还依赖于 glibc、由 CVD 驱动的解析器流量、工作进程设置以及精确的目标库。捕获的镜像中已禁用 FreshClam,如果 daily.cvd 发生漂移或出现 daily.cld,启动将失败。
父工作区中当前长期运行的研究容器并非干净的测试装置:后续测试禁用了其外部发送者表单并更新了其 daily 数据库。这些更改在此处被有意排除。本捆绑包使用此前验证过原生执行的干净基线。
/sys/fs/cgroupzstd、带 venv 的 Python 3、C 编译器、file 和 GNU readelf目标与宿主机共享内核和 ASLR 实现。因此,依赖内核的映射行为仍然是可移植性变量。
同一时间只能有一个 systemd 测试装置共享宿主机的 cgroup 命名空间。在原始研究机器上,停止较旧的测试装置而不删除它:
docker stop zendto-installer-systemd-native
稍后可通过 docker start zendto-installer-systemd-native 恢复它。
在此目录下:
./scripts/verify-bundle.sh
./scripts/setup.sh
设置脚本将:
.venv;/var/zendto 卷并启动 systemd;并且clamav 身份执行
的一次性 renameat2(RENAME_EXCHANGE) 操作。默认端点仅限回环地址:
http://127.0.0.1:18084/
https://127.0.0.1:18447/
证书为自签名证书。PoC 有意对所有 HTTP 请求使用 verify=False。可在设置前选择不同的回环端口:
export ZENDTO_HTTP_PORT=19084
export ZENDTO_HTTPS_PORT=19447
./scripts/setup.sh
后续 Compose/辅助命令请使用相同的环境变量。
可随时检查目标:
./scripts/verify-target.sh
docker compose ps
干净的安装程序基线如下:
allowExternalUploads = TRUE
confirmExternalEmails = TRUE
captcha = google
其 CAPTCHA 和邮件值均为安装程序占位符,因此未经修改的归档实际上无法投递公开验证邮件。原生回归测试仅以等效的外部发送者 AuthData 行来表示这一已完成的应用程序步骤。目标代码、软件包、扫描器、权限和利用路径均未被修补。
使用以下命令创建那一行实验室数据和权限为 0600 的令牌文件:
./scripts/mint-lab-auth.py [email protected]
令牌值不会显示,并存储于:
.lab/upload-auth-token.txt
此本地辅助工具并不表示禁用了外部上传的部署可被远程利用。如果公开的外部发送者表单被禁用,即使提供了令牌文件,PoC 也会在到达 ClamAV 之前正确停止。
在正常配置的授权部署上,请省略 --external-auth-token-file:手动打开验证页面,完成 CAPTCHA,在攻击者控制的邮箱中接收目标生成的消息,并在隐藏提示符处粘贴其 URL/令牌。
此模式不发送任何 CAPTCHA、电子邮件、能力探测、上传、登录 POST 或扫描器内容。--skip-clamd-tcp-probe 还会抑制可选的只读原生 TCP/3310 检查:
mkdir -p -m 700 work
.venv/bin/python poc/zendto_6_15_8_unauth_clamav_rce.py \
https://127.0.0.1:18447 \
--enumerate-only \
--skip-clamd-tcp-probe \
--fingerprint-json work/fingerprint.json
枚举仍会产生普通的 Web 访问日志,并可能初始化正常的应用程序缓存。其 Debian/Ubuntu Apache 布局结果并不能证明原生软件包、Build ID、libc、CVD、socket 策略或 MAC 状态。本地 Docker 验证脚本为该测试装置提供了上述证据。
此操作验证实验室令牌和收件人策略,但不提交任何文件或扫描器内容:
.venv/bin/python poc/zendto_6_15_8_unauth_clamav_rce.py \
https://127.0.0.1:18447 \
--sender-email [email protected] \
--recipient [email protected]. \
--external-auth-token-file .lab/upload-auth-token.txt \
--probe-only \
--no-open-browser
[email protected]. 与此安装程序测试装置创建时输入的有意不寻常的 test. 内部域值匹配。自定义的真实部署可能需要实际授权的收件人。
警告:这不是良性的扫描器检查。在第一次尝试之前,远程模式会发送 EICAR,使用 PESpin 判别器有意使 clamd 崩溃,证明 socket 触发的恢复,并进行两次终端代码页探测以及额外的崩溃/恢复循环。被拒绝的映射会故意杀死 clamd。
使用全新的工作目录和无害命令:
run_dir="work/run-$(date +%Y%m%d-%H%M%S)"
mkdir -m 700 "$run_dir"
.venv/bin/python poc/zendto_6_15_8_unauth_clamav_rce.py \
https://127.0.0.1:18447 \
--sender-email [email protected] \
--recipient [email protected]. \
--external-auth-token-file .lab/upload-auth-token.txt \
--oracle-conditioned-attempts 72 \
--command 'id; uname -a' \
--workdir "$run_dir" \
--keep-workdir
首选的条件化路径会在提交单个损坏载体之前测试有效的 nibble-8 映射。当前的三种布局聚合是后期生命周期修正;其历史的一种布局 oracle 和同进程 RCE 阶段分别在 docs/ 中记录。
盲固定 nibble-6 回退方案也可显式使用:
.venv/bin/python poc/zendto_6_15_8_unauth_clamav_rce.py \
https://127.0.0.1:18447 \
--sender-email [email protected] \
--recipient [email protected]. \
--external-auth-token-file .lab/upload-auth-token.txt \
--restart-resample-attempts 72 \
--command 'id; uname -a' \
--workdir work/blind-run \
--keep-workdir
在独立的均匀 nibble 模型下,47 次全新映射约为 95%,72 次约为 99%。这些是几何映射概率,而非端到端可靠性保证。每次被拒绝的映射至少需要 12 秒的恢复延迟,外加强制执行的 40 秒工作进程退役期,不含扫描时间。72 次尝试的运行可能需要超过一小时。
结果不是从回调或 webshell 中读取的。DSO 会创建一个独立的、仅输出且有效期为 30 分钟的 AuthData 令牌,以 clamav 身份执行 /bin/sh -c,并通过匿名 ZendTo 页面返回有界的 stdout/stderr。成功需要一条包含以下所有一致项的带框架结果:
zendto_unauth_clamav_rce_result=1缺失、重复、截断或不匹配的证明均视为运行失败。
仅在 ./scripts/verify-target.sh 同时报告 [target] 和 [privesc-target] 成功后运行此操作。该包装器首先执行相同的无账户 ClamAV 阶段,要求其精确的有界武装证明,然后等待未更改的默认 root cron 消费者:
.venv/bin/python poc/zendto_clamav_to_root_smarty_privesc.py chain-unauth \
https://127.0.0.1:18447 \
--sender-email [email protected] \
--recipient [email protected]. \
--external-auth-token-file .lab/upload-auth-token.txt \
--oracle-conditioned-attempts 72 \
--root-command 'id; uname -a' \
--keep-workdir
令牌文件选项只是该测试装置已完成电子邮件流程的替代品。在授权的公开工作流中,请省略它并手动完成 CAPTCHA/电子邮件提示。chain-unauth 不使用任何 ZendTo 用户名或密码。
root 阶段被有意保持独立,尽管包装器自动化了其交接过程。它:
savechunk.php 上传绑定 nonce 的 PHP 武装器;clamav 身份原子性地交换克隆的 Smarty 编译目录;verify=False 下载 /zendto-root-result-<nonce>.json,要求
euid=0、精确的命令哈希、恢复标记和完成 nonce。Cron 可能使最终轮询等待近一个小时;默认超时为 3900 秒。root 命令限制为 1024 字节和 30 秒,stdout 和 stderr 各最多 1 MiB。在此一次性实验室中,其以 nonce 寻址的 JSON 有意保持 Web 可读,因此请仅使用无害的测试输出。
对于手动编排,emit 会打印要传递给无账户 PoC 的 --command 的精确命令;在武装证明之后,使用 poll 并附上打印的运行 ID 和精确 root 命令的 SHA-256。构建和审计记录见 docs/SMARTY_ROOT_BRIDGE.md。
当缺少前提条件时,此变体应安全停止。不要重叠武装尝试,也不要重试不明确的运行:失败的阶段可能遗留传入的 PHP 文件、隐藏克隆、锁或已被替换的缓存。每次 root 测试请使用全新的测试装置:
./scripts/reset-lab.sh --yes-delete-lab-volume
./scripts/mint-lab-auth.py [email protected]
每个 clamd 进程支持一次成功的损坏。只有在新运行的破坏性回收探测返回预期的扫描器失败响应,且 EICAR 证明通过活动 socket 恢复之后,第二次调用才是合理的。不要重用运行 ID、暂存块名称、结果令牌或客户端工作目录。
遇到任何重定向、网络超时、不明确的正文/状态、意外的干净响应或 EICAR 恢复失败时,立即停止。绝不要手动向状态不明的守护进程重新提交先前的损坏请求。
打包的服务具有 Restart=no。可重复性来自仍然活动的 clamav-daemon.socket,而非自动服务重启策略。如果 socket 被禁用或受到启动限制,故意的崩溃可能使扫描器不可用。
预期的目标残留物包括访问/错误日志、clamd 崩溃记录或核心转储、来自干净 oracle 防护的有界孤立声明文件、一个仅追加的 /var/zendto/incoming/Bridge<nonce>.1 DSO,以及短期的 AuthData 行。默认清理仅在一整天之后才会淘汰普通孤立声明。
root 变体还会留下 nonce PHP 武装器、隐藏克隆和锁、root 拥有的公开 JSON 结果,以及私有的 /run stdout/stderr。成功的包装器会在执行 root 命令前恢复真实的 templates_c,但这些证据文件会一直保留到容器和卷被重置。
测试后撤销实验室上传令牌:
./scripts/revoke-lab-auth.py
停止目标并保留其证据卷:
docker compose down
如需干净的全新测试装置,请仅显式删除本项目的命名卷并从锁定镜像重新启动:
./scripts/reset-lab.sh --yes-delete-lab-volume
如需将工作目录和指纹 JSON 作为证据,请在重置前对其进行归档。
docker-compose.yml exact systemd/socket target topology
PROFILE.json exact package/hash/CVD profile
image/*.tar.zst local ignored target artifact
poc/ no-account driver and support modules
third_party/ minimized builder/source/binary closure
scripts/setup.sh image/client/Compose setup
scripts/verify-bundle.sh delivery-manifest verification
scripts/verify-target.sh fail-closed target verification
scripts/verify-privesc-target.sh root-variant semantic preflight
scripts/mint-lab-auth.py local completed-email substitute
scripts/revoke-lab-auth.py remove that lab bearer
scripts/reset-lab.sh explicit clean-volume reset
source-recipe/ installer provenance, not exact rebuild
docs/ technical regression/audit notes
source-recipe/install.ZendTo.tgz 及其运行手册已包含在内,用于溯源和源码构建。不要将新的实时仓库构建视为精确:安装程序并不锁定所有软件包或 ClamAV 数据库。精确回归需要被忽略的本地镜像归档与 PROFILE.json 匹配。
MANIFEST.sha256 覆盖除自身之外的每个交付文件。Docker 镜像归档、宿主编译的 third_party/installer-runtime/clamav-pespin/pespin_summit 辅助程序,以及生成的 .venv、.lab、work 和 __pycache__ 目录树均被忽略。设置脚本会从其清单化的 C 源码重新构建该辅助程序,并单独验证本地镜像。
打包时的静态检查、远程预检、概率重采样和插桩端到端检查均记录在 docs/BUNDLE_VALIDATION.md 中。该记录将远程观察结果与明确提供了本地观察模块基址的那一次验证运行区分开来。
| 组件 | 精确测试值 |
|---|
| Docker 镜像 ID | sha256:de6d2f06ca04943362a9bdec5026e808448953728f31bd926343a4ed2ca2ef7c |
| ZendTo | 6.15-8 |
| ClamAV/libclamav 软件包 | 1.4.3+dfsg-1~deb12u2 |
| libclamav | libclamav.so.12.0.3, SHA-256 55e3cd94…027c |
| libclamav Build ID | e6427ab62146ee3001fe463d12e797e9d25bf81a |
| glibc | 2.36-9+deb12u14, SHA-256 6b4a4535…421 |
| main 数据库 | v63, SHA-256 0b2182d2…365 |
| daily 数据库 | v28082, SHA-256 cddbcccf…906 |
| bytecode 数据库 | v339, SHA-256 6d4aa01f…ffb |
| clamd | MaxThreads 12, IdleTimeout 30, Restart=no |
| 生命周期 | 启用 clamav-daemon.socket,systemd PID 1 |
| Web 运行时 | Apache 2.4.68, PHP 8.2.32 |
| MAC 姿态 | 不受限的特权 Docker 测试装置 |
| root 桥接 | 默认 clamav 位于 www-data;/var/zendto 为 root:www-data 0775 |
| root 消费者 | 默认 root 清理 cron,每小时第 25 分钟 |
| Smarty 缓存 | 确定性 Smarty 4.5.4 编译的 zendto.conf |
| 交换文件系统 | ext4 后端的命名 /var/zendto 卷 |