Feb 26, 2026·6 min read·28 visits
Local root exploit in Junos OS kernel allowing bypass of binary signature enforcement (Veriexec). Actively exploited by UNC3886 to plant rootkits (RedPenguin). Low CVSS (4.4) masks high impact: total, persistent device compromise.
A critical improper isolation vulnerability in the Juniper Networks Junos OS kernel allows local attackers to bypass the Veriexec integrity subsystem. Exploited in the wild by the China-nexus threat actor UNC3886 as part of the RedPenguin campaign, this flaw enables the installation of persistent, stealthy rootkits on carrier-grade routers, turning network infrastructure into invisible espionage platforms.
Carrier-grade routers are supposed to be fortresses. When you buy a Juniper MX or SRX series box, you're paying for hardware that moves packets at the speed of light and software that is hardened against the chaotic cesspool of the internet. A key part of that hardening is Veriexec (Verified Executables), a kernel-level bodyguard derived from FreeBSD. Its job is simple: if a binary isn't digitally signed by Juniper, it doesn't run. Period.
But what if you could distract the bodyguard? CVE-2025-21590 is exactly that distraction. It isn't a remote code execution vulnerability that kicks the front door down. It's something far more insidious. It's a method for an attacker who already has the keys (shell access) to turn off the security cameras and install a secret elevator in the basement.
This vulnerability is the centerpiece of the RedPenguin campaign by UNC3886, a China-nexus threat actor known for living off the land—and now, living inside the kernel. They aren't just breaking in; they are moving in, setting up furniture, and ensuring that even if you reboot the device, they are still watching your traffic.
To understand the gravity of CVE-2025-21590, you have to respect the architecture of Junos OS. It runs on a modified FreeBSD kernel. In this environment, root isn't supposed to be God anymore. Even the root user is restricted by Veriexec, which enforces mandatory access control based on digital signatures. You can't just insmod a malicious kernel module or replace /sbin/init with a bash script, because the kernel checks the signature before execution.
The vulnerability, classified as CWE-653 (Improper Isolation or Compartmentalization), breaks this contract. It resides in how the kernel handles specific state transitions or memory isolation for privileged processes. The flaw allows a local attacker—specifically one who has already dropped to a shell (which is distinct from the CLI)—to manipulate kernel memory or capabilities in a way that effectively toggles the Veriexec enforcement flag to 'OFF'.
Think of it like a bank vault with a time lock. The lock is unbreakable, but the wiring for the timer is exposed on the outside wall. If you know which wire to cut, the vault opens immediately. UNC3886 found the wire. By exploiting this isolation failure, they can inject code into the kernel space, bypassing the very mechanism designed to prevent code injection.
While the exact source code for the proprietary Junos kernel isn't public, we can reconstruct the vulnerability mechanism based on standard BSD Veriexec implementations and the exploit behavior. In a hardened BSD system, the Veriexec level is usually raised during boot and cannot be lowered (a property known as securelevel).
However, the flaw in Junos OS likely involves a race condition or an exposed syscall that allows modifying the veriexec_bypass check. Conceptually, the vulnerable kernel logic looks something like this:
// Conceptual Vulnerability in Kernel Access Control
int veriexec_verify(struct vnode *vp, struct thread *td, int mode) {
// If the global bypass flag is set, skip checks
// The BUG: This flag should be immutable after boot!
if (kernel_security_state->bypass_veriexec == 1) {
return (0); // Authorized!
}
// ... perform signature verification ...
}The exploit targets the memory structure holding bypass_veriexec (or its equivalent capability bitmask). By leveraging the improper isolation, the attacker essentially does this:
// The Attack Primitive
void disable_protection() {
// 1. Locate the kernel security structure in memory
void *security_struct = find_kernel_symbol("security_state");
// 2. Overwrite the enforcement flag
// This should be blocked by memory protection, but isn't.
write_kernel_memory(security_struct + OFFSET_FLAGS, 0x1);
}The fix implemented in versions like 21.2R3-S9 and 24.2R2 likely involves strictly enforcing Write-XOR-Execute (W^X) on these kernel structures or removing the code path that allowed the state modification entirely.
The attack chain observed in the wild is a masterclass in persistence. It doesn't start with an exploit; it usually starts with compromised credentials. But credentials can be rotated. A rootkit is forever.
The attacker gains access to the Juniper device via SSH, authenticating as a high-privileged user. They drop to the underlying BSD shell (start shell). They upload a small, seemingly innocuous binary. This is the loader.
The loader executes. It triggers CVE-2025-21590, manipulating the kernel state to temporarily suspend Veriexec enforcement. For a brief window, the router is blind to signature violations.
With the shields down, the attacker deploys the payload. In the RedPenguin campaign, this involves highly sophisticated rootkits like REPTILE, MEDUSA, or MOPSLED. These aren't just user-mode binaries; they are kernel modules or injected threads into legitimate daemons (like rpd or chassisd).
# Conceptual Attack Flow
user@junos% ./cve_2025_21590_exploit
[+] Kernel patching successful. Veriexec disabled.
user@junos% insmod reptile.ko
[+] Module loaded.
user@junos% ./cleanup.sh
[+] Logs wiped. Veriexec re-enabled.Once loaded, the rootkit hooks kernel functions like getdents (get directory entries) and sysctl. If an admin runs show system processes or looks at /var/log, the rootkit filters the output. The malware is running, but the router lies to you about it.
If you look at the CVSS score of 4.4 (Medium), you might be tempted to deprioritize this patch. That would be a catastrophic mistake. The score is low only because the attacker needs high privileges (Local/Admin) to execute it. But in the world of APTs and nation-state actors, gaining initial access is trivial—it's maintaining access that is hard.
CVE-2025-21590 solves the "hard" part for the attacker. It converts a compromised password into a compromised hardware lifecycle. The impact is Critical for three reasons:
This is not a vulnerability you patch next month. If you are a target of interest for groups like UNC3886, this is a "drop everything" emergency.
CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:N/I:H/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
Junos OS Juniper Networks | < 21.2R3-S9 | 21.2R3-S9 |
Junos OS Juniper Networks | 21.4 < 21.4R3-S10 | 21.4R3-S10 |
Junos OS Juniper Networks | 24.2 < 24.2R1-S2 | 24.2R1-S2 |
| Attribute | Detail |
|---|---|
| CVE ID | CVE-2025-21590 |
| CWE ID | CWE-653 (Improper Isolation) |
| CVSS v3.1 | 4.4 (Medium) |
| Attack Vector | Local (Shell Required) |
| Impact | Security Bypass / Persistence |
| KEV Status | Listed (Active Exploitation) |
| Threat Actor | UNC3886 (RedPenguin) |
Improper Isolation or Compartmentalization