---
Summary:
A recent vulnerability—CVE-2025-27148—impacts users of the Gradle build automation tool, specifically through its native-platform integration. Due to how temporary files were handled on Unix-like systems in older releases, attackers could escalate privileges by racing with library initialization. This post explains the bug, who is at risk, the technical details (with code), available fixes, and practical ways to stay safe.
Quick Background: What’s Gradle and Native-Platform?
Gradle is a popular open-source build automation tool, powering Android, CI/CD pipelines, and more. To offer native OS integration (like process handling, file watching, native libraries), Gradle uses net.rubygrapefruit:native-platform, a Java library that bridges native APIs.
When Gradle or any Java process loads native binaries, it often copies them from internal resources to a temporary system directory—commonly /tmp/ on Unix-like systems. If that step is mishandled, you may be open to race conditions or privilege escalation.
The Issue: Unsafe Temporary Directory Use Before Gradle 8.12.1
On some Unix-like systems, /tmp/ is a world-writable directory, meaning anyone can create or delete files there. To avoid one user deleting another's files, the "sticky" bit (chmod +t /tmp) is usually set—but not always.
Before version .22-milestone-28 of the native-platform library, if Native.get(Class<?>) was called (without proper initialization), the library defaulted to using the system temp directory for extracting native binaries:
// Simplified: NativeLibraryLocator.java (before fix)
public class NativeLibraryLocator {
public File locateLibrary() {
File tmpDir = new File(System.getProperty("java.io.tmpdir"));
// Create directory for user
File nativeDir = new File(tmpDir, "gradle-native-" + System.getProperty("user.name"));
nativeDir.mkdirs();
// Extract or copy native binaries
...
return nativeDir;
}
}
The Risk:
Suppose a malicious low-privileged user is on the system. They can
1. Delete the directory Gradle uses for library initialization before the privileged user runs a Gradle build.
Create a symlink or stage a malicious binary in its place.
3. When the privileged Gradle process runs, it might execute the attacker’s binary as root (or the Gradle user), resulting in local privilege escalation.
Where did it go wrong?
- Gradle 8.12, using a vulnerable version of the library, calls Native.get() without Native.init(File)—no opportunity for secure file path setup.
- The default path in /tmp/ is accessible to all users.
- No check if the native directory/binary was replaced or tampered with before execution.
> Who is *not* at risk?
> - Users on Windows or modern macOS
> - Unix systems with /tmp "sticky bit" set (drwxrwxrwt)
> - /tmp mounted as noexec
Let's see a (theoretical) attack in practice
Suppose /tmp/ is world-writable and doesn’t have the sticky bit. The Gradle native library expects to gather its binaries in /tmp/gradle-native-yourusername/. An attacker can:
# As attacker:
rm -rf /tmp/gradle-native-victimuser # Remove legitimate dir
ln -s /home/attacker/malicious /tmp/gradle-native-victimuser # Point to attacker signal
# As victim (or root running a Gradle build):
gradle build # Unknowingly runs code stashed in attacker's dir
Alternatively, the attacker could simply plant malicious binaries.
Code Flow (Before Patch)
// NativeLibraryLocator.java lines 68-78 (BEFORE fix)
if (nativeLibDir == null) {
nativeLibDir = new File(System.getProperty("java.io.tmpdir"), "gradle-native-" + System.getProperty("user.name"));
}
nativeLibDir.mkdirs();
// Binaries extracted here, loaded/executed
Problem?
The call to mkdirs() and subsequent binary writes do not check owner or permissions. Race conditions are possible.
Patch Example
// After fix: You must call init() with a safe, user-private temp dir
File workDir = new File("/home/builduser/.gradle/native");
Native.init(workDir);
Native.get(ProcessLauncher.class);
If the temp dir is not safely set, initialization fails, not falling back to /tmp unsafely.
Set the Sticky Bit:
Ensure your /tmp is mounted with the sticky bit:
sudo chmod +t /tmp
`
org.gradle.jvmargs=-Djava.io.tmpdir=/your/safe/private/tmp
`bash
export JAVA_OPTS="-Djava.io.tmpdir=/your/safe/private/tmp"
`
3. Mount /tmp as noexec:
Adding noexec to your fstab for /tmp prevents binaries being executed from there (though Gradle 8.12 won't start).
Timeline and Links
- Upstream Advisory from Gradle Security
- net.rubygrapefruit:native-platform Changelog
- Official Gradle Release Notes 8.12.1
Who’s Affected?
- Sites using Gradle 8.12 (specifically) on Linux/*nix platforms without sticky /tmp
Upgrade Gradle to 8.12.1 or 8.13+
- Audit your system temp dir permissions (ls -ld /tmp)
- Check for spurious directories/symlinks in /tmp
If you can't upgrade:
Use the workarounds above to limit access to temp directories.
Technical Takeaway
Never trust shared system temp directories for executable code or binaries—even for "temporary" files. Race conditions and privilege escalations like CVE-2025-27148 have happened before, and will happen again, when that assumption is violated.
Be cautious about where your build tools place files, especially when running with privileges!
*If you found this overview helpful, check out further reading on CWE-379: Creation of Temporary File in Directory with Insecure Permissions. Stay safe, and always keep your build pipelines up to date!*
Timeline
Published on: 02/25/2025 21:15:18 UTC