Skip to content

Commit e99401e

Browse files
committed
Landing rapid7#1817 - couchdb login module
2 parents eb2e735 + a88321c commit e99401e

File tree

1 file changed

+118
-0
lines changed

1 file changed

+118
-0
lines changed
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
##
2+
# This file is part of the Metasploit Framework and may be subject to
3+
# redistribution and commercial restrictions. Please see the Metasploit
4+
# Framework web site for more information on licensing and terms of use.
5+
# http://metasploit.com/framework/
6+
##
7+
8+
require 'msf/core'
9+
10+
class Metasploit3 < Msf::Auxiliary
11+
12+
include Msf::Exploit::Remote::HttpClient
13+
include Msf::Auxiliary::Report
14+
include Msf::Auxiliary::AuthBrute
15+
include Msf::Auxiliary::Scanner
16+
17+
def initialize(info={})
18+
super(update_info(info,
19+
'Name' => 'CouchDB Login Utility',
20+
'Description' => %{
21+
This module will test CouchDB logins on a range of
22+
machines and report successful logins.
23+
},
24+
'Author' =>
25+
[
26+
'espreto <robertoespreto[at]gmail.com>'
27+
],
28+
'License' => MSF_LICENSE
29+
))
30+
31+
register_options(
32+
[
33+
Opt::RPORT(5984),
34+
OptString.new('TARGETURI', [false, "TARGETURI for CouchDB. Default here is /", "/"]),
35+
OptPath.new('USERPASS_FILE', [ false, "File containing users and passwords separated by space, one pair per line",
36+
File.join(Msf::Config.install_root, "data", "wordlists", "http_default_userpass.txt") ]),
37+
OptPath.new('USER_FILE', [ false, "File containing users, one per line",
38+
File.join(Msf::Config.install_root, "data", "wordlists", "http_default_users.txt") ]),
39+
OptPath.new('PASS_FILE', [ false, "File containing passwords, one per line",
40+
File.join(Msf::Config.install_root, "data", "wordlists", "http_default_pass.txt") ]),
41+
OptBool.new('USER_AS_PASS', [ false, "Try the username as the password for all users", false]),
42+
], self.class)
43+
end
44+
45+
def run_host(ip)
46+
47+
user = datastore['USERNAME'].to_s
48+
pass = datastore['PASSWORD'].to_s
49+
50+
if user.nil? || user.strip == ''
51+
each_user_pass do |user, pass|
52+
do_login(user, pass)
53+
end
54+
return
55+
end
56+
57+
vprint_status("#{rhost}:#{rport} - Trying to login with '#{user}' : '#{pass}'")
58+
59+
uri = target_uri.path
60+
61+
res = send_request_cgi({
62+
'uri' => normalize_uri(uri, '_users/_all_docs'),
63+
'method' => 'GET',
64+
'authorization' => basic_auth(user, pass)
65+
})
66+
67+
return if res.nil?
68+
return if (res.headers['Server'].nil? or res.headers['Server'] !~ /CouchDB/)
69+
return if (res.code == 404)
70+
71+
if [200, 301, 302].include?(res.code)
72+
vprint_good("#{rhost}:#{rport} - Successful login with '#{user}' : '#{pass}'")
73+
end
74+
75+
rescue ::Rex::ConnectionError
76+
vprint_error("'#{rhost}':'#{rport}' - Failed to connect to the web server")
77+
end
78+
79+
def do_login(user, pass)
80+
vprint_status("Trying username:'#{user}' with password:'#{pass}'")
81+
begin
82+
83+
uri = target_uri.path
84+
res = send_request_cgi(
85+
{
86+
'uri' => normalize_uri(uri, '_users/_all_docs'),
87+
'method' => 'GET',
88+
'ctype' => 'text/plain',
89+
'authorization' => basic_auth(user, pass)
90+
})
91+
92+
if res and res.code != 200
93+
return :skip_pass
94+
else
95+
vprint_good("#{rhost}:#{rport} - Successful login with. '#{user}' : '#{pass}'")
96+
97+
report_hash = {
98+
:host => datastore['RHOST'],
99+
:port => datastore['RPORT'],
100+
:sname => 'couchdb',
101+
:user => user,
102+
:pass => pass,
103+
:active => true,
104+
:type => 'password'}
105+
106+
report_auth_info(report_hash)
107+
return :next_user
108+
end
109+
110+
rescue ::Rex::ConnectionError, ::Errno::ECONNREFUSED, ::Errno::ETIMEDOUT
111+
print_error("HTTP Connection Failed, Aborting")
112+
return :abort
113+
end
114+
rescue ::Exception => e
115+
print_error("Error: #{e.to_s}")
116+
return nil
117+
end
118+
end

0 commit comments

Comments
 (0)