如何检查 Debian 或 Ubuntu 服务器受哪些 CVE 影响
使用 debsecan 和 debvulns 比较已安装软件包与漏洞源,列出服务器受影响的全部 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 表示安全团队已评估该问题,并认为没有必要为稳定版发布更新,因此该修复不会进入你的发行版。扫描器会在该发行版的整个生命周期内每次运行都报告这个 CVE,使用多少 apt upgrade 都无法清除它。这是人们放弃使用这些工具的最常见原因,而不是工具本身存在错误。
使用 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 表示仓库中已有修复版本,因此今天升级后即可消除该行。没有 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 中提供了该功能。版本 35 新增了 pro cves,用于列出受已知 CVE 影响的已安装软件包。
pro cves
pro cves --fixable
pro cves --unfixablePackage 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 安全软件源,执行常规升级即可安装。esm-infra 或 esm-apps 表示修复仅存在于 Expanded Security Maintenance 中,需要为该计算机关联 Ubuntu Pro 订阅。- 表示目前任何位置都没有可用修复,这一行相当于 Ubuntu 中的 Debian no DSA。
对于单个 CVE,使用以下两个命令即可完整了解情况:
pro cve CVE-2024-5480
pro fix --dry-run CVE-2020-25686pro 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 年 8 月,当前版本为 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,通用漏洞评分系统)基础分数的补充参考:CVSS 描述漏洞被利用后可能造成的严重程度,而 EPSS 描述有人实际尝试利用该漏洞的可能性。
这里同样适用 debsecan 的警告。该 feed 来自 Debian,因此应在 Debian 上运行 debvulns。在 Ubuntu 上,pro cves 是与其软件仓库匹配的工具。
将 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.targetsudo 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: 3KCUR 是当前正在运行的内核。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;它在两个发行版上的行为相同。
请按以下顺序应用决策规则:
- CVE 位于共享库中,已安装修复软件包,并且
needrestart -r l列出了仍在使用旧副本的服务。重启这些服务。无需重启系统。 - CVE 位于内核中,并且
KCUR与KEXP不一致。只有重启才能替换正在运行的内核,除非使用 VPS 上的内核实时修补;该方法只覆盖可应用于运行中系统的那部分内核修复。 - 当前发行版没有针对该 CVE 的修复。Debian 中显示为
no DSA,Ubuntu 中显示为-的Origin。重启不会改变任何结果。记录风险,或关闭受影响的功能以降低风险。 - 扫描器列出了该 CVE,但没有任何可访问的服务使用该软件包。按常规补丁周期修复即可。仅供本地
man命令链接的库存在缺陷,不属于安全事件。
能够持续一个安静月份的节奏
选择一种六个月后仍能坚持执行的节奏,因为这项工作的价值完全取决于重复执行。
每天自动运行扫描。在 Debian 上,这通常是配置了 REPORT 的 debsecan cron 条目,或导出器自身的刷新循环。让 无人值守升级自动安装安全修复,并按其自身计划运行。这样,扫描器的任务就是报告自动化流程未能解决的问题,而不是成为唯一的防线。
只针对一个数字发出告警,不要针对整份报告。可修复的 critical 严重性问题持续 600 小时,是大多数单台服务器每年会达到几次的阈值。这个频率足够低,因此告警到达时您确实会查看。其他内容应放在仪表板上,并在 每月服务器维护期间检查。
在需要之前先写下重启规则,因为您可能需要在不便的时间执行。内核 CVE 对应不同的 KEXP 时,安排重启。库 CVE 则重启 needrestart 所列出的服务。没有可用修复时,记录该问题,并在下个月再次检查。
每季度重新查看一次排除项。位于 dpkg 管理范围之外的对象只会增加:有人添加的容器、放入 /usr/local/bin 的二进制文件。每一项都可能导致扫描器因无法查看而错误报告系统正常。在服务器集群中,将该清单与用于管理多台 Linux 服务器的工具放在一起,确保不止一个人能看到这些例外。
FAQ
如何列出影响 Debian 服务器的 CVE?
安装 debsecan,然后运行 debsecan --suite trixie,并使用当前实际运行版本的代号。每一行输出包含一个 CVE 和一个已安装的二进制软件包;括号中的 fixed 表示归档中已经有修复版本。添加 --only-fixed --format packages,可将输出限制为今天可以升级的软件包名称。若要输出 JSON 和 EPSS 分数,请将 debvulns 安装到使用 --system-site-packages 创建的虚拟环境中,以便它能够导入 python3-apt。
为什么扫描器一直报告 apt 无法修复的 CVE?
因为 Debian 安全团队已将该问题标记为适用于你的版本的 no DSA,表示他们认为问题严重性不足以进行稳定版更新,因此不会提供修复软件包。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。