一个完全可复现的实验环境和一个手写的 CVE-2015-3306 漏洞利用程序—— 作为漏洞研究的学习练习而构建:补丁对比、手动触发和原始套接字漏洞利用开发。
ProFTPD 的 mod_copy 模块实现了 SITE CPFR / SITE CPTO
命令对,该命令对可在服务器端复制文件,而无需将数据传输到客户端。
在 1.3.5 版本中,两个处理程序均未检查会话是否已 认证。该模块只是假设*“没有人会在登录前发送 SITE 命令”*——这一错误的假设最终演变成了一个 CVE。任何 未认证的客户端都可以将服务器进程可读取的任何文件复制到其可写入的任何位置。
修复(提交 212d54271f,随 1.3.5a 发布)做了两件事:
copy_cpfr 和 copy_cpto 的开头添加了认证门控:
authenticated = get_param_ptr(cmd->server->conf, "authenticated", FALSE);
if (authenticated == NULL || *authenticated == FALSE) {
pr_response_add_err(R_530, _("Please login with USER and PASS"));
errno = EPERM;
return PR_ERROR(cmd);
}
CopyEngine on|off 指令——此前,在随附该模块的构建中,该模块甚至无法被禁用。完整的统一差异位于 patch.diff 中。阅读补丁本身就是一项技能:修复之处会告诉你伤口在哪里。
Dockerfile 从官方源码包编译 ProFTPD 1.3.5(最后一个存在漏洞的版本),并启用 mod_copy:
docker build -t proftpd-135 .
docker run -d --name lab135 -p 127.0.0.1:2121:21 -p 127.0.0.1:30000-30010:30000-30010 proftpd-135
docker exec lab135 chmod 777 /home/ftp
proftpd.conf 中的 PassivePorts 固定了数据通道——FTP 的双通道架构(控制通道 + 动态数据通道)是简单的容器映射失败的原因:控制端口可用,但数据端口不可用。
exploit.py 使用原始套接字——不使用 ftplib,因为库会隐藏协议,而隐藏协议恰恰正是我们要对抗的:
SITE CPFR /etc/segredo.txt -> 350 (未发送 USER/PASS:这正是漏洞所在)
SITE CPTO /home/ftp/... -> 250 (执行任意复制)
USER ftp / PASS ... -> 230 (登录只是数据外泄的路径)
PASV / RETR -> 150 -> 226 (捕获标志)
DELE -> 250 (清理:不留任何入侵痕迹)
AllowOverwrite、端口映射)决定了最终的影响范围。time.time_ns(),而非 time.time()),并且不依赖残留状态。RETR 会回复两次:先 150,然后 226。在下一条命令之前必须将两者都读取完毕。-d10)会准确告诉你哪个检查被触发了。仅供教育和授权实验环境使用。针对你不拥有或未经书面许可测试的系统运行此程序是违法的。