Overview
CVE-2018-1196 describes a security misconfiguration in Spring Boot related to the embedded launch script that can install a Spring Boot app as a Linux service. The script included with Spring Boot 1.5.9 and earlier and 2.0.0.M1 through 2.0.0.M7 is vulnerable to a symlink attack, allowing the run_user (the system user that runs the service) to overwrite and potentially take ownership of arbitrary files on the same host. This misconfiguration arises when an application is installed as a service and relies on the embedded launch script to manage startup and runtime. The risk is elevated because an attacker with shell access to the run_user could abuse symbolic links to redirect writes to sensitive locations, enabling privilege escalation or compromise of system integrity. This vulnerability is categorized under CWE-59 (Improper Link Resolution or Symlink Handling). The vulnerability does not affect deployments that do not install the app as a service or that do not rely on the embedded launch script. In practice, many Spring Boot deployments use OS-level service managers (systemd, init.d) to start the app, and those environments must ensure they are not exposed to this misconfiguration.
Affected Versions
1.5.9 and earlier; 2.0.0.M1 through 2.0.0.M7
Code Fix Example
Spring Boot API Security Remediation
/* Java demonstration: vulnerable vs. safe launcher handling for a Spring Boot app (sandboxed, non-production demonstration) */
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Arrays;
import java.util.List;
public class ServiceLaunchDemo {
public static void main(String[] args) throws Exception {
if (args.length > 0 && "safe".equals(args[0])) {
demonstrateSafe();
} else {
demonstrateVulnerable();
}
}
// Vulnerable pattern: launcher is a symlink to a target file. If an attacker can modify the target or the symlink path,
// arbitrary files could be overwritten by the run_user.
static void demonstrateVulnerable() throws IOException {
Path tmp = Files.createTempDirectory("springboot-vuln");
Path scriptDir = tmp.resolve("bin");
Path launcher = scriptDir.resolve("run-service.sh");
Path target = tmp.resolve("real-runner/launcher.sh");
Files.createDirectories(target.getParent());
Files.write(target, Arrays.asList("#!/bin/sh", "echo Vulnerable launcher"), StandardCharsets.UTF_8,
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
// Simulate the vulnerable setup: launcher is a symlink to the target (the real launcher that could be abused)
try {
Files.createDirectories(launcher.getParent());
java.nio.file.Files.createSymbolicLink(launcher, target);
} catch (UnsupportedOperationException | IOException e) {
System.out.println("Symlink creation not supported in this environment. This demo shows the pattern but cannot execute a real exploit here.");
return;
}
// Execute the launcher (would operate on the linked target in a real attack)
ProcessBuilder pb = new ProcessBuilder(launcher.toString());
pb.inheritIO();
pb.start();
}
// Safe pattern: avoid symlinks; use fixed, non-writable launcher paths and explicit, secure invocation.
static void demonstrateSafe() throws IOException {
Path tmp = Files.createTempDirectory("springboot-safe");
Path scriptDir = tmp.resolve("bin");
Path safeLauncher = scriptDir.resolve("run-service.sh");
Files.createDirectories(scriptDir);
List<String> lines = Arrays.asList("#!/bin/sh", "echo Safe launcher executing");
Files.write(safeLauncher, lines, StandardCharsets.UTF_8, StandardOpenOption.CREATE,
StandardOpenOption.TRUNCATE_EXISTING);
safeLauncher.toFile().setExecutable(true);
ProcessBuilder pb = new ProcessBuilder(safeLauncher.toString());
pb.inheritIO();
pb.start();
}
}