Oct 8, 2026·6 min read·6 visits
A local attacker can exploit a TOCTOU race condition and insecure temporary file creation in lz4-java's JNI loader to execute arbitrary code as the victim JVM user.
A local privilege escalation and code execution vulnerability exists in the yawkat fork of lz4-java when extracting its bundled JNI shared library into the system temporary directory. Predictable path derivation and lack of exclusive file creation flags allow a local attacker to hijack library loading via a race condition.
The yawkat fork of lz4-java (upstream net.jpountz.lz4) features a utility class responsible for loading its JNI native library dynamically at runtime. When the library is initialized, if no system-native lz4 library is pre-installed on the host system, the loader extracts its bundled native library (.so, .dylib, or .dll) into a temporary directory.
Historically, this dynamic extraction targeted the system temporary directory defined by the java.io.tmpdir system property (typically /tmp on UNIX systems). In these multi-user directories, multiple processes share the same storage space. The vulnerable library versions ranging from 1.7.0 up to 1.11.3 expose a significant attack surface during this initialization phase.
The vulnerability is classified under CWE-367 (Time-of-check Time-of-use Race Condition) and CWE-377 (Insecure Temporary File). Local unprivileged attackers can exploit this behavior to hijack the binary dynamic extraction and force the victim JVM to execute arbitrary native code. The risk is elevated on shared hosting servers, terminal servers, and multi-tenant environments where low-privilege users share the same operating system.
The technical root cause lies in how the JNI loader determines the destination file path and writes the compiled binary. During start-up, net.jpountz.util.Native.load() generates a lockfile using the safe File.createTempFile() API. This initial lockfile creation is robust because it relies on OS-level exclusive creation flags like O_CREAT | O_EXCL which fail if the file name already exists.
However, the flaw is introduced in the next step where the actual target binary destination path is calculated. Instead of calling File.createTempFile() again to allocate a safe, unique file path for the library, the system derives the destination path programmatically. It strips the .lck extension from the previously created lockfile path via string replacement to obtain the final library location.
Because the resulting destination path is predicted and passed directly to a standard FileOutputStream constructor, the JVM does not enforce exclusive creation. It opens the target path without checking if the file is a pre-existing symbolic link or file descriptor owned by another user. If an attacker pre-creates the file or drops a symbolic link at that predicted path between the time the lockfile is created and the time the stream writes to it, the JVM blindly follows the link and writes to the destination.
Analyzing the vulnerable code path vs. the patched code reveals the precise mechanism of the flaw and its remediation. The vulnerable JNI extraction routine uses a lockfile to coordinate multi-process extraction, but relies on a predictable name conversion that leaves it vulnerable to race conditions.
Below is the comparison highlighting the vulnerable path and the patched implementation:
// VULNERABLE CODE PATH (lz4-java < 1.11.4)
// Creates a secure temporary file to act as a lock
tempLibLock = File.createTempFile("liblz4-java-", "." + os().libExtension + ".lck");
// Programmatic derivation of the target library path - INSECURE
tempLib = new File(tempLibLock.getAbsolutePath().replaceFirst(".lck$", ""));
// FileOutputStream is opened without exclusive flags, making it vulnerable to symlink attack
try (FileOutputStream out = new FileOutputStream(tempLib)) {
byte[] buf = new byte[4096];
// ... copies stream to tempLib ...
}
// PATCHED CODE PATH (lz4-java >= 1.11.4)
// No lockfile is derived. The library is directly created using createTempFile which ensures O_EXCL
tempLib = File.createTempFile("liblz4-java-", "." + os().libExtension);
try (FileOutputStream out = new FileOutputStream(tempLib)) {
byte[] buf = new byte[4096];
// ... copies stream securely ...
}The fix is complete and highly effective because it removes the string replacement entirely. By calling File.createTempFile directly on the final target binary name, the application ensures that the operating system atomically validates file creation, preventing an attacker from planting a file or symlink beforehand. Variant attacks targeting this specific path are prevented by the atomic nature of the system calls behind createTempFile.
To successfully execute this attack, a local adversary must monitor file events in the target temporary directory using mechanisms like inotify or a rapid polling loop. The attacker awaits the creation of a lockfile with the pattern liblz4-java-*.lck which signals that a JVM application using the library has started.
Upon identifying a lockfile, the attacker calculates the expected JNI output path. Within the brief window of time before the JVM starts writing the library payload, the attacker places a symbolic link pointing to a shared file or directly pre-creates the target filename with world-writable permissions.
Once the JVM finishes writing the binary data through the symlink, the attacker immediately modifies or replaces the target binary with a compiled malicious native library containing custom JNI_OnLoad payloads. When the victim JVM continues its initialization and calls System.load(), it executes the malicious dynamic library, elevating the attacker's privileges to those of the victim JVM process.
The impact of successful exploitation is complete compromise of the JVM process. Because the malicious library is loaded into memory using System.load(), the code runs within the same memory space and privilege context as the calling application.
If the victim application runs under a highly privileged service account or root user, the local attacker gains corresponding privileges on the host system. This can lead to arbitrary code execution, local privilege escalation, unauthorized database access, or file system manipulation.
The CVSS v4.0 score is assessed at 7.3 (High) with a vector of CVSS:4.0/AV:L/AC:H/AT:P/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N. This reflects that while the attack is restricted to local vectors and requires winning a tight race condition, the resulting impact on confidentiality, integrity, and availability is critical.
The definitive solution is upgrading the lz4-java dependency to version 1.11.4 or higher, which replaces the insecure lockfile logic with safe atomic file creations. Organizations should update their build definitions to pull the latest secure release from Central Repository.
If upgrading is not immediately possible, several effective mitigations can be implemented to minimize the attack surface. The most reliable workaround is configuring a private temporary directory for the JVM process, which prevents local attackers from monitoring or writing to the target directory. This is achieved by setting the system property -Djava.io.tmpdir=/path/to/private/dir where the private directory has restricted permissions (e.g., 700).
Additionally, system administrators can enable OS-level symlink protections. In modern Linux distributions, setting fs.protected_symlinks=1 and fs.protected_regular=2 via sysctl prevents processes from following symbolic links in world-writable directories if the link owner does not match the process owner. This kernel control completely blocks the symlink vector of this exploit.
CVSS:4.0/AV:L/AC:H/AT:P/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
lz4-java yawkat | >= 1.7.0, < 1.11.4 | 1.11.4 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-367 (TOCTOU) / CWE-377 (Insecure Temporary File) |
| Attack Vector | Local |
| CVSS Score | 7.3 (High) |
| EPSS Score / Percentile | 0.00083 / 0.21% |
| Exploit Status | Proof-of-Concept |
| KEV Status | Not Listed |
The software checks the state of a resource before using that resource, but the resource's state can change between the check and the use.
An unauthenticated Server-Side Request Forgery (SSRF) vulnerability exists in Ghost CMS from version 6.54.1 to 6.65.0. The vulnerability stems from a validation bypass in the favicon resolution logic within the bookmark-fetching subsystem, which allows remote, unauthenticated attackers to trigger arbitrary HTTP requests to the local host and internal networks. This bypass circumvents the custom DNS-level IP blocklist controls configured globally in the application.
A resource allocation vulnerability (CWE-770) in lz4-java before version 1.11.4 allows an unauthenticated remote attacker to trigger CPU exhaustion and high garbage collection overhead by streaming empty concatenated LZ4 frames.
A Denial of Service (DoS) vulnerability exists in the yawkat fork of lz4-java prior to version 1.11.4. Under specific non-default configurations (stopOnEmptyBlock = false), parsing crafted streams with a large sequence of contiguous empty LZ4 blocks triggers uncontrolled recursion inside the LZ4BlockInputStream.refill() method, causing stack exhaustion and thread termination.
CVE-2026-76485 is a critical stack-based buffer overflow vulnerability in the VXLAN OAM (NGOAM) parsing component of Cisco NX-OS Software. The flaw enables an unauthenticated, remote attacker to execute arbitrary code with root privileges or trigger a denial of service on affected Nexus switches. This vulnerability is triggered through crafted packets sent to an IP interface. No workarounds are currently available to mitigate the vulnerability while preserving the NGOAM functionality. Cisco has published software patches to address this flaw.
A missing authorization vulnerability in Langflow versions 1.0.0 through 1.10.0 allows authenticated users (and unauthenticated users in versions prior to 1.7.2) to access private workflow structures and execute graph components by targeting deprecated API endpoints.
A critical OS command injection vulnerability exists in Langflow's Model Context Protocol (MCP) server integration using stdio transport, allowing unauthenticated remote command execution under default configurations.