容器镜像静态扫描原理
前言
有这样一个工具, 可以对镜像进行扫描, 拿出其中包含的软件包、lib库(pip包等)、危险文件等, 并能够识别出哪些包是存在漏洞的. 比如trivy
工具. 那么他们是如何做到这件事情的呢?
正好最近在研究这块, 索性就简单谈一谈(其实原理很简单).
获取包含内容
对于镜像的存储, 层之间的关系, 以及如何将多层数据进行聚合, 可以看之前的这篇文章.
扫描单层软件包
扫描单层的数据, 说白了就是文件扫描. 比如:
- 获取系统版本
- 从
etc/os-release
文件获取系统版本 - 从
etc/debian_version
文件获取debian
的版本 - 从
etc/lsb-release
文件获取ubuntu
版本 - 从
etc/alpine-release
文件获取alpine
的版本
- 从
- 获取系统包
- 从
lib/apk/db/installed
文件获取安装的所有apk
包 - 从
var/lib/dpkg/status
文件获取安装的所有dpkg
包
- 从
- 获取依赖库
- 依据
.jar
后缀拿到所有jar
包 - 依据
go.mod
文件名获取依赖的库 - 从
requirements.txt
文件拿到依赖的pip
库
- 依据
- 获取风险文件
- 通过一些文件识别的扫描库, 可实现对恶意文件的识别. 比如yara
通过对本层所有文件的扫描, 就能够将当前层包含的信息全部拿到了.
多层合并
在拿到每一层的数据之后, 接下来就是将多层的数据进行聚合了. 软件包的聚合规则与文件的聚合规则相同. 可以回这篇文章复习一下文件是如何聚合的.
- 覆盖: 对于单文件内容, 上层覆盖下层, 比如:
- 系统包.
apk/dpkg
等, 在信息修改后, 其对应的单位件会覆盖
- 系统包.
- 合并: 将多层内容进行合并. 比如:
jar
文件, 每层增加一个, 在最后将所有jar
包合并到一起
- 移除: 之前说过, 当文件删除的时候, 会保留一个
without
文件. 因此, 当上层将下层内容删除时, 需要在最终聚合的时候去掉.
经过以上步骤, 整个镜像的完整信息就已经拿到了.
识别漏洞
如何识别漏洞呢? 就更简单了, 在本地数据库中维护一份漏洞库, 标识了某个系统的某个软件的某个版本, 存在某个漏洞. 然后拿着匹配到的结果一个一个找就行了.
总结
以上, 就是镜像的静态扫描原理了. 看完之后是不是发现特别的简单?
但其实写起来并没有想象中那么简单, 比如合并的操作就比较繁琐. 不过这里只介绍原理, 知道是怎么回事就行了.
还有一些扫描是动态扫描, 会将镜像启动, 然后通过攻击行为来判断是否存在某种漏洞. 不在本文的说明范围内.