AUR Supply Chain Attack: How to check your (Arch Linux) System
The supply chain attacks on linux just keep coming: in June 2026, a malicious package was discovered in the Arch User Repository (AUR). I first read about this in ioctl.fail. Here is what happened in the short version (for long version go to ioctl.fail).
An attacker masked a binary by hiding it in the dependency tree of (the legitimate package) atomic-lockfile (v1.4.2). The hidden binary runs at install time via an npm hook. It is designed to steal credentials, installs a persistent systemd service and loads an eBPF rootkit to hide itself and exfiltrates everything over Tor. All silently - so this is pretty advanced stuff.
I use AUR regularly and - up until now - without too much thought (I know I know). So I took this occasion to check my system for infection and thought it might be helpful for you to read about this. This is a quick write-up of what and how I looked for and what you can do to reduce your attack surface going forward.
What the malware does
The malicious package looks like a normal npm package with a file tree looking roughly like this:
atomic-lockfile/
├── package.json
├── index.js
└── src/
└── hooks/
└── deps ← compiled ELF binary, committed as a plain fileThe deps binary is embedded in src/hooks/deps and is triggered by a preinstall npm hook. So when you run npm install, npm will download and extract the malware to your system. The malware then harvests credentials from Chrome-based browsers, workplace tools like Slack, Teams, Discord and look for secrets in developer services like Github, npm, OpenAI, SSH keys, etc.
It also creates persistence via a systemd service with Restart=always or RestartSec=30. If you run npm as a user (as you should), the script will at least have no superuser powers. But it will still be able to steal credentials from ~/.ssh/ and ~/.config/ and shell history. If you install as root user (no no) the script will load an eBPF rootkit.
It will then exfiltrate data via Tor to a hardcoded (but obfuscated) onion address. This is another instance of exploiting the trust model of AUR: packages are community-maintained, and the community convention is “read the PKGBUILD before you build.” However, I have to admit I hardly ever do on my private machine. But that convention breaks down anyway when the malicious payload is buried inside a lifecycle hook of a nested npm dependency. To detect a package like that you would need to read the full dependency tree.
How to check your system
So you can imagine that I was pretty paranoid about having pulled in the infected package. I wanted to check my system immediately; let me walk you through what I did:
Step 1 — Was the package ever installed?
First check your npm to see if the file in question was ever installed.
npm list -g atomic-lockfile
npm list atomic-lockfile
ls ~/.npm/atomic-lockfile/
ls ~/.cache/yay/ # or ~/.cache/paru/If atomic-lockfile v1.4.2 appears anywhere, treat the system as compromised.
Step 2 — Inspect systemd units
We have seen that the malware installs a service with a random name and RestartSec=30, so check your running services for that:
systemctl list-unit-files --type=service --state=enabled
systemctl --user list-unit-files --type=service --state=enabled
# Grep for the restart signature directly
grep -r "Restart" /etc/systemd/system/
grep -r "Restart" ~/.config/systemd/user/Any service you don’t recognise with Restart=always is suspicious. For me this returned the getty terminal service - harmless.
Step 3 — Check the BPF filesystem
I run my npm as a user, but let’s check the BPF angle just in case. Look for hidden files.
ls -la /sys/fs/bpf/The rootkit pins maps with predictable names: hidden_pids, hidden_names, hidden_inodes. If these exist, your process view is being filtered your /proc output cannot be trusted.
Step 4 — Scan for unowned executables in /var/lib/
The binary copies itself to /var/lib/<random_name>. Since the name is generated, search by characteristic:
find /var/lib -maxdepth 3 -type f -executable \
! -path "*/docker/*" ! -path "*/containerd/*" 2>/dev/null | while read f; do
pacman -Qo "$f" &>/dev/null || echo "UNOWNED: $f"
doneAnything UNOWNED is suspicious. For me that returned color profile databases - (check with file <path>) - harmless.
Step 5 — Network check
The malware reports to a Tor C2 server. So check your network traffic:
ss -tnp state established
ss -tnp | grep "127.0.0.1"Look for: outbound connections on port 9050/9150 (Tor SOCKS) from unexpected processes, or local loopback connections to unfamiliar ports (the malware routes through a local proxy).
Step 6 — Hash check
If you find a suspicious file, you can check for the known hash:
sha256sum /path/to/suspicious/file
# Known hash: 6144D433F8A0316869877B5F834C801251BBB936E5F1577C5680878C7443C98BIf you find any sign of the malware during the steps above, you should treat your system as compromised. If possible do not wipe yet, but preserve the evidence. You must cycle your passwords for the services mentioned above! If you found nothing you are probably fine.
Reducing my attack surface
Checking for this specific attack is of course useful after the fact. But for me this has been a wake-up call to build better cybersecurity habits on my local machine. Here is what I have committed to:
Audit my AUR
pacman -QmThis lists all “foreign” packages - packages not from the official repos. This is my AUR footprint. I will review it periodically and remove packages I no longer need. Fewer AUR packages means a smaller attack surface.
Always review PKGBUILDs and package metadata before building
I should really stick to this - this is the AUR social contract. Most AUR helpers make it easy:
# yay — review before building
yay -S <package> --editmenu
# or manually
yay -G <package> # clone to current dir
less <package>/PKGBUILD
less <package>/<package>.installI will try to pay attention to .install files and any lifecycle hooks in embedded package.json files. A PKGBUILD that pulls in npm dependencies is a larger trust surface than one that builds from source.
I will also - before installing an AUR package - check the vote count and comment/commit history for a package. A package with 3 votes, no comments, and a recent .SRCINFO update touching a lifecycle hook is a red flag.
Disable npm lifecycle scripts globally
For development work, lifecycle scripts are often necessary. But you can disable them system-wide and re-enable per project:
npm config set ignore-scripts trueWhen you need scripts in a specific project you can just do npm install --ignore-scripts=false. This reduces the attack surface meaningfully: the atomic-lockfile payload ran via preinstall hook. With scripts disabled, it would have never executed.
Longterm - build in a clean chroot or use aurutils
Longterm I am thinking about changing the process how I install aur packages altogether: Either use makechrootpkg or aurutils to build packages in an isolated chroot. Using makechrootpkg you can build a package in a clean environment without access to your secrets.
This doesn’t stop a malicious binary from being installed later, but it prevents the build process itself from harvesting your secrets.
Another option is using aurutils, which separates the AUR workflow into discrete auditable steps (fetch → review → build → install) rather than collapsing them into a single command. Both approaches bring more friction, but also more control.
I will have to look into these options and probably make another post on these.
The bigger picture
This attack is a reminder that supply chain attacks on linux are not theoretical. Especially npm has seen this pattern repeatedly. The AUR’s trust model (community review of PKGBUILDs) is sound in principle, but breaks down when people don’t pay attention. This case is especially nefarious because:
- The malicious payload is nested inside a dependency’s dependency
- The PKGBUILD itself is clean — only the bundled npm package is compromised
- Reviewers don’t audit the full dependency tree
This is the other side of the open-source coin: if you run community-maintained software, you inherit its trust model.
References: Preliminary analysis of AUR malware (ioctl.fail)