技术背景
当在Linux或Mac系统上执行Docker命令时,系统将通过创建socket访问/var/run/docker.sock,从而同Docker daemon交互执行命令。因此,当能够直接访问/var/run/docker.sock,即拥有了对于该Docker服务器的管理权。
docker.sock同样也支持TCP协议交互。早期的Docker API配置在TCP端口2375;大量直接暴露在公网上的端口造成了严重的安全隐患,因此Docker socket被调整到localhost或是/var/run/docker.sock。
本文将以攻击者的视角,在不依赖Docker客户端的情况下,针对暴露的docker.sock端口进行一系列攻击。
01 暴露原因
Docker API开放的原因可能是因为多个开发团队共享一个Docker服务器,方便共同管理而设置为API远程访问。针对该情况,扫描端口2375和2376(SSL加密通信),即可发现暴露点;或者直接在HTTP/HTTPS相应头中搜索Docker字样。
另一种暴露/var/run/docker.sock的原因则是为了在docker容器内运行docker命令,创建 Docker-in-Docker,因此将宿主机的docker.sock挂载到容器。例如,在jenkins容器内运行docker命令执行构建镜像的操作。针对该情况,在该容器下扫描docker.sock字样,即可确定是否存在docker.sock。
02 信息收集
通过公共搜索引擎,例如shodan或者fofa,可以发现暴露docker.sock的服务器依旧存在,且不在少数。
利用fofa可匹配出大量暴露的docker.sock
在实战对抗中,当发现端口2375/2376时,可以尝试通过http/https抓取服务器返回头中“Server”是否包含Docker字样;或是使用nmap等进行扫描。
通过nmap扫描端口2375
当拿到机器或者容器权限时,也可尝试搜索docker.sock等字样判断该机器是否拥有docker服务器的访问权限。
找到本地的docker.sock
03 初始访问
当找到docker.sock的暴露点时,即可开始后续的信息收集。
版本信息的收集,可以提供更多的关于该目标的情况。例如,版本的更新情况比较真实的反映运维人员对于资产的梳理和版本的关注。如果是缺乏安全意识的运维成员,后续渗透中,其他比较常见的错误配置也值得一试。
通过curl查询目标Docker版本信息
下一步,可以列出正在运行的容器,以判断该容器环境的活跃程度。
查询目标的容器运行情况
一般情况而言,可以针对容器的创建时间来判断该Docker服务器的活跃度。在攻防对抗中,攻守的对抗博弈不止存在于技术层面,还有时机。根据Docker服务器的活跃程度,在合适的时间发动攻击,可最大化扩大战果。
另外一个重要的信息,是当前Docker下所存储的镜像。选择拉取远程镜像会产生多余的流量,甚至触发警报;如非必要,攻击者尽量就地取材,利用环境中已经存在的镜像,可以避免告警,最大化隐匿攻击。
查询目标已下载的镜像信息
如果是docker.sock本地挂载的行为,那么docker和curl命令都可以达成相同的操作,此处不再赘述。
利用curl发送unix socket 与docker.sock交互
04 执行
攻击者可以直接对容器发起命令;也可以创建新容器进行操作。前者相对于后者更为隐秘;而当Docker环境中无正在运行的容器时,创建容器是不得不做的选择;攻击者可以尽可能将容器命名为看似合法的容器。
直接对正在运行的容器上执行命令分为两步
创建Cmd指令
-
执行Cmd
需要注意的是,容器上命令不存在时,执行便不会成功。例如,当利用DNSLOG解析时,可以多换几个命令,ping,curl,wget等,以防止某些命令没有安装的情况。
通过解析dnslog判断命令执行是否成功
创建Cmd后,服务器返回的Id信息,要作为第二步执行Cmd的参数。
从DNSLOG解析,可以判断命令执行成功,并且目标出网。
获取到dnslog解析记录
此时可以通过同样的方式,进行反弹shell。除了直接调用nc——这种情况需要改容器环境安装了nc,还可以通过curl或者wget运行远程的服务器的脚本。
当Docker服务器暂无运行的容器时,尽量利用环境中现有镜像,启动一个容器,进行命令执行。
创建ubuntu:18.04容器
创建容器分为两步。第一步是配置容器的基本信息,此处需要注意,如果所需镜像不存在,Docker服务器会自动下载最新的容器镜像。
当容器信息配置完毕之后,利用start指令,启动容器。可以通过之前介绍的方法,验证容器已经正常启动。
容器创建成功
此时,可以通过命令执行,对容器进行更新和一些基础工具的安装,方便进行后续的攻击。
05 权限提升
当能够直接创建容器的时,权限提升比较直白——只需要直接挂在主机目录攻击者创建的容器里。
此时,攻击者对于运行Docker服务器的主机的文件系统拥有直接的读写权限。为了进一步直接控制主机系统,攻击者可以通过写入计划任务的方法,反弹shell。
挂载主机目录只需要在创建的容器配置里添加设置,并设置该容器为高权限容器即可。
创建容器并挂载主机目录到/host
通过之前在“执行”部分介绍的方法,反弹shell。
成功挂载主机目录
通过反弹的shell,可以看到host目录出现在该容器内,说明主机目录已被挂载成功。此时可以进一步通过写入crontab,获取到主机权限。
06 总结
本文针对容器安全中的较为常见的攻击面——暴露的docker.sock,进行了一系列的测试和思考。主要以攻击者的视角,对该弱配置可能导致的一系列隐患,进行了测试与验证。从该弱配置可能出现的原因,到利用该弱配置进行的信息收集、初试访问、代码执行和权限提升。
另外,以上攻击场景同样适用于存在SSRF漏洞的情况下。
后续的持久化、痕迹清理等等已经不在本文探讨范围,留给日后介绍。
绿盟科技M01N战队专注于Red Team、APT等高级攻击技术、战术及威胁研究,涉及Web安全、终端安全、AD安全、云安全等相关领域。通过研判现网攻击技术发展方向,以攻促防,为风险识别及威胁对抗提供决策支撑,全面提升安全防护能力。
M01N Team
聚焦高级攻防对抗热点技术
绿盟科技蓝军技术研究战队