概念部分可以參考趨勢科技的 使用 Distroless 技巧來縮小容器映像並提升雲端資安,其中提到 Distroless 是一種的鏡像檔案,其中只包含應用程式本身以及應用程式執行時所需要的元件,並不包含套件管理員、指令列介面或其他程式。藉由這樣的方式可以減少駭客拿到容器的控制權後做更進一步利用。
鏡像的編譯部分可以參考 GoogleContainerTools/distroless,新增一個 Dockerfile 針對之前用的鏡像檔案做一個 distroless 鏡像出來,其中第一個容器只是為了編譯執行程式,而第二個容器則是為了部署用。在 python 這種不做編譯的方式看起來是沒甚麼必要,但是其他編譯式語言就會有所差別。
FROM python:3-slim AS build-env
COPY /tools /app
WORKDIR /app
FROM gcr.io/distroless/python3
COPY --from=build-env /app /app
WORKDIR /app
CMD ["reverse_server.py"]
docker build --no-cache --tag aeifkz/my-ubuntu:distroless . ;
docker run --rm -p 30089:30089 --name distroless aeifkz/my-ubuntu:distroless ;
docker exec -it distroless sh ;
# 會發現甚麼常用的指令都沒有,極度難用
ls ;
到底要如何排除這個窘境呢? 參考 How to use and build your own distroless images 可以知道透過安裝 cdebug 指令並透過 cdebug exec -it [container name] 進行偵錯,但這種方式需要有宿主機的控制權才能夠執行。這個工具的原理則可以參考 Docker: How To Debug Distroless And Slim Containers,主要是啟動一個偵錯,並於容器啟動時掛載偵錯對象的 pid 及 network,然後利用/proc/1/root 這個小技巧存取到掛載 pid 所在的檔案目錄,接著在容器內對偵錯對象新增一個 symbolic links,然後 export $PATH 資料,最後再用 chroot 切換跟目錄。
# 列出現有容器資訊,先看一下要偵錯對象的 CONTAINER ID
docker ps ;
container_id="[CONTAINER ID]"
# 開啟一個偵錯容器,並於啟動時掛載偵錯對象的 pid 及 network
# musl from Alpine (statically compiled)
docker run --rm -it --name debugger --pid container:$container_id --network container:$container_id busybox:musl sh ;
ln -s /proc/$$/root/bin /proc/1/root/.cdebug ;
# 以下是從 cdebug 的容器抄來的
export PATH=$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/.cdebug ;
chroot /proc/1/root sh ;
# 完美執行 ls
ls ;
雖然這招對一般的 distroless 容器用起來沒甚麼問題,但是如果該容器是用 read-only 啟動的話就真的沒轍了。其他參考資料 : 當遇到 Distroless Container 除錯要什麼沒什麼該怎麼辦? 你的好朋友 kubectl debug
那究竟 distroless 能夠防禦多少種攻擊呢? 如果不把相關使用指令運送進去容器的情況下來做個分析。以下是測試指令,內容參考 The difference between Docker ENTRYPOINT and CMD :
docker run --rm -it --entrypoint=sh --name distroless aeifkz/my-ubuntu:distroless ;
攻擊手法 | distroless 能否阻擋? |
---|---|
privileged + host pid | Y (找不到 nsenter) |
--cap-add=ALL + host pid | Y (找不到 nsenter) |
privileged | Y (找不到 mount、chroot) |
(CVE-2022-0492) unshare + cgroup 特權逃逸手法 | Y (找不到 unshare) |
安裝 linux_module | Y (找不到 insmod) |
docker.sock 掛載 | Y (GLIBC not found) |
作業12 : 透過以下指令建立容器,透過 nc 連到目標容器後,想辦法利用這次學習到的特性取得其他容器的 flag 檔案資訊。
docker run -d -it --rm --name flag aeifkz/my-ubuntu:v1.0 bash ;
#意思產生一個 flag 檔案
echo "ZmxhZ3toYWNrZXIgaGF0ZSBEaXN0cm9sZXNzfQo=" | base64 -d > flag.txt ;
docker cp flag.txt flag:/ ;
rm flag.txt ;
docker run -d --rm --name target -p 30089:30089 --pid container:flag aeifkz/my-ubuntu:v1.0 ;
# 開始解題
docker exec -it target bash ;
今日總結 :