怎么确认自己有没有中 AUR 投毒

2026 年 6 月的「Atomic Arch」事件里,1936 个 AUR 包被投毒,而 PKGBUILD 本身看着是干净的。这份自查分两轮:先比包名,再扫 IOC。末尾是 2026-09 在本机复跑的结果。

2026 年 6 月,AUR 上有一批包被投毒,官方名单里是 1936 个包名。

这件事值得单独立一份自查清单,原因载荷:窃取浏览器 Cookie、SSH 密钥、 各类 Token 之外,它在 root 权限下会加载一个 eBPF rootkit 来隐藏自己的进程、文件和网络连接。 按公开分析的说法,那东西卸载软件包去不掉,只能重装系统。

所以自查这件事,做得越早越省事(当然,由于 AUR 投毒事件发生时我没有使用 Arch,而用回 Arch 的时候已经是 7 月了,所以在时间上看起来有些奇怪)。

先决条件

需要 说明
Arch 系发行版 CachyOS / EndeavourOS / Manjaro 同样适用
能读 `/var/lib/pacman/` 查已装包与安装脚本,普通用户通常可读
几分钟 第一轮几秒,第二轮主要花在搜文件上

两个自查命令:

```bash
pacman -Qm                    # 列出所有非官方仓库的包(也就是 AUR 来的)
ls /sys/fs/bpf/               # eBPF 映射,rootkit 藏身的地方之一
```

事件经过

日期 发生了什么
6 月 11 日 第一波。载荷通过 `npm install atomic-lockfile` 拉取,1500+ 包
6 月 13 日 第二波。改用 `bun install js-digest`,代码混淆规避检测
6 月 14 日 第三波。载荷高度混淆,靠 AI 模型辅助分析才发现
6 月 15 日 Arch 暂停 AUR 新用户注册,清理全部恶意包

官方仓库没有受影响,被渗透的是 AUR 这个社区仓库。

攻击是怎么做的

```plaintext
① 找孤儿包
   AUR 允许认领无人维护的包
        ↓
② 认领后改 PKGBUILD 与 .install
        ↓
③ 伪造提交元数据,看起来像原维护者
        ↓
④ 载荷不写在 PKGBUILD 里
   而是让 PKGBUILD 去 npm / bun 下载
```

第 ④ 步是这件事最难查的地方。你去读 PKGBUILD,上面没有一行恶意代码 —— 它只是调用了一次包管理器,而恶意内容在远端那个 npm 包里。

所以「我每次都读过 PKGBUILD」在这里不构成防线。要防的是另一件事: AUR 包为什么要从 npm / pip / bun 下载东西。

第一轮:比包名

拿到官方名单,和自己的 AUR 包列表做一次全等比对。

```bash
# 官方名单:https://md.archlinux.org/s/SxbqukK6IA
# 存成 package-list.txt,一行一个包名

pacman -Qm | awk '{print $1}' > /tmp/aur-mine.txt
comm -12 <(sort /tmp/aur-mine.txt) <(sort package-list.txt)
```

`comm -12` 输出的是两个文件的交集。有输出就是命中,没有输出就是干净。

用 `comm` 而不是 `grep -f`,是因为它按整行精确比对,不会把 `fcitx5` 匹配到名单里的 `fcitx`。

本机(2026-09-20)的结果:

```bash
pacman -Qm | wc -l        # → 27
```

27 个 AUR 包逐个比对,0 命中。

第二轮:扫 IOC

包名干净不等于没中 —— 包可能已经删掉,载荷还在。下面每一项都是可以独立跑的。

① eBPF rootkit 的映射

```bash
ls /sys/fs/bpf/ | grep hidden
# → 无输出
```

公开分析里提到的映射名是 `hidden_pids`、`hidden_names`、`hidden_inodes`。 这几个名字本身就是「我要藏东西」的意思,正常系统上不会有。

② 恶意二进制

```bash
find /usr /opt /var/lib /tmp -maxdepth 4 -name deps -type f 2>/dev/null
# → 无输出
```

载荷是一个 Rust 写的、名叫 `deps` 的 ELF 文件。

③ npm 缓存里的恶意包

```bash
grep -rl 'atomic-lockfile\|js-digest\|lockfile-js\|nextfile-js' ~/.npm/_cacache/index-v5/
# → 无输出
```

四个恶意 npm 包名:

包名 版本 说明
`atomic-lockfile` 1.4.2 第一波,载荷文件 `src/hooks/deps`
`js-digest` 4.2.2 第二波,载荷伪装成 `lib/install-deps.mjs`
`lockfile-js` 1.4.2 变体
`nextfile-js` 1.4.2 变体

④ 持久化

```bash
ls ~/.config/systemd/user/*.service
# 逐个看:是不是你自己建的
```

载荷会往 systemd 里塞服务,特征是 `Restart=always` 配 `RestartSec=30`。

```bash
grep -rl 'RestartSec=30' /etc/systemd/system/
# → 无输出
```

⑤ 出站连接

```bash
pgrep -a tor
# → 无输出
```

C2 走的是 Tor v3 onion:

```plaintext
olrh4mibs62l6kkuvvjyc5lrercqg5tz543r4lsw3o6mh5qb7g7sneid.onion
```

同时也往 `temp.sh` 上传。系统上没有 Tor 进程,这条基本可以排除。

⑥ 安装脚本

```bash
find /var/lib/pacman/local/ -name '*.install'
```

本机 1763 个包目录里,`.install` 文件数是 0 —— 这条检查项在本机没有可检查的对象。 写出来是因为它对你的机器可能不是 0。

复跑结果(2026-09-20)

上面两轮在本机重跑了一遍,不是复述七月的结论:

检查项 结果
AUR 包名比对(27 个 vs 1936 行名单) 0 命中
eBPF `hidden_*` 映射 0 处
恶意二进制 `deps` 0 个
npm 缓存中的恶意包 0 处
用户级 systemd 服务 3 个,都是自己建的(主题同步、mip-paper、kdeconnect 监听)
Tor 进程 0 个
`/var/lib` 下 `atomic*` 植入物 0 个
`RestartSec=30` 的系统服务 0 个
pacman `.install` 脚本 全机 0 个

三个「看起来像、其实无关」的

自查时容易误报的几处:

看着可疑 实际情况
linux-cachyos 内核 CachyOS 内核来自官方仓库。投毒名单里的是 AUR 上的 `linux-cachyos-native`,另一个东西
`fcitx` 在名单里 那是旧版 `fcitx`。官方仓库的 `fcitx5` 不受影响
有 `proper-lockfile` 合法 npm 包,和恶意的 `lockfile-js` 不是一回事

装之前怎么防

第一轮和第二轮都是事后检查,而 eBPF rootkit 那一步是不可逆的。所以在装之前:

  1. 能用官方仓库就用官方仓库。 AUR 只留给确实没有替代的包,本机 1763 个包里只有 27 个来自 AUR,这个比例是刻意维持的。
  2. AUR 包主动去 npm / pip / bun 下载依赖的,直接当可疑处理。 这是这次事件的核心手法。
  3. 警惕「最近换过维护者」的废弃包。 攻击入口就是孤儿包收养。
  4. 看 `source` 字段的上游 URL,是不是指向项目的官方地址。
  5. 别跳过 PKGBUILD 的 diff。 `yay` 默认会展示,认真读完再回车。

正常打包不会在安装阶段去网上抓代码。这么做既违反打包惯例,也绕开了读 PKGBUILD 这个动作 —— 而那是你手上仅有的审查点。

如果中了

按公开分析的描述,eBPF rootkit 会隐藏自己的进程、文件和套接字,卸载软件包去不掉。

处理顺序:

  1. 断网,别让它继续外传
  2. 备份数据,不要备份二进制和配置(`~/.ssh`、浏览器配置、各类 Token 都算泄露,要轮换而不是备份)
  3. 重装系统
  4. 重装后轮换所有凭据:SSH 密钥、GitHub Token、各类 API Key、浏览器登录态

参考