OpenShift Online 3.x and OpenShift Dedicated 3.x were also affected and have been patched by Red Hat.

The underlying cause is comparable to an issue in Kubernetes' kubectl cp command I found and reported in March 2018 (Kubernetes issue 61297, Red Hat bug 1564305, reference CVE-2018-1002100). The same code can be found in OpenShift's oc cp command and OpenShift's oc rsync command was vulnerable too.

Engineering a build to emit a malicious tar archive from save-artifacts was almost trivial. The main challenge was that the S2I builder makes exactly one call to the execve(2) system call to invoke bsdtar, and that's well before the artifacts are unpacked and can insert or replace executables. No dynamic library is loaded either.

The proof of concept exploit now relies on the fact that the builder container can overwrite the kernel's core_pattern, described in more detail in core(5). Once that's done the build triggers a core dump and the kernel executes the command previously written to core_pattern in the context of the host system. Here we'll only use an Nginx container, but it'd be trivial to start a custom image in a privileged container with the host filesystem mounted.

The original report was reproduced against OpenShift Container Platform v3.6.173.0.96 and v3.7.23.

Reproduction

Create a new project (depending on cluster environment this is done another way, i.e. through a control panel):

master$ oc new-project --skip-config-write poc

Assuming the client now has access to the newly created project we'll create the necessary objects:

Once finished we go for the kill. This second build will retrieve artifacts from the first build and by unpacking those overwrites the kernel's core_pattern. The assemble script will detect that the settings are in place and will trigger a core dump.

This Nginx server in a container was started by a source-to-image build which isn't supposed to have this kind of access.

Exploiting path traversal in kubectl cp

The kubectl cp command uses the tar program installed within a container to create an archive. It then proceeds to unpack the archive on the client. When the container is controlled by a malicious party who can get a victim to copy any file from a container, i.e. for debugging, they could overwrite any file writable by the victim and whose path can be predicted.

The client code doesn't set the file mode, hence the PoC uses a plain text file. If the attacker knows the path of an executable writable by the victim (or the latter runs the client as root), executables can be replaced and code execution on the client is gained. There are ways to gain code execution from non-executable files.

While not demonstrated, it's to be expected that a modified and malicious K8s API server could inject arbitrary files into any program execution request originating from a file copy and wouldn't even need a prepared and explicitly requested container.

Reproduction

For the sake of simplicity we'll use a busybox container and patch it manually, but obviously the malicious tar program could be part of the image (including a widely used base image) and be more advanced than used for the demonstration.

Now comes the part which would've to be done by the victim: copy a file from the container. The PoC code doesn't care about the source file or directory.

$ kubectl cp poc-...:/etc/hosts /tmp/container-hosts

A file has been written in /var/tmp on the client system:

$ cat /var/tmp/unexpected
Hello World

Exploiting path traversal in oc cp and oc rsync

The oc rsync command, just like kubectl cp, uses the tar program installed within a container to create an archive. See the section on exploiting kubectl cp for more details. oc cp is the same underlying code as kubectl cp.