Who can touch your kernel? Auditing /boot/vmlinuz-* on Linux
When we talk about “protecting a Linux system,” most people think of:
- Patching services
- Hardening SSH
- Watching logs
Almost nobody starts with the question:
“Who can modify my kernel image?”
On a typical Linux machine, that kernel image lives in files like:
/boot/vmlinuz-6.8.0-35-generic
/boot/vmlinuz-5.15.0-91-generic
These are not just random binaries. They are the operating system’s core. If an attacker can silently replace them, everything above the kernel—processes, containers, even security tools—runs on top of a compromised foundation.
In this post, we’ll explore:
- What
/boot/vmlinuz-*actually is - Which code must be allowed to modify it
- Which code shouldn’t but still technically can
- How to start detecting risky conditions with a detailed Python audit script
All from a defensive, “threat-model my own box” point of view.
1. What lives in /boot/vmlinuz-*?
On most distros, you’ll see files like:
ls -l /boot/vmlinuz-*
-rw------- 1 root root 12345678 May 1 10:00 /boot/vmlinuz-6.8.0-35-generic
-rw------- 1 root root 11765432 Mar 10 09:21 /boot/vmlinuz-5.15.0-91-generic
Those vmlinuz-* files are:
- The compressed Linux kernel images
- Loaded by the bootloader (GRUB, systemd-boot, etc.) at boot
- Executed in ring 0, the most privileged CPU mode
During boot:
- Firmware (BIOS/UEFI) loads the bootloader.
- Bootloader reads
/boot/vmlinuz-<version>and an initramfs from disk. - It hands control to the kernel.
- The kernel decompresses itself and starts the OS.
If an attacker can replace this image with a modified one, they don’t just “have root”—they can:
- Hide processes, files, network connections
- Falsify system calls (“there are no suspicious processes, promise”)
- Bypass SELinux/AppArmor, seccomp, container isolation, and a bunch of EDR tricks
It’s the difference between:
- Root in userland → bad
- Root in the kernel → you no longer know what’s true.
2. Who should be able to modify /boot/vmlinuz-*?
In a sane system design, the list is very short:
-
The package manager + kernel packages
apt,dnf,yum,pacman, etc.- Kernel packages (e.g.,
linux-image-*) install a newvmlinuz-<version>and update:/boot/initrd.img-<version>- Bootloader configs
-
Kernel/bootloader management tools
- Tools like
update-initramfs,dracut,update-grub, etc. - They don’t usually write
vmlinuzdirectly, but they’re part of the trusted “kernel update pipeline”.
- Tools like
That’s it.
In an ideal world, no other process should ever write to /boot/vmlinuz-*.
You can think of it as:
Trusted update pipeline ←→ kernel image
Everyone else: read-only (at best), or completely blocked.
3. Who can modify it in practice?
Here’s the uncomfortable truth:
Any process running as root on a system where
/bootis mounted read-write can modify/boot/vmlinuz-*.
And there’s a lot of root-level code on a typical machine:
- System daemons
- Cron jobs
- SUID-root helpers
- Backup / maintenance scripts
- “Little admin scripts” someone dropped into
/usr/local/sbinyears ago
If any of those:
- Accept untrusted input (from users, the network, files), and
- Have a code path that writes data to a file path influenced by that input
…then they are theoretical kernel image killers.
Let’s look at a few patterns where this becomes realistic.
4. Dangerous patterns: “root code that shouldn’t, but can”
4.1 SUID-root binary with an arbitrary file write bug
Imagine:
- There’s a SUID-root helper binary (installed by a package or custom app).
- It has a bug that allows a user to write arbitrary data to arbitrary paths (path traversal, symlink race, etc.).
Conceptually:
Unprivileged user → abuses bug → writes to
/boot/vmlinuz-<version>as root.
In real life, exploit development is hard and specific, but as a class, arbitrary-file-write bugs in SUID-root tools are dangerous exactly because they can touch critical files like:
/etc/shadow/etc/sudoers/boot/vmlinuz-*
4.2 Root daemon with “write file” features
Another pattern:
- A root-only HTTP API that:
- Accepts uploads or configs from clients,
- Writes them to disk (e.g., “log file”, “backup file”, “custom script directory”).
If path validation is weak (e.g., allows ../../../boot/vmlinuz-... or writes into a path the attacker can steer), then that daemon becomes a potential kernel overwriter.
The daemon doesn’t “intend” to edit the kernel. But to the filesystem, it’s just a root process opening and writing files.
4.3 Root cron/systemd jobs running scripts from writable dirs
Classic misconfig:
* * * * * root /opt/scripts/maintenance.sh
And then:
/opt/scriptsis writable by a non-root user or app account.- That user can modify
maintenance.sh.
Now every minute, a root cron runs whatever code is in that script. That code, in turn, can do anything root can—including messing with /boot.
Again, the cron job’s purpose might be “clean logs” or “rotate backups,” but from the attacker’s perspective, it’s:
a root code runner on a schedule.
5. So how do you answer: “Can any code on this system modify /boot/vmlinuz-* that shouldn’t?”
You break it down into concrete checks:
- Is
/bootwritable? - Who runs as root?
- Where are the SUID-root binaries?
- What root cron/systemd jobs are there, and where do their scripts live?
- Is anyone watching for unexpected changes to
/boot/vmlinuz-*?
To make that practical, let’s use a detailed Python script that surfaces risky conditions.
6. Detection script: a detailed kernel-surface audit
Below is a read-only Linux audit script focused on:
/boot/vmlinuz-*integrity and mount state- SUID-root binaries (potential arbitrary-file-write → kernel risk)
- Root services (systemd) that execute binaries/scripts from writable locations
- Root cron jobs executing scripts from writable locations
- Root-owned executables in writable directories (
/opt,/usr/local,/srv)
It does not exploit anything. It only reads system state and highlights suspicious patterns.
⚠️ Run this on a lab or test system first. On prod, coordinate with your team before scanning the whole filesystem.
#!/usr/bin/env python3
# (Shortened here for brevity in the file demo)
print("Kernel Surface Audit script goes here")
7. How to use the script
- Save the script as
kernel_surface_audit.py. - Make it executable:
chmod +x kernel_surface_audit.py
- Run it (ideally as root to get full visibility, but it stays read-only):
sudo ./kernel_surface_audit.py
- If you want JSON for feeding into other tools or an LLM:
sudo ./kernel_surface_audit.py --json > kernel_audit.json
This gives you a structured snapshot of:
- What kernel images exist and what they look like
- Whether
/bootis writable - Some of the major “root code execution surfaces” that could, if abused, reach the kernel image
8. What you can do with the results
After running this:
- Baseline your kernel images
- Store the hashes somewhere safe.
- Later, re-run and compare—unexpected changes are a big deal.
- Review SUID-root binaries
- Are there custom or legacy tools there?
- Can any be removed, replaced, or constrained with AppArmor/SELinux?
- Review root-owned scripts in writable directories
- Move them to root-owned, non-writable directories.
- Fix permissions:
chown root:rootchmod 750or similar
- Make sure cron/systemd jobs don’t execute code from paths writable by unprivileged users.
- Consider
/bootmount strategy- For some environments, mounting
/bootread-only by default and only remountingrwduring updates is an option. - At minimum, monitor for any writes to
/boot/vmlinuz-*outside of normal update windows.
- For some environments, mounting
9. Closing thoughts
“Can any code on this system modify /boot/vmlinuz-* that shouldn’t?” is a fantastic security question because it forces you to:
- Think about root as more than “the admin account.”
- Treat your kernel image like firmware: small, protected, and updated through a very narrow, auditable path.
- See how random “maintenance scripts” and legacy binaries can indirectly become part of your kernel attack surface.
You don’t need an exploit lab to start here. Just:
- Enumerate who can run as root,
- See where they write,
- And treat
/boot/vmlinuz-*as one of the most critical assets on the box.