Path: blob/master/modules/auxiliary/scanner/couchdb/couchdb_login.rb
19813 views
##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::Report8include Msf::Auxiliary::AuthBrute9include Msf::Auxiliary::Scanner1011def initialize(info = {})12super(13update_info(14info,15'Name' => 'CouchDB Login Utility',16'Description' => %q{17This module tests CouchDB logins on a range of18machines and report successful logins.19},20'Author' => [21'espreto <robertoespreto[at]gmail.com>'22],23'License' => MSF_LICENSE,24'Notes' => {25'Stability' => [CRASH_SAFE],26'SideEffects' => [IOC_IN_LOGS, ACCOUNT_LOCKOUTS],27'Reliability' => []28}29)30)3132register_options(33[34Opt::RPORT(5984),35OptString.new('TARGETURI', [false, 'TARGETURI for CouchDB. Default here is /', '/']),36OptPath.new('USERPASS_FILE', [37false, 'File containing users and passwords separated by space, one pair per line',38File.join(Msf::Config.data_directory, 'wordlists', 'http_default_userpass.txt')39]),40OptPath.new('USER_FILE', [41false, 'File containing users, one per line',42File.join(Msf::Config.data_directory, 'wordlists', 'http_default_users.txt')43]),44OptPath.new('PASS_FILE', [45false, 'File containing passwords, one per line',46File.join(Msf::Config.data_directory, 'wordlists', 'http_default_pass.txt')47]),48OptBool.new('USER_AS_PASS', [ false, 'Try the username as the password for all users', false]),49]50)5152deregister_options('HttpUsername', 'HttpPassword')53end5455def run_host(_ip)56user = datastore['HttpUsername'].to_s57pass = datastore['HttpPassword'].to_s5859if user.nil? || user.strip == ''60each_user_pass do |u, p|61do_login(u, p)62end63return64end6566vprint_status("#{rhost}:#{rport} - Trying to login with '#{user}' : '#{pass}'")6768uri = target_uri.path6970res = send_request_cgi({71'uri' => normalize_uri(uri, '_users/_all_docs'),72'method' => 'GET',73'authorization' => basic_auth(user, pass)74})7576return if res.nil?77return if res.headers['Server'].nil? || res.headers['Server'] !~ /CouchDB/78return if res.code == 4047980if [200, 301, 302].include?(res.code)81vprint_good("#{rhost}:#{rport} - Successful login with '#{user}' : '#{pass}'")82end83rescue ::Rex::ConnectionError84vprint_error("'#{rhost}':'#{rport}' - Failed to connect to the web server")85end8687def report_cred(opts)88service_data = {89address: opts[:ip],90port: opts[:port],91service_name: opts[:service_name],92protocol: 'tcp',93workspace_id: myworkspace_id94}9596credential_data = {97origin_type: :service,98module_fullname: fullname,99username: opts[:user],100private_data: opts[:password],101private_type: :password102}.merge(service_data)103104login_data = {105core: create_credential(credential_data),106status: Metasploit::Model::Login::Status::UNTRIED,107proof: opts[:proof]108}.merge(service_data)109110create_credential_login(login_data)111end112113def do_login(user, pass)114vprint_status("Trying username:'#{user}' with password:'#{pass}'")115116res = send_request_cgi({117'uri' => normalize_uri(target_uri.path, '_users/_all_docs'),118'method' => 'GET',119'ctype' => 'text/plain',120'authorization' => basic_auth(user, pass)121})122123unless res124print_error('HTTP connection failed, aborting')125return :abort126end127128return :skip_pass unless res.code == 200129130print_good("#{peer} - Successful login with: '#{user}' : '#{pass}'")131132report_cred(133ip: rhost,134port: rport,135service_name: 'couchdb',136user: user,137password: pass,138proof: res.code.to_s139)140141:next_user142rescue ::Rex::ConnectionError, ::Errno::ECONNREFUSED, ::Errno::ETIMEDOUT143print_error('HTTP connection failed, aborting')144return :abort145rescue StandardError => e146print_error("Error: #{e}")147return nil148end149end150151152