Alpine Linux中的远程代码执行漏洞
Max Justicz
2018年9月13日
摘要:我在Alpine Linux的默认包管理器apk中发现了多个漏洞。Alpine是一个极其轻量级的Linux发行版,被广泛应用于Docker环境。其中最严重的漏洞(即本文重点)允许网络中间人(或恶意软件包镜像)在用户机器上执行任意代码。由于默认软件仓库未使用TLS加密传输软件包,该漏洞的危害性被进一步放大。该漏洞已被修复,Alpine基础镜像已完成更新——建议重新构建基于Alpine的派生镜像!
获得代码执行权限后,我找到了一种巧妙的方法(无需SYS_PTRACE权限)通过写入/proc/<pid>/mem
使原始apk进程以0退出码退出。这意味着Dockerfile中使用apk安装软件包的步骤被利用后仍能成功构建。
以下是我作为中间人攻击基于Alpine的Docker容器的演示片段:
[视频演示链接]
漏洞分析
任意文件创建导致远程代码执行
Alpine软件包以.apk文件格式分发,这些文件实质上是gzip压缩的tar包。当apk拉取软件包时,它会先将文件解压至根目录(/),然后才校验哈希值是否与签名清单中的记录匹配。严格来说,在解压过程中,每个文件名和硬链接目标都会添加.apk-new
后缀。当apk发现下载的软件包哈希不匹配时,会尝试删除所有已解压的文件和目录。
由于apk的"提交钩子"(commit hooks)功能,持久的任意文件写入可轻易转换为代码执行。如果我们能设法将文件解压至/etc/apk/commit_hooks.d/
并在清理过程中保留该文件,它将在apk退出前被执行。
通过控制下载的tar文件,我们可以创建持久的"提交钩子":
- 在默认不存在的
/etc/apk/commit_hooks.d/
创建文件夹(注意:解压的文件夹不会添加.apk-new
后缀) - 创建指向
/etc/apk/commit_hooks.d/x
的符号链接(名称任意,例如link
)。该链接扩展后名为link.apk-new
,但仍指向/etc/apk/commit_hooks.d/x
- 创建名为
link
的常规文件(同样扩展为link.apk-new
)。这将通过符号链接写入,最终在/etc/apk/commit_hooks.d/x
创建文件 - 当apk发现软件包哈希与签名索引不匹配时,首先会取消链接
link.apk-new
——但/etc/apk/commit_hooks.d/x
将保留!随后尝试取消链接/etc/apk/commit_hooks.d/
时会因目录非空(包含我们的载荷)而返回ENOTEMPTY错误
修复退出码
既然我们能在apk退出前在客户端执行任意代码,就必须确保apk进程优雅退出。如果在Dockerfile构建步骤中使用apk,当其返回非零退出码时该步骤将失败。
若不采取任何措施,apk将返回与其安装失败软件包数量相等的退出码(此时至少为1)。有趣的是,该值可能溢出——如果错误数量%256==0,进程将以0退出码退出,构建仍会成功(此问题已在此处修复)。
我的首次尝试是使用gdb附加进程并直接调用exit(0)。但Docker容器默认不具备SYS_PTRACE能力,因此无法实现。不过作为root用户,我们可以读写apk进程的/proc/<pid>/mem
:
|
|
具体步骤:
- 使用pidof获取apk进程的PID
- 通过
/proc/<pid>/maps
查找进程的可执行内存区域 - 将最终执行exit(0)的shellcode直接写入内存。令人惊讶的是该方法居然奏效——我原本预期写入操作会失败
当提交钩子退出后apk恢复执行时,将运行我们的shellcode。
结论
如果您在生产环境中使用Alpine Linux,应当:1. 重新构建镜像;2. 考虑向开发者捐款。apk似乎主要由一位开发者维护,他在不到一周内修复了此漏洞。Alpine的主要维护者随后很快发布了新版本。
作者插播:可能有数百家在生产环境中使用Alpine Linux的组织受此漏洞影响。其中部分组织设有漏洞赏金计划,如果类似漏洞由内部开发者发现将会提供丰厚奖励。如果漏洞赏金计划的目的是帮助组织提升安全性,那么依赖项中的关键漏洞是否也应该在一定程度上符合奖励条件?
为此我在上月推出了BountyGraph。BountyGraph为重要依赖项的漏洞赏金计划提供了众筹机制。欢迎关注!
联系方式
Max Justicz
max@justi.cz
mastodon.mit.edu/@maxj
本博客会仅发布几篇文章后就停止更新吗?敬请期待!