SSD Nodes Learn Hosting plans →
指南 Matt Connor作者: Matt Connor

Debian和Ubuntu服务器如何检查已知CVE

在Debian上使用debsecan或debvulns,在Ubuntu上使用pro cves列出已知CVE,并仅针对值得重启的漏洞发送告警。

当前有哪些已知 CVE 会影响此服务器

要了解当前有哪些已知 CVE 会影响您的服务器,请将已安装软件包的版本与已发布的漏洞源进行比较。CVE(通用漏洞和暴露)是用于标识已知安全漏洞的公开编号体系。在 Debian 上,应使用 debsecan 或 debvulns 进行比较。在 Ubuntu 上,应使用 pro cves,因为 Ubuntu 维护自己的跟踪器,而 Debian 工具读取的是错误的跟踪器。

应用更新和了解服务器面临哪些风险是两项不同的工作。apt upgrade 只能回答一个问题:是否有更新版本的软件包可用。它不会告诉您此计算机上有哪些 CVE 尚未修复,也不会告诉您其中哪些漏洞在当前发行版中还没有修复程序。第二项工作需要使用扫描器,而使用扫描器前,您需要准确了解它比较的内容。

实际比较的内容

其机制足够简单,便于理解。扫描器从 dpkg 数据库读取已安装软件包列表。然后,它将每个二进制软件包映射回构建它的源软件包,因为漏洞数据按源软件包记录。扫描器下载一个数据源,其中针对每个 CVE 和每个源软件包,列出该问题在各个套件中得到修复的版本。随后,它使用与 dpkg --compare-versions 相同的版本排序规则,将已安装版本与修复版本进行比较。

Debian 或 Ubuntu 上的 CVE 扫描器,本质上是将版本与缓存的数据源进行比较。仅此而已。

二进制软件包到源软件包的映射是人们经常跳过的部分,而跳过它会导致漏报。openssl 源软件包在 Debian 13 和 Ubuntu 24.04 上构建名为 libssl3t64 的二进制软件包。针对 openssl 提交的 CVE,无法通过简单搜索二进制软件包名称匹配到。这就是 debvulns 要求 python3-apt 的原因:APT 缓存包含该映射;没有它,工具会回退到 dpkg-query,可能漏掉源软件包名称与二进制软件包名称不同的问题。

数据源还包含按发行版版本划分的状态,并非简单的是或否。打开某个源软件包的跟踪器页面,例如Debian 安全跟踪器中的 openssl 条目,每个发行版版本列都会显示 vulnerable 或 fixed;处于开放状态的问题有时还会标注 no DSA 或 postponed。no DSA 表示安全团队认为该问题不值得立即发布安全公告(DSA),通常是因为严重性较低。这不表示永远不会提供修复。Debian 的跟踪器文档说明,这些问题通常不会一直处于未修复状态:它们仍可能在稳定版点版本中修复,或与之后针对更严重问题的更新一并发布。无论哪种情况,修复后的软件包都会通过常规软件仓库提供,apt upgrade 会安装它,扫描器中的对应条目也会消失。在此之前,扫描器每次运行都会报告该 CVE,这种情况可能持续数月。确实有少数标记表示该发行版版本不会再提供更新。Debian 文档将 ignored 列为 no DSA 的子状态之一,并与 postponed 并列;它表示安全团队完全不会修复的问题。end-of-life 表示在 stable 或 oldstable 发行版中提前停止安全支持的软件包,这种情况很少见,主要适用于 Web 浏览器。unimportant 也用于表示不在安全支持范围内的软件包中的问题。某条报告始终无法消失,是人们放弃使用这类工具的最常见原因;这并不是工具缺陷。

使用 debsecan 扫描 Debian 服务器

debsecan 位于 Debian 软件包归档中,无需配置外部软件源。

sudo apt update && sudo apt install -y debsecan
debsecan --suite trixie

每一行输出表示一个已安装二进制软件包对应的一个 CVE:

CVE-2016-2776 bind9-host (remotely exploitable, high urgency)
CVE-2016-8864 libdns-export100 (fixed, remotely exploitable, medium urgency)

紧急级别和 remotely exploitable 标记直接来自 Debian 跟踪器。fixed 表示跟踪器已为你的软件源套件列出修复版本,通常升级后该行就会消失。但并非总是如此:debsecan 的手册页警告,修复版本可能在软件包为你的系统架构完成构建之前就已列出,因为构建仍在进行,或者该软件包因其他原因无法安装。因此,apt upgrade 可能暂时没有可安装的软件包。没有 fixed 的行表示你今天无法关闭的问题。

传入 --suite,否则一半输出都会不准确。debsecan 只使用软件源套件来判断是否存在已修复的软件包,以及某个软件包是否已过时。如果软件源套件填写错误,CVE 列表仍然正确,但 fixed 标记和过时软件包警告会不准确。使用你实际运行的发行版代号:Debian 13 使用 trixie,Debian 12 使用 bookworm。

以下两种视图适合放入脚本:

debsecan --suite trixie --only-fixed --format packages
debsecan --suite trixie --only-fixed --format detail

--format packages 只输出二进制软件包名称,每行一个。这就是可以直接处理的软件包列表。--format detail 输出源中针对每个问题保存的全部信息。--only-fixed 会排除归档中尚未列出修复软件包的所有问题,包括上文所述、仍在等待小版本发布的 no DSA 条目,因此输出会缩减为通常可以完成的工作。上文的架构限制同样适用:已列出的修复版本不一定对应今天就能下载的软件包。

要执行计划任务,请编辑 /etc/default/debsecan,并设置 SUITE、MAILTO 和 REPORT。该软件包提供 debsecan-create-cron,它会写入一条 cron 条目,每小时在随机选择的分钟执行。每小时执行并不表示每小时扫描:debsecan --cron 会检查当天是否已下载数据;如果已下载,就直接退出。使用随机分钟是为了避免所有 Debian 机器在同一秒访问跟踪器。

在 Ubuntu 上,debsecan 为什么会报告错误信息

debsecan 位于 Ubuntu 的 universe 仓库中(24.04 中的版本为 0.4.20.1)。它可以正常安装,但会输出看似可靠、实际上不应信任的结果。它使用 Debian 安全跟踪器提供的数据,而该跟踪器不跟踪 Ubuntu。Ubuntu 独有的问题不会出现在其中。类似 3.0.13-0ubuntu3.4 的 Ubuntu 版本字符串永远不会匹配 Debian 的修复版本,debsecan 会将它无法识别的版本与 Debian unstable 进行比较。结果是:Canonical 数月前已经修复的问题仍会触发警报,而实际存在的问题却可能不会被报告。Launchpad 错误 #95925 自 2007 年起就记录了这一问题。

过去曾有一个桥接项目。ust2dsa 项目会将 Ubuntu CVE 跟踪器的数据转换为 debsecan 使用的格式,并每六小时重新发布一次。该项目已于 2021 年 10 月归档。现在让 --source 使用它,只会得到一个五年前就不再更新的数据源。这比没有扫描器更糟,因为过期的数据源会报告系统正常。

使用 pro cves 列出 Ubuntu 服务器上的 CVE

Ubuntu 在 ubuntu-pro-client 中提供了相应功能。Version 35 添加了 pro cves,用于列出受已知 CVE 影响的已安装软件包。

pro cves
pro cves --fixable
pro cves --unfixable
Package         Priority     Origin        Vulnerability
firefox         medium       esm-infra     CVE-2020-6852
openssh         low          standard      CVE-2021-3188
vim             critical     esm-infra     CVE-2011-3374
vim-tiny        high         -             CVE-2011-3380

先查看 Origin 列,因为它决定了可执行的操作。standard 表示修复位于普通的 Ubuntu security 或 updates pocket 中,执行常规升级即可安装。esm-infra 或 esm-apps 表示修复仅位于 Expanded Security Maintenance 中,需要为计算机绑定 Ubuntu Pro 订阅。- 表示目前尚无修复可用;该行相当于 debsecan 输出中不含 fixed 的行。

对于单个 CVE,使用以下两个命令即可完整了解情况:

pro cve CVE-2024-5480
pro fix --dry-run CVE-2020-25686

pro fix --dry-run 不会执行任何更改,只会准确显示当前状态:

CVE-2020-25686: Dnsmasq vulnerabilities
 - https://ubuntu.com/security/CVE-2020-25686

1 affected package is installed: dnsmasq
(1/1) dnsmasq:
A fix is available in Ubuntu standard updates.
{ apt update && apt install --only-upgrade -y dnsmasq }

✔ CVE-2020-25686 is resolved.

不受影响的计算机会输出 No affected source packages are installed.。尚无可用修复的计算机会输出类似 ✘ CVE-2017-9233 is not resolved. 的行。删除 --dry-run 后,同一命令即可执行升级。pro security-status 提供配套视图:它按软件仓库统计已安装的软件包,并报告待安装的安全更新数量。这一点很重要,因为 Main 和 Universe 提供的支持承诺不同。

安装 debvulns 并固定版本

debvulns是基于 Debian 安全跟踪器构建的 Python 工具包。它基于同一份数据提供命令行扫描器和 Prometheus exporter。截至 2026 年 10 月,当前版本为 0.2.2。

在 Debian 12 或更高版本上运行 pip install debvulns 会失败并显示 error: externally-managed-environment,因为系统 Python 由 dpkg 管理。请安装到一个仍可访问系统 python3-apt 的虚拟环境中:

sudo apt update && sudo apt install -y python3-venv python3-apt
sudo python3 -m venv --system-site-packages /opt/debvulns
sudo /opt/debvulns/bin/pip install debvulns==0.2.2
/opt/debvulns/bin/debvulns --severity high --format json | head -40

--system-site-packages 是关键标志。没有它,虚拟环境无法导入 python3-apt,debvulns 会退回使用 dpkg-query,并且不会显示错误消息,但你会失去二进制包到源代码包的映射。还要固定版本。如果扫描器在两个星期二之间自行改变行为,你就无法将本次结果与上周的扫描进行比较。

你将使用以下标志:

  • --severity 接受 critical、high、medium、low 或 negligible,并筛选输出。
  • --format 接受 json 或 csv。默认值为 JSON。
  • --sort-by 接受 package 或 cve。
  • --suite 覆盖自动检测到的 Debian 代号。
  • --no-cache 跳过缓存的 feed,下载新的 feed。
  • -v 将工具的操作记录到 stderr,包括它获取的 URL。

debvulns 还会下载 EPSS(exploit prediction scoring system)分数,并为每个发现项附加一个分数。EPSS 是每天发布的估计值,用于表示某个 CVE 在未来三十天内被利用的概率。与 CVSS(common vulnerability scoring system)基础分数结合使用时,EPSS 是一种有用的补充判断依据:CVSS 描述漏洞被利用后可能造成的严重程度,而 EPSS 描述有人实际尝试利用该漏洞的可能性。

与 debsecan 一样,这里也有相同的警告。该 feed 来自 Debian,因此应在 Debian 上运行 debvulns。在 Ubuntu 上,pro cves 是与其 archive 匹配的工具。

将 CVE 数量导出到 Prometheus

如果您已经在运行监控,exporter 会将其转换为可触发告警的数值,而不是一份无人打开的报告。

sudo useradd --system --no-create-home --shell /usr/sbin/nologin debvulns

写入 /etc/systemd/system/debvulns-exporter.service:

[Unit]
Description=debvulns Prometheus exporter
Wants=network-online.target
After=network-online.target

[Service]
Type=simple
User=debvulns
ExecStart=/opt/debvulns/bin/debvulns-exporter --port 9222 --refresh-interval 21600 --cache-dir /var/cache/debvulns-exporter
CacheDirectory=debvulns-exporter
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
Restart=on-failure
RestartSec=30

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now debvulns-exporter
curl -s localhost:9222/-/ready
curl -s localhost:9222/metrics | grep -c debvulns_

CacheDirectory=debvulns-exporter 是 ProtectSystem=strict 能够正常工作的关键:systemd 会创建由服务用户拥有的 /var/cache/debvulns-exporter,进程只能写入此路径。首次扫描完成前,/metrics 返回 HTTP 503;完成后,/-/ready 才返回 200。这样可以避免 exporter 发布尚未测量的 0。进程存活后,/-/healthy 立即返回 200,因此请使用它检查存活状态,使用 /-/ready 检查抓取就绪状态。

建议用于编写规则的指标:

  • debvulns_vulnerabilities_total 是按严重性、是否有可用修复方案以及问题是否可从远程访问标记的汇总数量。
  • debvulns_vulnerability_info 为每个 CVE 和软件包提供一个时间序列,并将 CVE ID 作为标签。
  • debvulns_vulnerability_epss_score 是每个 CVE 的 EPSS 概率。
  • debvulns_scan_status 在上次扫描成功时为 1,失败时为 0。
  • debvulns_last_scan_timestamp_seconds 记录上次扫描的执行时间。

编写规则前,请先从您自己的主机获取准确的标签值。因为不匹配任何内容的选择器不会触发告警,看起来与运行正常的服务器完全相同:

curl -s localhost:9222/metrics | grep '^debvulns_vulnerabilities_total'

然后编写两条规则:

groups:
  - name: debvulns
    rules:
      - alert: FixableCriticalCVE
        expr: sum by (instance) (debvulns_vulnerabilities_total{severity="critical", fix_available="true"}) > 0
        for: 6h
        annotations:
          summary: "A critical CVE with an available fix has stayed open for six hours"
      - alert: CVEScanStale
        expr: time() - debvulns_last_scan_timestamp_seconds > 172800
        for: 30m
        annotations:
          summary: "CVE data on this host is more than two days old"

for: 6h 子句是确保第一条告警可信的关键。没有它,自动升级任务可能在 20 分钟后修复某个 CVE,但您现在就会收到告警。如果告警在您读完之前就自行恢复,您很快就会忽略下一条告警。使用它后,您只会收到仍未被自动修补的问题。

第二条规则经常被忽略。停止扫描的扫描器会报告 0 个漏洞,而在监控面板上,0 与运行正常完全相同。除了检查数据内容,还应根据数据的更新时间触发告警。

软件包扫描器无法看到的内容

这类工具的边界比大多数人预期的更明确。它们会将已安装的软件包版本与漏洞源进行比较,而它们掌握的所有信息都来自 dpkg 数据库。任何未通过软件包管理器安装的内容都不可见。

这包括:

  • Docker 镜像中的所有内容。扫描器可以看到主机上的 docker.io 软件包,但不了解容器中的用户态环境、解释器或库。
  • 每个 pip、npm、cargo 和 go 依赖项,包括镜像在构建时拉取的依赖项。
  • 静态二进制文件,以及通过 tarball 或 curl ... | sh 行安装的任何内容,因为它们完全没有软件包元数据。
  • 您自行编译的软件,即使其版本与发行版软件包相同。

对于镜像,应在构建镜像的位置使用镜像扫描器扫描,而不是在主机上扫描。语言依赖项的风险模型本身也不同:常见故障是软件包被劫持或包含恶意代码,而不是使用带有已知 CVE 的旧版本。服务器上的 npm 供应链攻击介绍了问题的这一部分,而这并不是 debsecan 原本能够发现的部分。

您的结果只和缓存源一样新

这里的每个工具都会使用缓存。debvulns 能写入 /var/cache/debvulns 时就使用它,否则回退到 ~/.cache/debvulns;只有缓存超过 24 小时后,它才会重新下载。debsecan --cron 每个日历日只下载一次。导出器默认每 24 小时刷新一次,并拒绝小于 3600 秒的值。

因此,结果可能已经过时一天;网络故障时,您得到的不是错误,而是旧结果。调查特定问题时,请强制读取最新数据:

/opt/debvulns/bin/debvulns --no-cache --severity critical
ls -l --time-style=long-iso /var/cache/debvulns

上面的 CVEScanStale 规则会在所有主机上为您执行此操作。“没有高危 CVE”和“自星期四以来没有数据”在图表上看起来相同,但实际情况完全不同。

CVE 何时需要重启

磁盘上的修复包不等于内存中的进程已修复。apt upgrade 会替换文件,但长期运行的进程仍会使用启动时映射的副本。needrestart 可找出这些进程。

sudo apt install -y needrestart
sudo needrestart -r l
sudo needrestart -b -k

-r l 会列出需要重启的内容,但不会执行任何操作。-b 是批处理模式,会输出机器可读的行,而不是对话框:

NEEDRESTART-KCUR: 6.12.48-amd64
NEEDRESTART-KEXP: 6.12.57-amd64
NEEDRESTART-KSTA: 3

KCUR 是当前正在运行的内核。KEXP 是磁盘上安装的最新内核。两者不同时,KSTA 会报告 3,表示需要重启。若您希望抓取指标而不是解析输出,needrestart -o 会以 OpenMetrics 格式输出相同信息。

Ubuntu 还会在软件包的安装后脚本表示需要重启时创建 /var/run/reboot-required,并在 /var/run/reboot-required.pkgs 中列出相关软件包。该路径位于 tmpfs 中,因此按设计会在每次启动时消失。pro system reboot-required 用一个单词回答相同问题,并会单独报告 livepatch 是否已应用到正在运行的内核。Debian 默认不会创建此类文件。安装 reboot-notifier(Debian 13 中的版本为 0.12)即可获得兼容的等效功能;也可以使用 needrestart,它在两个发行版上的行为相同。

按以下顺序应用决策规则:

  1. CVE 位于共享库中,修复包已安装,并且 needrestart -r l 列出了仍在使用旧副本的服务。重启这些服务即可,无需重启系统。
  2. CVE 位于内核中,且 KCUR 与 KEXP 不同。只有重启才能替换正在运行的内核;除非使用 VPS 上的 live kernel patching,该功能可覆盖能够应用到正在运行系统的部分内核修复。
  3. 您的发行版尚无针对该 CVE 的修复包。其表现为 debsecan 输出中没有 fixed 的行(通常表示正在等待点版本发布的 no DSA 问题),或者 Ubuntu 中出现 Origin 的 -。重启不会改变任何内容。记录当前暴露情况,并在下一次更新后重新检查;也可以关闭受影响的功能进行缓解。
  4. 扫描器列出了该 CVE,但没有任何可访问的服务使用该软件包。按常规补丁周期修复即可。仅供本地 man 命令链接使用的库存在漏洞,不属于安全事件。

能在安静月份持续执行的节奏

选择一个 6 个月后仍能执行的节奏,因为这项工作的价值完全取决于持续重复。

每天无人值守地运行扫描。在 Debian 上,这可以通过设置了 REPORT 的 debsecan cron 条目实现,也可以使用导出器自身的刷新循环。让 无人值守升级 按自身计划安装安全修复,这样扫描器的任务就是报告自动化机制未能解决的问题,而不是成为唯一的防御措施。

针对一个数值发出告警,而不是针对整份报告。可修复的 critical 严重性问题持续 6 小时,是大多数单台服务器每年会达到几次的阈值。这个频率足够低,因此告警到达时您确实会查看。其他问题都应放在仪表板上,在 每月服务器维护 期间检查。

在需要重启之前先写下重启规则,因为您可能会在不方便的时间执行它。具有不同 KEXP 的内核 CVE 表示需要安排重启。库 CVE 表示重启 needrestart 指定的服务。没有可用修复时,记录该问题,并在下个月再次检查。

每季度重新检查一次排除项。dpkg 之外运行的内容只会增加:有人添加的容器、放入 /usr/local/bin 的二进制文件。每一项都可能导致扫描器因无法查看而报告干净状态。在服务器集群中,将此清单与用于管理多个 Linux 服务器的工具放在一起,以便不止一个人能够看到这些例外。

FAQ

如何列出影响 Debian 服务器的 CVE?

安装 debsecan,然后运行 debsecan --suite trixie,其中应使用实际运行版本的代号。每一行输出都包含一个 CVE 和一个已安装的二进制软件包;括号中的 fixed 表示跟踪器已为你的套件列出修复版本。添加 --only-fixed --format packages 可将输出限制为已列出修复版本的软件包名称。debsecan 的手册页警告称,修复版本可能在软件包为你的架构构建之前就已列出,因此 apt upgrade 今天可能无法清除其中所有项目。如需 JSON 输出和 EPSS 分数,请在通过 --system-site-packages 创建的虚拟环境中安装 debvulns,以便它能够导入 python3-apt。

为什么扫描器持续报告 apt 无法修复的 CVE?

因为你的发行版软件仓库中尚未提供该问题的修复软件包。在 Debian 中,通常原因是存在 no DSA 标记:安全团队认为该问题影响较小,无需立即发布安全公告。这并不表示问题会一直未修复。Debian 跟踪器文档说明,这类问题仍可能在点版本中修复,或合并到针对更严重问题的后续更新中;修复发布后,apt upgrade 会安装该修复并清除这一行。标记为 ignored 的问题、在你的发行版中标记为 end-of-life 的软件包,以及不在安全支持范围内的软件包,在该发行版中都不会获得任何更新。Ubuntu 中对应的情况是 pro cves 输出中的 Origin -。在修复发布前,该 CVE 在你的计算机上确实仍处于未修复状态,因此扫描器继续列出它是正确的。使用 debsecan --only-fixed 或 pro cves --fixable 仅查看你能够处理的项目,并在常规维护时检查其余项目。

CVE 扫描器能发现 Docker 容器内部的漏洞吗?

不能。这些工具只读取主机的 dpkg 数据库,因此只能看到 docker.io 软件包,不了解镜像内部的操作系统或库。同样的盲区也涵盖所有 pip 或 npm 依赖项,以及从 tarball 安装的任何内容。应在构建容器镜像的地方使用镜像扫描器进行扫描,并将语言依赖项作为具有不同故障模式的独立问题处理。

CVE 何时确实需要重启?

只有修复位于内核中时才需要重启。运行 sudo needrestart -b -k,将正在运行的内核 NEEDRESTART-KCUR 与已安装的最新内核 NEEDRESTART-KEXP 进行比较。如果两者不同,NEEDRESTART-KSTA 会报告 3,表示需要重启。对于共享库中的 CVE,修复后的文件已经位于磁盘上;你只需重启仍在映射旧副本的进程,sudo needrestart -r l 会列出这些进程。

我可以在 Ubuntu 服务器上运行 debsecan 吗?

它可以从 universe 安装,但其结果不正确。debsecan 读取 Debian 安全跟踪器;该跟踪器不跟踪 Ubuntu 问题,也不识别 3.0.13-0ubuntu3.4 等 Ubuntu 版本字符串。因此,它会针对 Canonical 很久以前就已修复的问题产生误报,却不会报告仅存在于 Ubuntu 中的问题。曾经将 Ubuntu 数据转换为 debsecan 格式的 ust2dsa 桥接工具已于 October 2021 归档,其数据源也不再更新。在 Ubuntu 上使用 pro cves。

#cve#debian#ubuntu#vulnerability-scanning#监控#安全