Your App Center List Is Not a Security Audit

Your App Center List Is Not a Security Audit

你的应用中心列表并非安全审计

I opened Ubuntu’s App Center, scrolled through ~90 installed packages, and asked the obvious question: is any of this malware? The list looked suspicious in all the wrong ways. A font package last updated nine years ago. A dozen unlabeled GStreamer plugins with generic gray icons. Something called BRLTTY I had no memory of installing. Plenty of surface area for paranoia.

我打开了 Ubuntu 的应用中心,浏览了大约 90 个已安装的软件包,并提出了一个显而易见的问题:这里面有恶意软件吗?这份列表看起来处处透着可疑:一个九年前更新的字体包;十几个带有通用灰色图标、未标注名称的 GStreamer 插件;还有一个我完全不记得安装过的名为 BRLTTY 的东西。这些都足以让人产生疑虑。

None of it turned out to matter, and the reason is worth writing down: App Center is a package browser, not an inventory. It shows you the two sources it manages — the Ubuntu archive and the Snap Store — and nothing else. Every path an attacker would actually use to get code onto a developer workstation is invisible to it. This is the audit I ran instead, and what each command is actually good for.

但事实证明这些都不重要,原因值得记录下来:应用中心是一个软件包浏览器,而不是一份完整的资产清单。它只显示它所管理的两个来源——Ubuntu 归档库和 Snap Store,除此之外一无所知。攻击者真正用来将代码植入开发者工作站的每一条路径,它都无法察觉。因此,我执行了以下审计,并说明了每条命令的实际用途。

Reading the GUI correctly first

首先,正确解读图形界面

Two things I had wrong at a glance. “Updated 9 years ago” is not neglect. That’s the package version date. (URW)++ Core Font Set 20200910-8, TeXInfo, BRLTTY — these are frozen-by-design dependencies pulled in by other packages. A font metric file doesn’t need a 2026 release to be safe. Gray placeholder icons are not unsigned packages. They mean no AppStream metadata or .desktop file. Most are libraries that were never meant to appear in a launcher. A few, as I found later, are a real (and benign) finding. Neither observation tells you anything about integrity. For that you need the actual tooling.

我有两点看错了。“9 年前更新”并不代表被遗弃,那是软件包的版本日期。(URW)++ Core Font Set 20200910-8、TeXInfo、BRLTTY——这些都是为了保持稳定而冻结的依赖项,由其他软件包引入。字体度量文件不需要 2026 年的版本来保证安全。灰色的占位符图标也不是未签名的软件包,它们意味着缺少 AppStream 元数据或 .desktop 文件。大多数是本就不该出现在启动器里的库文件。正如我后来发现的,其中少数确实是真实(且无害)的发现。但这两种观察都无法告诉你关于完整性的任何信息。要了解完整性,你需要使用真正的工具。

1. Snap publishers

1. Snap 发布者

snap list
for s in $(snap list | awk 'NR>1{print $1}'); do snap info "$s" | grep -E '^(name|publisher)' done

The publisher column is the entire point. Snap uses two markers:

  • ✓ Verified publisher — identity confirmed by Canonical
  • ✪ Starred — a trusted community maintainer

发布者列才是重点。Snap 使用两个标记:

  • ✓ 已验证发布者 — 身份已由 Canonical 确认
  • ✪ 星标 — 受信任的社区维护者

On my machine: 21 snaps, all marked. Canonical for the base and GNOME runtimes, mozilla✓ for Firefox, bitwarden✓ (8bit Solutions LLC), spotify✓. The three starred ones were Discord and ffmpeg-2404 from Snapcrafters, and Pinta from James Carroll — the legitimate upstream maintainers in both cases. An unmarked publisher on a snap claiming to be a well-known app is the thing to look for. There weren’t any.

在我的机器上:有 21 个 snap 包,全部都有标记。Canonical 负责基础和 GNOME 运行时,mozilla✓ 负责 Firefox,bitwarden✓ (8bit Solutions LLC),spotify✓。那三个星标包分别是来自 Snapcrafters 的 Discord 和 ffmpeg-2404,以及来自 James Carroll 的 Pinta——它们都是合法的上游维护者。如果一个声称是知名应用的 snap 包发布者没有标记,那才是需要警惕的。我这里没有发现这种情况。

2. Third-party APT repositories

2. 第三方 APT 仓库

This is the real risk vector, and it’s the one App Center will never show you.

grep -rhE '^deb ' /etc/apt/sources.list /etc/apt/sources.list.d/ 2>/dev/null

这是真正的风险向量,也是应用中心永远不会向你展示的部分。

Seven results, all using signed-by= with a dedicated keyring — which is the modern, correct form. The deprecated apt-key approach trusts a key for every repo on the system; signed-by scopes it to one. If you see repos without it, that’s worth fixing regardless of whether anything is compromised.

共有七个结果,全部使用了带有专用密钥环的 signed-by= 参数——这是现代且正确的做法。已被弃用的 apt-key 方法会信任系统中的每一个仓库密钥;而 signed-by 将其范围限制在一个仓库内。如果你看到没有此参数的仓库,无论是否被入侵,都值得修复。

Six were immediately recognizable: Docker, GitHub CLI, Heroku, Cloudflare WARP, mise, and the Ulauncher PPA. The seventh was truncated in my terminal and read, in part, antigravity-repo-key.gpg … us-central1-apt.pkg.dev/projects/antigra… Unfamiliar name, Google Cloud infrastructure hostname. Exactly the shape of thing that deserves thirty seconds of verification rather than a shrug. It’s Google’s Antigravity IDE. Google’s official Linux download page publishes that repo verbatim — same keyring path, same Artifact Registry host, same project. Confirmed, but confirming the URL is only half the check.

其中六个一眼就能认出:Docker、GitHub CLI、Heroku、Cloudflare WARP、mise 和 Ulauncher PPA。第七个在我的终端里被截断了,部分显示为 antigravity-repo-key.gpg ... us-central1-apt.pkg.dev/projects/antigra....。这是一个陌生的名字,属于 Google Cloud 基础设施的主机名。这正是那种值得花 30 秒验证而不是耸耸肩忽略的东西。它是 Google 的 Antigravity IDE。Google 的官方 Linux 下载页面逐字发布了该仓库——相同的密钥环路径、相同的主机名、相同的项目。确认无误,但确认 URL 只是检查的一半。

3. Verifying the signing key

3. 验证签名密钥

A repo URL you recognize means nothing if the key trusting it was swapped.

gpg --show-keys --fingerprint /etc/apt/keyrings/antigravity-repo-key.gpg

如果你信任的密钥被替换了,那么即使 URL 你很熟悉也毫无意义。

Full-length match against the published fingerprint, and a single pub key with no extra subkeys smuggled into the file. Compare all 40 characters — matching the last eight is a known-weak check that collision attacks have historically defeated.

与已发布的指纹进行全长比对,确保只有一个公钥,且文件中没有混入额外的子密钥。请比对全部 40 个字符——只匹配最后八位是一种已知的弱检查,历史上曾被碰撞攻击破解过。

One nuance that matters. This is Google’s shared Artifact Registry signing key. It signs every public AR apt repo, not just Antigravity’s. So the fingerprint proves “Google Artifact Registry published this” — it does not prove “the Antigravity team published this.” The trust boundary is the repository path. Verify both halves or you’ve verified neither.

有一个重要的细节:这是 Google 共享的 Artifact Registry 签名密钥。它签署了每一个公共 AR apt 仓库,而不仅仅是 Antigravity 的。因此,指纹证明了“Google Artifact Registry 发布了此内容”,但它不能证明“Antigravity 团队发布了此内容”。信任边界在于仓库路径。必须验证这两部分,否则等于什么都没验证。

4. Binary integrity

4. 二进制完整性

This is the command that actually answers the malware question.

sudo apt install debsums
sudo debsums -s

这是真正能回答恶意软件问题的命令。

debsums recomputes the checksum of every file installed from a .deb and compares it against what the package shipped. -s prints only problems. If something had patched a system binary, this is where it surfaces.

debsums 会重新计算每个从 .deb 安装的文件的校验和,并将其与软件包发布时的校验和进行比较。-s 参数仅打印出问题。如果有什么东西篡改了系统二进制文件,这里就会显示出来。

My output — three lines, all of the same kind: debsums: missing file /usr/share/applications/btop.desktop (from btop package) debsums: missing file /usr/share/applications/org.flameshot.Flameshot.desktop debsums: missing file /usr/share/applications/org.gnome.SystemMonitor.desktop

我的输出结果——三行,都是同一类型: debsums: 缺少文件 /usr/share/applications/btop.desktop (来自 btop 软件包) debsums: 缺少文件 /usr/share/applications/org.flameshot.Flameshot.desktop debsums: 缺少文件 /usr/share/applications/org.gnome.SystemMonitor.desktop

Missing is not modified, and the distinction is the whole finding. Zero checksum mismatches means not one binary or library on the system differs from what Ubuntu published. What’s missing is three launcher entries — which is also why those three showed gray placeholder icons back in the GUI. Almost certainly an app-grid cleanup I did and forgot. Malware adds .desktop files for persistence; it has no reason to delete them from unrelated packages.

“缺失”不等于“被修改”,而这种区别就是全部的发现。校验和零不匹配意味着系统中的每一个二进制文件或库都与 Ubuntu 发布的一致。缺失的是三个启动器条目——这也是为什么那三个图标在图形界面中显示为灰色占位符的原因。这几乎肯定是我之前清理应用网格时忘记了。恶意软件会添加 .desktop 文件以实现持久化;它没有理由从无关的软件包中删除它们。

sudo apt install --reinstall btop flameshot gnome-system-monitor

One caveat: not every package ships checksums, so debsums coverage is good but not total. Add sudo debsums -ca to list modified config files, which is a separate and noisier category.

一个注意事项:并非每个软件包都提供校验和,因此 debsums 的覆盖范围很广但并不全面。添加 sudo debsums -ca 可以列出被修改的配置文件,这是一个独立且更嘈杂的类别。

5. Running services and persistence

5. 运行中的服务与持久化

systemctl list-units --type=service --state=running
ls -la ~/.config/autostart/ /etc/systemd/system/

Thirty-four running services, every one accounted for: Docker and containerd, warp-svc matching the Cloudflare repo, ClamAV daemon and freshclam, and the usual GNOME and systemd stack. Autostart held two entries, both mine. The /etc/systemd/system/ listing had a detail I liked seeing: avahi-daemon.service -> /dev/null cups.service -> /dev/null libvirtd.service -> /dev/null

共有 34 个运行中的服务,每一个都有据可查:Docker 和 containerd,与 Cloudflare 仓库匹配的 warp-svc,ClamAV 守护进程和 freshclam,以及常规的 GNOME 和 systemd 堆栈。自启动项里有两个条目,都是我自己的。/etc/systemd/system/ 的列表里有一个我乐于见到的细节: avahi-daemon.service -> /dev/null cups.service -> /dev/null libvirtd.service -> /dev/null