# 用 Trivy 扫描新操作系统的漏洞

[Trivy](https://github.com/aquasecurity/trivy) 是个来自 [Aqua Security](https://www.aquasec.com/)的漏洞扫描系统，现已经被 Github Action、Harbor 等主流工具集成，能够非常方便的对镜像进行漏洞扫描，其扫描范围除了操作系统及其包管理系统安装的软件包之外，最近还加入了对 Ruby、PHP 等的漏洞检测，应该是该领域目前目前采用最广的开源工具之一了。

在我们使用 Trivy 对系统进行扫描加固的时候，遇到了一个问题，openEuler 这样的年轻操作系统，还没被 Trivy 接入其数据库之中，如何能借助这样的主流工具进行漏洞扫描呢？

漏洞的发现、处理、披露是个复杂的流程，下面引文来自我的译作《容器安全》（《Container Security: Fundamental Technology Concepts that Protect Containerized Applications 1st Edition》(Liz Rice)）：

> 一旦发现了新的漏洞，赛跑就开始了，系统管理员必须抢先修复漏洞，否则就可能遭受针对性的攻击。如果直接公开发布新问题，就相当于为攻击者开启了利用漏洞的自由竞赛。为了避免这种情况发明了一个概念，叫做负责任的安全披露。发现漏洞的安全研究人员会联系相关软件的开发者或供应商。双方商定一个时限，在这个时限之后，研究人员可以公布他们的发现。在这里，对供应商来说，有一些积极的压力，要求他们努力及时提供修复，因为在公布之前提供修复，对供应商和用户都有好处。 新发现的问题会有一个唯一标示符，前缀为 CVE，是 Common Vulnerabilities and Exposures 的缩写，CVE 后面是年份代码。例如 ShellShock 漏洞是 2014 年被发现的，其标示符为 CVE-2014-6271。管理这些编码的机构是 [MITRE](https://mitre.org/)，MITRE 监管着一些 CVE 编码授权机构（简称 CNA），CNA 能够在特定领域中签发 CVE ID。有些大型软件商（例如微软、红帽以及 Oracle）就是 CNA， 能在各自产品范围内授予 CVE 编码。Github 在 2019 年末也获得了 CNA 资格。 在 [NVD（National Vulnerability Database（国家漏洞数据库））](https://nvd.nist.gov/) 中使用 CVE 编码跟踪受到该漏洞影响的软件包和版本。有了受影响的软件包版本的列表，所以如果当前使用的软件版本出现在这个列表里，是不是代表当前环境已经受到影响了？并不一定，这和当前使用的 Linux 发行版是相关的，可能发行版自身已经针对该软件包发布了一个补丁版本。 以 ShellShock 为例。这是一个存在于 GNU Bash 中的严重漏洞。[NVD 上的 CVE-2014-6271 页面](https://nvd.nist.gov/vuln/detail/CVE-2014-6271)里列出了一个很长的列表，覆盖了从 1.14.0 到 4.3 的版本范围。如果运行一个旧版本的 Ubuntu 12.04 并且发现其中的 Bash 版本是 4.2-2ubuntu2.2，你可能会认为这是一个基于 bash 4.2 的版本，属于受影响之列。 实际上根据 [Ubuntu 的官方说法](https://ubuntu.com/security/notices/USN-2362-1)，这个版本已经修复了该漏洞，是安全的。Ubuntu 维护者认为，与其让所有 12.04 用户升级到全新的 bash 版本，不如单独进行补丁并发布一个小的补丁版本。

因此扫描工具除了需要关注 CVE 的通用信息之外，更重要的是引入发行版开发商的安全数据库，才能准确地完成扫描任务。

## Trivy 的基本功能

### Trivy 工具链

Trivy 自身只是一个扫描工具，实际上支撑这个工具的还有一个工具链，多种工具/库的协同，完成了从 CVE 到扫描识别的各个环节，其中包括：

* `vuln-list-update`：负责更新各个来源的威胁数据，转换成 JSON 数据，保存在 `vuln-list` 项目之中。
    
* `trivy-db`：既是工具，也是库，用于操作 Trivy 的数据库。
    
* `fanal`：从 `vuln-list` 获取数据，并构建成 bbolt 格式的数据库文件，可以用 upload 命令上传到 Github Release。
    
* `Trivy`：获取 trivy-db 的 Release 数据，进行漏洞扫描工作。
    

综上所述，Trivy 的总体工作流程：

1. 从操作系统厂商等 CVE 源获取数据，使用 `vuln-list-update` 脚本进行汇总，转换为一致的 JSON 数据，保存到 `vuln-list` 项目。
    
2. `trivy-db` 从 `vuln-list` 下载数据，转换为 bbolt 格式，发布到 `trivy-db` 的 Release。
    
3. Trivy 下载 `trivy-db` 数据，作为本地检测的数据源。
    

### Trivy 的扫描流程

Trivy 首先会使用 Fanal 对待扫描镜像进行检测，Fanal 会根据基础镜像哈希码查询缓存（MACOS 中是 `~/Library/Caches/trivy/fanal/fanal.db`）中是否保存了对应的“哈希-操作系统”记录，如果没有，则会解压基础镜像层，遍历其中文件，并根据其 `analyzer` 中包含的各个操作系统的文件特征来判断基础镜像的操作系统，例如 Alpine 的特征文件是 `etc/alpine-release`，而 Photon 的特征文件是 `usr/lib/os-release` 和 `etc/os-release`；如果有，则直接从缓存数据中取出对应的操作系统名称和版本。

`ospkg` 中的 `detect.go` 中硬编码了操作系统和扫描器的对应关系，例如：

```golang
case fos.RedHat, fos.CentOS:
  return redhat.NewScanner()
```

确定了具体的扫描器之后，就会调用具体的扫描器，根据漏洞库 `~/Library/Caches/trivy/db/trivy.db` 作为数据源进行扫描。

## 扫描新操作的方法

## 篡改 Fanal 缓存

如果我们假设新系统可以和 Redhat 8 共享同样的漏洞库，就可以直接在 Fanal 缓存中加入 新的的镜像关系，对应到 Redhat 8 上，就能够进行扫描了，例如：

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1745947267135/dee49220-405f-4cde-b1bc-4361a775f961.jpeg align="center")

## 正式一点的方式

篡改缓存的扫描方法是非常不负责的，这大概不会代表真实的操作系统安全现状。根据上文工具链的介绍，应该能清楚地了解到这个过程：

首先要 Fork `vuln-list-update`，其中加入新操作系统的数据源，并转换为通用格式，例如：

```JSON
{
    ...
 "cwe": "CWE-120",
  "statement": "",
  "acknowledgement": "",
  "name": "CVE-2021-0326",
  "document_distribution": "",
  "details": [
    "In p2p_copy_client_info of p2p.c, there is a possible out of bounds write due to a missing bounds check. This could lead to remote code execution if the target device is performing a Wi-Fi Direct search, with no additional execution privileges needed. User interaction is not needed for exploitation.Product: AndroidVersions: Android-10 Android-11 Android-8.1 Android-9Android ID: A-172937525"
  ],
  "references": [
    "https://w1.fi/security/2020-2/wpa_supplicant-p2p-group-info-processing-vulnerability.txt"
  ]
  ...
}
```

接下来要修改 Fanal，除了在其中加入新系统的甄别方法，例如 `/etc/openEuler-release` 的解析过程。

然后是创建新操作系统的扫描器，基本上可以参照 Debian/Redhat/Alpine 几个大体系的扫描器完成工作。

上述工作可以通过分叉的方式自己独立运行，也可以通过 PR 的方式回馈给 Trivy 项目组，公开成为通用的扫描方案。
