記錄一次逆向容器鏡像的過程
環境準備
- 安裝Docke
curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun
- 安裝DIVE
wget https://github.com/wagoodman/dive/releases/download/v0.9.2/dive_0.9.2_linux_amd64.debsudo apt install ./dive_0.9.2_linux_amd64.deb
鏡像分析
選擇要分析的鏡像為ubuntu的官方鏡像,首先導出鏡像,保存為ubuntu.tar
sudo docker pull ubuntu sudo docker save -o ubuntu.tar ubuntu
為了方便分析,這里將ubuntu.tar在windows上面解壓,可以發現ubuntu鏡像一共包含三個文件和一個文件夾。

首先查看manifest.json的內容,可以看出該文件一共包含了三個字段,Config、RepoTages和Layers。
- Config 的值為鏡像配置文件的json文件,對應825d55fb6340083b06e69e02e823a02918f3ffb575ed2a87026d4645a7fd9e1b.json;
- RepoTages 為鏡像的名稱和標簽;
- Layers 包含了鏡像的有哪些層,每一個元素代表一個層目錄,由此可見ubuntu只含有一個層,對應
- 8f7ee37aa1d53dcded9b5c22b0a57d8bb8d35d9f42273651668e7aca23bd7581/layer.tar
[ { "Config": "825d55fb6340083b06e69e02e823a02918f3ffb575ed2a87026d4645a7fd9e1b.json", "RepoTags": [ "ubuntu:latest" ], "Layers": [ "8f7ee37aa1d53dcded9b5c22b0a57d8bb8d35d9f42273651668e7aca23bd7581/layer.tar" ] }]
接著查看
825d55fb6340083b06e69e02e823a02918f3ffb575ed2a87026d4645a7fd9e1b.json的內容,該文件記錄了鏡像的關鍵信息,
https://github.com/moby/moby/blob/master/image/spec/v1.2.md
上述鏈接簡述了每一個字段的意義,如config包含了鏡像生成容器時基礎的執行參數,Cmd為容器入口點的默認參數 等。
我們主要關注的是 history 列表,它列出了鏡像中的每一層,Docker 鏡像由這些層堆疊而成。Dockerfile 中幾乎每條命令都會變成一個層,描述該命令對鏡像所做的更改。在ubuntu鏡像中,可以看到history列表實際上有兩層, 但是其中一層的empty_layer 標記為 true,這代表著本次操作不改變文件系統鏡像,不額外生成新的層,所以ubuntu鏡像實際上只有一層。
{ "architecture": "amd64", "config": { "Hostname": "", "Domainname": "", "User": "", "AttachStdin": false, "AttachStdout": false, "AttachStderr": false, "Tty": false, "OpenStdin": false, "StdinOnce": false, "Env": [ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" ], "Cmd": [ "bash" ], "Image": "sha256:ccb5a0910a0c4d62d88fd6aec23d035803c808719cc5ce148878f352b1a2a540", "Volumes": null, "WorkingDir": "", "Entrypoint": null, "OnBuild": null, "Labels": null }, "container": "3f151de249ccf525ce2ef3806956fd20f3d4c46ab831529056dde22d50146d4b", "container_config": { "Hostname": "3f151de249cc", "Domainname": "", "User": "", "AttachStdin": false, "AttachStdout": false, "AttachStderr": false, "Tty": false, "OpenStdin": false, "StdinOnce": false, "Env": [ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" ], "Cmd": [ "/bin/sh", "-c", "#(nop) ", "CMD [\"bash\"]" ], "Image": "sha256:ccb5a0910a0c4d62d88fd6aec23d035803c808719cc5ce148878f352b1a2a540", "Volumes": null, "WorkingDir": "", "Entrypoint": null, "OnBuild": null, "Labels": {} }, "created": "2022-04-05T22:20:51.04675426Z", "docker_version": "20.10.12", "history": [ { "created": "2022-04-05T22:20:50.696744314Z", "created_by": "/bin/sh -c #(nop) ADD file:b83df51ab7caf8a4dc35f730f5a18a59403300c59eecae4cf5779cba0f6fda6e in / " }, { "created": "2022-04-05T22:20:51.04675426Z", "created_by": "/bin/sh -c #(nop) CMD [\"bash\"]", "empty_layer": true } ], "os": "linux", "rootfs": { "type": "layers", "diff_ids": [ "sha256:c5ec52c98b3193052e15d783aca2bef10d8d829fa0d58fedfede511920b8f997" ] }}
在docker中使用docker inspect 和docker history 命令也能查看鏡像或容器的相關信息,該信息與上訴是一一對應的。

docker history ubuntu
命令對應上訴
825d55fb6340083b06e69e02e823a02918f3ffb575ed2a87026d4645a7fd9e1b.json 的history字段。
root@ubuntu:/home/null# docker history ubuntuIMAGE CREATED CREATED BY SIZE COMMENT825d55fb6340 2 weeks ago /bin/sh -c #(nop) CMD ["bash"] 0B<missing> 2 weeks ago /bin/sh -c #(nop) ADD file:b83df51ab7caf8a4d… 72.8MB
接下來打開
8f7ee37aa1d53dcded9b5c22b0a57d8bb8d35d9f42273651668e7aca23bd7581文件夾,該文件夾包含三個文件。
VERSION json layer.tar
其中VERSION代表ubuntu的版本,值為1.0;json文件的很多內容是與上面是重合的。鏡像的核心內容在layer.tar里,將該文件解壓,可以發現該文件夾的內容就是對應linux文件系統。在ubuntu的鏡像中,大部分文件存在于usr/bin目錄,對應了ubuntu系統的 一些常見的命令,如ls、apt-get等。

使用DIVE能夠更好的查看鏡像每一層的內容,和各層比上一層做出的改變,下圖為dive分析ubuntu的界面。DIVE主要具有以下主要功能:
- 屏幕左上角提供鏡像層列表以及與每個鏡像層的大小。
- 提供有關鏡像的效率(百分比值)、潛在浪費的空間以及鏡像的總大小的一般統計信息。
- 對于每個選定的鏡像層,右邊會顯示出該層對應的文件系統視圖,其中包含每個文件夾大小的數據。

DIVE使用的一些快捷鍵:
Ctrl+Spacebar - 左右面板切換 Spacebar - 打開/關閉目錄樹, Ctrl+A - 顯示/關閉添加的文件, Ctrl+R - 顯示/關閉移除的文件, Ctrl+M - 顯示/關閉修改的文件, Ctrl+U - 顯示/關閉未修改的文件, Ctrl+ L - 顯示鏡像層變動, Ctrl+/ - 過濾文件, Ctrl+C - 退出.
由于ubuntu鏡像只有一層,所以看不出層與層之間內容的變化,所以這里啟動ubuntu容器,并像容器中寫入一些內容來觀察在容器中進行操作對于容器鏡像的改變。
root@ubuntu:/home/null# docker run -it --name ubuntu_test ubunturoot@871b5aa0e0c5:/# echo hello > hello.txtroot@871b5aa0e0c5:/# echo world > world.txtroot@871b5aa0e0c5:/# exitexit
接著將容器打包成鏡像并導出,以便進一步分析
docker commit ubuntu_test ubuntu_test docker save -o ubuntu_test.tar ubuntu_test
將ubuntu_test.tar解壓,發現相比于原來的ubuntu.tar解壓后的文件,多了一個文件夾,也就意味著多了一層。
查看manifest.json查看變化,發現Layers中也已經多了一層
"804da1860ebbda2e61e48e68b0fd1c7f2ddebfcfbc18dbb5c865d2b76d01cc29/layer.tar"
查看
05feb719705701fda9a39795e16f245df59db1c26261112f88d81131511e6111.json文件的history字段,同樣的多出了一層: created表示創建的時間,created_by表示創建的命令。
"history": [ { "created": "2022-04-05T22:20:50.696744314Z", "created_by": "/bin/sh -c #(nop) ADD file:b83df51ab7caf8a4dc35f730f5a18a59403300c59eecae4cf5779cba0f6fda6e in / " }, { "created": "2022-04-05T22:20:51.04675426Z", "created_by": "/bin/sh -c #(nop) CMD [\"bash\"]", "empty_layer": true }, { "created": "2022-04-22T02:20:20.681237703Z", "created_by": "bash" } ],
打開新文件夾,解壓layer.tar里面的內容便為我們對容器的操作添加更改的內容,一共更改了三處,其中hello.txt和world.txt是我們手動添加的文件,root文件夾下的.bash_history是記錄執行過命令。

sudo dive ubuntu_test
tap鍵可以切換左右視圖,上下鍵進行切換條目,在右邊可以很明顯的表示第二層相比于第一層改變的地方,黃色表示改變的目錄,綠色代表改變的文件,剛好對應新生成文件夾的內容。

其他工具
下面分享一些對容器鏡像進行分析的網站或工具
1.contains,一個支持在線分析容器鏡像的網站。
https://contains.dev/
2.trivy,鏡像掃描工具,可以檢測鏡像、文件系統、git存儲庫的漏洞以及配置問題
https://github.com/aquasecurity/trivy
3.Clair,靜態分析容器鏡像中的漏洞
https://github.com/quay/clair
4.Anchore,用于深度分析docker鏡像,掃描容器鏡像和文件系統中的漏洞。
https://anchore.com/opensource/
5.Dagda,用于對 docker 鏡像和容器中的木馬、惡意軟件、病毒等已知漏洞進行靜態分析
https://github.com/eliasgranderubio/dagda/
6.Aqua Security,保護使用容器等云原生技術構建的應用程序。
https://www.aquasec.com/products/container-vulnerability-scanning/