容器取证

容器使我们能够以更快、更可靠的方式构建和部署应用程序,从而彻底改变了现代软件开发。因此,近年来,容器化变得越来越流行。但是,与任何技术一样,它们也不能幸免于安全风险和漏洞。犯罪分子还可以将它们用于恶意目的,例如托管恶意软件或进行任何其他非法活动。

在本实验中,我们将了解容器的基础知识,然后我们将探讨可用于对容器化环境进行调查以识别潜在安全威胁和恶意活动的工具和技术。

容器-它们是什么?

容器是软件包,其中包含在任何环境中运行所需的所有元素。通过这种方式,容器虚拟化了操作系统,并在任何地方运行,从私有数据中心到公共云,甚至在开发人员的个人笔记本电脑上。从 Gmail 到 YouTube 再到 Google 搜索,Google 的所有内容都在容器中运行。

简单地说,容器就像一个应用程序的捆绑包,它拥有运行所需的一切,包括应用程序的代码和配置,以及依赖项和库。

想象一下,您正在开发一个应用程序,并且代码需要特定版本的编程语言和库才能运行。您可以将它们安装在自己的计算机上,但这可能会导致与您可能已安装的其他语言或库版本发生冲突。即使您设法解决了计算机上的所有冲突,也可能会在将要部署应用程序的托管服务器上遇到类似的冲突。

或者,您可以将应用程序及其依赖项打包到一个容器中,该容器将使用所需的编程语言和库版本运行应用程序,而不会与主机上安装的版本发生任何冲突。然后,您可以将此容器移动到另一台计算机或服务器,应用程序将与您自己的计算机上完全相同。

容器和虚拟机-有何区别?

你可能已经熟悉VM:虚拟操作系统(如 Linux 或 Windows)在可访问底层硬件的主机操作系统上运行。容器通常与虚拟机 (VM) 进行比较。与虚拟机一样,容器允许您将应用程序与库和其他依赖项打包在一起,从而为运行软件服务提供隔离的环境。

什么是Docker?

Docker 是一种容器编排工具,可帮助我们管理容器的整个生命周期,包括构建、运行和终止容器。它有时也被称为“容器引擎”或“容器运行时”,还有其他类似的工具,如 Podman、Cri-o、Runc、Containerd 等也提供这些功能。

获取证据

在容器取证的上下文中,我们的证据将主要包括我们可以从容器(正在运行或停止)获取的任何信息、容器映像、容器日志、容器运行时/引擎日志等。我们可以通过多种方式获取与容器相关的证据,包括导出其文件系统、检查容器与其基础映像之间的差异、检查容器的元数据和配置、查看容器日志、检查映像历史记录以及获取容器内运行的进程的内存转储。因此,让我们逐一探索这些方法中的每一种:

导出文件系统

我们获取证据的一种方法是将容器的文件系统导出为 tar 存档。

为了说明这个过程,让我们启动一个容器:

1
2
3
$ docker run -it alpine sh
/ # ls
bin dev etc home lib media mnt opt proc root run sbin srv sys tmp usr var

在容器中,让我们创建一个名为 hello_world 里面有文字“你好,世界!

1
#/ echo "Hello, World!" > hello_world

接下来,在保持容器运行的同时,打开另一个终端并检查容器的 ID:

1
2
3
$ docker ps --all
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
87908a159c5b alpine "sh" 6 minutes ago Exited (0) 6 minutes ago agitated_matsumoto

现在,让我们将容器的文件系统导出为 tar 存档:

1
$ docker export 87908a159c5b -o output.tar

然后,我们可以提取文件系统并确认 hello_world 通过运行以下命令显示文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
$ mkdir output

$ tar -xf output.tar -C output

$ ls -la output
total 4.0K
drwxr-xr-x 1 w w 156 Apr 12 05:59 .
drwxr-xr-x 1 w w 52 Apr 12 05:58 ..
-rwxr-xr-x 1 w w 0 Apr 12 05:47 .dockerenv
drwxr-xr-x 1 w w 862 Mar 29 19:45 bin
drwxr-xr-x 1 w w 26 Apr 12 05:47 dev
drwxr-xr-x 1 w w 566 Apr 12 05:47 etc
-rw-r--r-- 1 w w 14 Apr 12 05:47 hello_world
drwxr-xr-x 1 w w 0 Mar 29 19:45 home
drwxr-xr-x 1 w w 282 Mar 29 19:45 lib
drwxr-xr-x 1 w w 28 Mar 29 19:45 media
drwxr-xr-x 1 w w 0 Mar 29 19:45 mnt
drwxr-xr-x 1 w w 0 Mar 29 19:45 opt
dr-xr-xr-x 1 w w 0 Mar 29 19:45 proc
drwx------ 1 w w 24 Apr 12 05:47 root
drwxr-xr-x 1 w w 0 Mar 29 19:45 run
drwxr-xr-x 1 w w 782 Mar 29 19:45 sbin
drwxr-xr-x 1 w w 0 Mar 29 19:45 srv
drwxr-xr-x 1 w w 0 Mar 29 19:45 sys
drwxr-xr-x 1 w w 0 Mar 29 19:45 tmp
drwxr-xr-x 1 w w 40 Mar 29 19:45 usr
drwxr-xr-x 1 w w 86 Mar 29 19:45 var

$ cat output/hello_world
Hello, World!

识别容器和基础映像之间的差异

这涉及将正在运行或停止的容器的当前状态与其基本映像进行比较,以识别所做的任何更改。这在检测恶意软件或未经授权的实体可能对容器所做的任何更改时特别有用。

识别差异的一种方法是使用 docker diff 命令,根据 Docker 的文档,跟踪三种类型的更改:

我们可以在上一个示例中创建的容器上试用它:

1
2
3
4
$ docker diff 87908a159c5b
C /root
A /root/.ash_history
A /hello_world

从输出中可以看出, /root 目录已更改,并添加了一个名为 /root/.ash_history (当我们在容器内执行第一个命令时自动创建),并添加了 /hello_world 我们创建的文件。

💡 值得注意的是,Alpine 的默认 shell 是 ash,因此历史文件被命名为 .ash_history 而不是 .sh_history,即使我们指定了 sh 在我们的 docker run 命令。

检查容器元数据和配置

检查容器配置涉及检查容器的配置详细信息,例如其网络设置、环境变量和存储配置。

检查容器配置的一种方法是使用 docker inspect 命令,它提供容器配置和元数据的详细视图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ docker inspect 87908a159c5b
[
{
"Id": "87908a159c5b20ef2d251f023fc666d03309e2739743f4cedf77f049cf634169",
"Created": "2023-04-12T00:47:25.919621316Z",
"Path": "sh",
"Args": [],
"State": {
"Status": "exited",
"Running": false,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 0,
"ExitCode": 0,
"Error": "",
"StartedAt": "2023-04-12T00:47:26.302842966Z",
"FinishedAt": "2023-04-12T00:47:50.743670453Z"
},
...
...
...

我们可以利用 –format 用于提取特定信息(例如实例的 IP 地址)的选项:

1
2
$ docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' 87908a159c5b
172.17.0.2

或者实例的 MAC 地址:

1
2
$ docker inspect --format='{{range .NetworkSettings.Networks}}{{.MacAddress}}{{end}}' 87908a159c5b
02:42:ac:11:00:02

或者主机上容器的进程 ID:

1
2
$ docker inspect --format='{{.State.Pid}}' 87908a159c5b
27666

我们也可以通过运行 ps 命令:

1
2
3
$ ps -fp 27666
UID PID PPID C STIME TTY TIME CMD
root 27666 27646 0 07:54 pts/0 00:00:00 sh

查看容器日志

要查找可能在容器内执行的任何命令,或可能在容器内运行的任何服务的日志,我们可以利用 docker logs 命令:

1
2
3
4
5
$ docker logs 87908a159c5b
/ # ls
bin dev etc home lib media mnt opt proc root run sbin srv sys tmp usr var
/ # echo "Hello, World!" > hello_world
/ # exit

检查图像历史记录

检查图像的历史记录有助于识别对其所做的任何更改,包括潜在的恶意修改或添加恶意文件或图层。这在检查图像在原始生成后是否被篡改时特别有用。

这可以使用 docker history 命令,它按时间倒序显示图像的历史记录,显示添加到图像的每个图层:

1
2
3
4
$ docker history alpine --no-trunc
IMAGE CREATED CREATED BY SIZE COMMENT
sha256:9ed4aefc74f6792b5a804d1d146fe4b4a2299147b0f50eaf2b08435d7b38c27e 13 days ago /bin/sh -c #(nop) CMD ["/bin/sh"] 0B
<missing> 13 days ago /bin/sh -c #(nop) ADD file:9a4f77dfaba7fd2aa78186e4ef0e7486ad55101cefc1fabbc1b385601bb38920 in / 7.04MB

获取容器的内存转储

获取容器进程的内存转储可用于调查其运行时状态、检测恶意活动或提取机密。

我们可以使用这样的工具 dd, gdb或 gcore 获取容器进程的内存转储。在这些选项中,我们将使用 gcore,这需要进程 ID(请查看有关检查容器元数据和配置的部分)作为输入:

1
2
3
4
5
$ sudo gcore 27666
warning: Target and debugger are in different PID namespaces; thread lists and other data are likely unreliable. Connect to gdbserver inside the container.
0x00007f48fa0993c1 in ?? () from target:/lib/ld-musl-x86_64.so.1
Saved corefile core.27666
[Inferior 1 (process 27666) detached]

一旦我们有了内存转储,我们就可以搜索任何有趣的信息,例如机密或恶意字符串。例如,由于我们在本实验开始时将“Hello, World”写入容器内的文件中,因此让我们尝试使用 strings 命令:

1
2
3
$ strings core.27666 | grep "Hello, World"
echo "Hello, World!" > hello_world
echo "Hello, World!" > hello_world

结论

总而言之,我们了解了容器的基础知识,并探索了在容器化环境中进行取证的几种技术,但重要的是要注意,还有其他技术和工具可以自动执行这些过程。其中一些工具包括 docker 取证工具包 、 docker 浏览器 、 容器浏览器 和 docker 层提取 。

话虽如此,如果您有兴趣进一步了解容器和安全,我强烈建议您查看 Liz Rice 的《容器安全 》一书。它提供了对容器安全世界的宝贵见解,对于初学者和有经验的专业人士来说都是一个很好的资源。